1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugin.surefire.booterclient;
20
21 import javax.annotation.Nonnull;
22 import javax.annotation.Nullable;
23
24 import java.io.File;
25 import java.util.Collections;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Map.Entry;
29 import java.util.Properties;
30
31 import org.apache.maven.plugin.surefire.JdkAttributes;
32 import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.Commandline;
33 import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
34 import org.apache.maven.surefire.api.util.internal.ImmutableMap;
35 import org.apache.maven.surefire.booter.AbstractPathConfiguration;
36 import org.apache.maven.surefire.booter.Classpath;
37 import org.apache.maven.surefire.booter.StartupConfiguration;
38 import org.apache.maven.surefire.booter.SurefireBooterForkException;
39 import org.apache.maven.surefire.extensions.ForkNodeFactory;
40 import org.apache.maven.surefire.shared.utils.cli.CommandLineException;
41
42 import static org.apache.maven.plugin.surefire.SurefireHelper.replaceForkThreadsInPath;
43 import static org.apache.maven.plugin.surefire.SurefireHelper.replaceThreadNumberPlaceholders;
44 import static org.apache.maven.plugin.surefire.util.Relocator.relocate;
45 import static org.apache.maven.surefire.booter.Classpath.join;
46
47
48
49
50
51
52
53 public abstract class DefaultForkConfiguration extends ForkConfiguration {
54 @Nonnull
55 private final Classpath booterClasspath;
56
57 @Nonnull
58 private final File tempDirectory;
59
60 @Nullable
61 private final String debugLine;
62
63 @Nonnull
64 private final File workingDirectory;
65
66 @Nonnull
67 private final Properties modelProperties;
68
69 @Nullable
70 private final String argLine;
71
72 @Nonnull
73 private final Map<String, String> environmentVariables;
74
75 @Nonnull
76 private final String[] excludedEnvironmentVariables;
77
78 private final boolean debug;
79 private final int forkCount;
80 private final boolean reuseForks;
81
82 @Nonnull
83 private final Platform pluginPlatform;
84
85 @Nonnull
86 private final ConsoleLogger log;
87
88 @Nonnull
89 private final ForkNodeFactory forkNodeFactory;
90
91 @SuppressWarnings("checkstyle:parameternumber")
92 protected DefaultForkConfiguration(
93 @Nonnull Classpath booterClasspath,
94 @Nonnull File tempDirectory,
95 @Nullable String debugLine,
96 @Nonnull File workingDirectory,
97 @Nonnull Properties modelProperties,
98 @Nullable String argLine,
99 @Nonnull Map<String, String> environmentVariables,
100 @Nonnull String[] excludedEnvironmentVariables,
101 boolean debug,
102 int forkCount,
103 boolean reuseForks,
104 @Nonnull Platform pluginPlatform,
105 @Nonnull ConsoleLogger log,
106 @Nonnull ForkNodeFactory forkNodeFactory) {
107 this.booterClasspath = booterClasspath;
108 this.tempDirectory = tempDirectory;
109 this.debugLine = debugLine;
110 this.workingDirectory = workingDirectory;
111 this.modelProperties = modelProperties;
112 this.argLine = argLine;
113 this.environmentVariables = toImmutable(environmentVariables);
114 this.excludedEnvironmentVariables = excludedEnvironmentVariables;
115 this.debug = debug;
116 this.forkCount = forkCount;
117 this.reuseForks = reuseForks;
118 this.pluginPlatform = pluginPlatform;
119 this.log = log;
120 this.forkNodeFactory = forkNodeFactory;
121 }
122
123 protected abstract void resolveClasspath(
124 @Nonnull Commandline cli,
125 @Nonnull String booterThatHasMainMethod,
126 @Nonnull StartupConfiguration config,
127 @Nonnull File dumpLogDirectory)
128 throws SurefireBooterForkException;
129
130 @Nonnull
131 protected String extendJvmArgLine(@Nonnull String jvmArgLine) {
132 return jvmArgLine;
133 }
134
135 @Nonnull
136 @Override
137 public final ForkNodeFactory getForkNodeFactory() {
138 return forkNodeFactory;
139 }
140
141
142
143
144
145
146
147
148 @Nonnull
149 @Override
150 public Commandline createCommandLine(
151 @Nonnull StartupConfiguration config, int forkNumber, @Nonnull File dumpLogDirectory)
152 throws SurefireBooterForkException {
153 try {
154 Commandline cli = new Commandline(getExcludedEnvironmentVariables());
155
156 cli.setWorkingDirectory(getWorkingDirectory(forkNumber).getAbsolutePath());
157
158 for (Entry<String, String> entry : getEnvironmentVariables().entrySet()) {
159 String value = entry.getValue();
160 if (value != null) {
161 value = replaceThreadNumberPlaceholders(value, forkNumber);
162 }
163 cli.addEnvironment(entry.getKey(), value == null ? "" : value);
164 }
165
166 cli.setExecutable(getJdkForTests().getJvmExecutable().getAbsolutePath());
167
168 String jvmArgLine = newJvmArgLine(forkNumber);
169 if (!jvmArgLine.isEmpty()) {
170 cli.createArg().setLine(jvmArgLine);
171 }
172
173 if (getDebugLine() != null && !getDebugLine().isEmpty()) {
174 cli.createArg().setLine(getDebugLine());
175 }
176
177 resolveClasspath(cli, findStartClass(config), config, dumpLogDirectory);
178
179 return cli;
180 } catch (CommandLineException e) {
181 throw new SurefireBooterForkException(e.getLocalizedMessage(), e);
182 }
183 }
184
185 protected ConsoleLogger getLogger() {
186 return log;
187 }
188
189 @Nonnull
190 protected List<String> toCompleteClasspath(@Nonnull StartupConfiguration conf) throws SurefireBooterForkException {
191 AbstractPathConfiguration pathConfig = conf.getClasspathConfiguration();
192 if (pathConfig.isClassPathConfig() == pathConfig.isModularPathConfig()) {
193 throw new SurefireBooterForkException("Could not find class-path config nor modular class-path either.");
194 }
195
196 Classpath bootClasspath = getBooterClasspath();
197 Classpath testClasspath = pathConfig.getTestClasspath();
198 Classpath providerClasspath = pathConfig.getProviderClasspath();
199 Classpath completeClasspath = join(join(bootClasspath, testClasspath), providerClasspath);
200
201 getLogger().debug(completeClasspath.getLogMessage("boot classpath:"));
202 getLogger().debug(completeClasspath.getCompactLogMessage("boot(compact) classpath:"));
203
204 return completeClasspath.getClassPath();
205 }
206
207 @Nonnull
208 private File getWorkingDirectory(int forkNumber) throws SurefireBooterForkException {
209 File cwd = replaceForkThreadsInPath(getWorkingDirectory(), forkNumber);
210
211 if (!cwd.exists() && !cwd.mkdirs()) {
212 throw new SurefireBooterForkException("Cannot create workingDirectory " + cwd.getAbsolutePath());
213 }
214
215 if (!cwd.isDirectory()) {
216 throw new SurefireBooterForkException(
217 "WorkingDirectory " + cwd.getAbsolutePath() + " exists and is not a directory");
218 }
219 return cwd;
220 }
221
222
223
224
225
226
227
228
229 @Nonnull
230 private String interpolateArgLineWithPropertyExpressions() {
231 if (getArgLine() == null) {
232 return "";
233 }
234
235 String resolvedArgLine = getArgLine().trim();
236
237 if (resolvedArgLine.isEmpty()) {
238 return "";
239 }
240
241 for (final String key : getModelProperties().stringPropertyNames()) {
242 String field = "@{" + key + "}";
243 if (getArgLine().contains(field)) {
244 resolvedArgLine =
245 resolvedArgLine.replace(field, getModelProperties().getProperty(key, ""));
246 }
247 }
248
249 return resolvedArgLine;
250 }
251
252 @Nonnull
253 private static String stripWhitespace(@Nonnull String argLine) {
254 return argLine.replaceAll("\\s", " ");
255 }
256
257
258
259
260
261
262
263
264
265 @Nonnull
266 private static <K, V> Map<K, V> toImmutable(@Nullable Map<K, V> map) {
267 return map == null ? Collections.<K, V>emptyMap() : new ImmutableMap<>(map);
268 }
269
270 @Override
271 @Nonnull
272 public File getTempDirectory() {
273 return tempDirectory;
274 }
275
276 @Override
277 @Nullable
278 protected String getDebugLine() {
279 return debugLine;
280 }
281
282 @Override
283 @Nonnull
284 protected File getWorkingDirectory() {
285 return workingDirectory;
286 }
287
288 @Override
289 @Nonnull
290 protected Properties getModelProperties() {
291 return modelProperties;
292 }
293
294 @Override
295 @Nullable
296 protected String getArgLine() {
297 return argLine;
298 }
299
300 @Override
301 @Nonnull
302 protected Map<String, String> getEnvironmentVariables() {
303 return environmentVariables;
304 }
305
306 @Nonnull
307 @Override
308 protected String[] getExcludedEnvironmentVariables() {
309 return excludedEnvironmentVariables;
310 }
311
312 @Override
313 protected boolean isDebug() {
314 return debug;
315 }
316
317 @Override
318 protected int getForkCount() {
319 return forkCount;
320 }
321
322 @Override
323 protected boolean isReuseForks() {
324 return reuseForks;
325 }
326
327 @Override
328 @Nonnull
329 protected Platform getPluginPlatform() {
330 return pluginPlatform;
331 }
332
333 @Override
334 @Nonnull
335 protected JdkAttributes getJdkForTests() {
336 return getPluginPlatform().getJdkExecAttributesForTests();
337 }
338
339 @Override
340 @Nonnull
341 protected Classpath getBooterClasspath() {
342 return booterClasspath;
343 }
344
345 @Nonnull
346 private String newJvmArgLine(int forks) {
347 String interpolatedArgs = stripWhitespace(interpolateArgLineWithPropertyExpressions());
348 String argsWithReplacedForkNumbers = replaceThreadNumberPlaceholders(interpolatedArgs, forks);
349 return extendJvmArgLine(argsWithReplacedForkNumbers);
350 }
351
352 @Nonnull
353 private static String findStartClass(StartupConfiguration config) {
354 return config.isShadefire() ? relocate(DEFAULT_PROVIDER_CLASS) : DEFAULT_PROVIDER_CLASS;
355 }
356 }