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