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 cli.addEnvironment(entry.getKey(), value == null ? "" : value);
161 }
162
163 cli.setExecutable(getJdkForTests().getJvmExecutable().getAbsolutePath());
164
165 String jvmArgLine = newJvmArgLine(forkNumber);
166 if (!jvmArgLine.isEmpty()) {
167 cli.createArg().setLine(jvmArgLine);
168 }
169
170 if (getDebugLine() != null && !getDebugLine().isEmpty()) {
171 cli.createArg().setLine(getDebugLine());
172 }
173
174 resolveClasspath(cli, findStartClass(config), config, dumpLogDirectory);
175
176 return cli;
177 } catch (CommandLineException e) {
178 throw new SurefireBooterForkException(e.getLocalizedMessage(), e);
179 }
180 }
181
182 protected ConsoleLogger getLogger() {
183 return log;
184 }
185
186 @Nonnull
187 protected List<String> toCompleteClasspath(@Nonnull StartupConfiguration conf) throws SurefireBooterForkException {
188 AbstractPathConfiguration pathConfig = conf.getClasspathConfiguration();
189 if (pathConfig.isClassPathConfig() == pathConfig.isModularPathConfig()) {
190 throw new SurefireBooterForkException("Could not find class-path config nor modular class-path either.");
191 }
192
193 Classpath bootClasspath = getBooterClasspath();
194 Classpath testClasspath = pathConfig.getTestClasspath();
195 Classpath providerClasspath = pathConfig.getProviderClasspath();
196 Classpath completeClasspath = join(join(bootClasspath, testClasspath), providerClasspath);
197
198 getLogger().debug(completeClasspath.getLogMessage("boot classpath:"));
199 getLogger().debug(completeClasspath.getCompactLogMessage("boot(compact) classpath:"));
200
201 return completeClasspath.getClassPath();
202 }
203
204 @Nonnull
205 private File getWorkingDirectory(int forkNumber) throws SurefireBooterForkException {
206 File cwd = replaceForkThreadsInPath(getWorkingDirectory(), forkNumber);
207
208 if (!cwd.exists() && !cwd.mkdirs()) {
209 throw new SurefireBooterForkException("Cannot create workingDirectory " + cwd.getAbsolutePath());
210 }
211
212 if (!cwd.isDirectory()) {
213 throw new SurefireBooterForkException(
214 "WorkingDirectory " + cwd.getAbsolutePath() + " exists and is not a directory");
215 }
216 return cwd;
217 }
218
219
220
221
222
223
224
225
226 @Nonnull
227 private String interpolateArgLineWithPropertyExpressions() {
228 if (getArgLine() == null) {
229 return "";
230 }
231
232 String resolvedArgLine = getArgLine().trim();
233
234 if (resolvedArgLine.isEmpty()) {
235 return "";
236 }
237
238 for (final String key : getModelProperties().stringPropertyNames()) {
239 String field = "@{" + key + "}";
240 if (getArgLine().contains(field)) {
241 resolvedArgLine =
242 resolvedArgLine.replace(field, getModelProperties().getProperty(key, ""));
243 }
244 }
245
246 return resolvedArgLine;
247 }
248
249 @Nonnull
250 private static String stripWhitespace(@Nonnull String argLine) {
251 return argLine.replaceAll("\\s", " ");
252 }
253
254
255
256
257
258
259
260
261
262 @Nonnull
263 private static <K, V> Map<K, V> toImmutable(@Nullable Map<K, V> map) {
264 return map == null ? Collections.<K, V>emptyMap() : new ImmutableMap<>(map);
265 }
266
267 @Override
268 @Nonnull
269 public File getTempDirectory() {
270 return tempDirectory;
271 }
272
273 @Override
274 @Nullable
275 protected String getDebugLine() {
276 return debugLine;
277 }
278
279 @Override
280 @Nonnull
281 protected File getWorkingDirectory() {
282 return workingDirectory;
283 }
284
285 @Override
286 @Nonnull
287 protected Properties getModelProperties() {
288 return modelProperties;
289 }
290
291 @Override
292 @Nullable
293 protected String getArgLine() {
294 return argLine;
295 }
296
297 @Override
298 @Nonnull
299 protected Map<String, String> getEnvironmentVariables() {
300 return environmentVariables;
301 }
302
303 @Nonnull
304 @Override
305 protected String[] getExcludedEnvironmentVariables() {
306 return excludedEnvironmentVariables;
307 }
308
309 @Override
310 protected boolean isDebug() {
311 return debug;
312 }
313
314 @Override
315 protected int getForkCount() {
316 return forkCount;
317 }
318
319 @Override
320 protected boolean isReuseForks() {
321 return reuseForks;
322 }
323
324 @Override
325 @Nonnull
326 protected Platform getPluginPlatform() {
327 return pluginPlatform;
328 }
329
330 @Override
331 @Nonnull
332 protected JdkAttributes getJdkForTests() {
333 return getPluginPlatform().getJdkExecAttributesForTests();
334 }
335
336 @Override
337 @Nonnull
338 protected Classpath getBooterClasspath() {
339 return booterClasspath;
340 }
341
342 @Nonnull
343 private String newJvmArgLine(int forks) {
344 String interpolatedArgs = stripWhitespace(interpolateArgLineWithPropertyExpressions());
345 String argsWithReplacedForkNumbers = replaceThreadNumberPlaceholders(interpolatedArgs, forks);
346 return extendJvmArgLine(argsWithReplacedForkNumbers);
347 }
348
349 @Nonnull
350 private static String findStartClass(StartupConfiguration config) {
351 return config.isShadefire() ? relocate(DEFAULT_PROVIDER_CLASS) : DEFAULT_PROVIDER_CLASS;
352 }
353 }