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