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