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