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.AbstractSurefireMojo;
23 import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.OutputStreamFlushableCommandline;
24 import org.apache.maven.plugin.surefire.util.Relocator;
25 import org.apache.maven.shared.utils.StringUtils;
26 import org.apache.maven.surefire.booter.Classpath;
27 import org.apache.maven.surefire.booter.ForkedBooter;
28 import org.apache.maven.surefire.booter.StartupConfiguration;
29 import org.apache.maven.surefire.booter.SurefireBooterForkException;
30 import org.apache.maven.surefire.util.UrlUtils;
31
32 import java.io.File;
33 import java.io.FileOutputStream;
34 import java.io.IOException;
35 import java.util.Enumeration;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.Properties;
39 import java.util.jar.JarEntry;
40 import java.util.jar.JarOutputStream;
41 import java.util.jar.Manifest;
42
43
44
45
46
47
48
49
50 public class ForkConfiguration
51 {
52 public static final String FORK_ONCE = "once";
53
54 public static final String FORK_ALWAYS = "always";
55
56 public static final String FORK_NEVER = "never";
57
58 public static final String FORK_PERTHREAD = "perthread";
59
60 private final int forkCount;
61
62 private final boolean reuseForks;
63
64 private final Classpath bootClasspathConfiguration;
65
66 private final String jvmExecutable;
67
68 private Properties modelProperties;
69
70 private final String argLine;
71
72 private final Map<String, String> environmentVariables;
73
74 private final File workingDirectory;
75
76 private final File tempDirectory;
77
78 private final boolean debug;
79
80 private final String debugLine;
81
82 @SuppressWarnings( "checkstyle:parameternumber" )
83 public ForkConfiguration( Classpath bootClasspathConfiguration, File tmpDir, String debugLine,
84 String jvmExecutable, File workingDirectory, Properties modelProperties, String argLine,
85 Map<String, String> environmentVariables, boolean debugEnabled, int forkCount,
86 boolean reuseForks )
87 {
88 this.bootClasspathConfiguration = bootClasspathConfiguration;
89 this.tempDirectory = tmpDir;
90 this.debugLine = debugLine;
91 this.jvmExecutable = jvmExecutable;
92 this.workingDirectory = workingDirectory;
93 this.modelProperties = modelProperties;
94 this.argLine = argLine;
95 this.environmentVariables = environmentVariables;
96 this.debug = debugEnabled;
97 this.forkCount = forkCount;
98 this.reuseForks = reuseForks;
99 }
100
101 public Classpath getBootClasspath()
102 {
103 return bootClasspathConfiguration;
104 }
105
106 public static String getEffectiveForkMode( String forkMode )
107 {
108 if ( "pertest".equalsIgnoreCase( forkMode ) )
109 {
110 return FORK_ALWAYS;
111 }
112 else if ( "none".equalsIgnoreCase( forkMode ) )
113 {
114 return FORK_NEVER;
115 }
116 else if ( forkMode.equals( FORK_NEVER ) || forkMode.equals( FORK_ONCE )
117 || forkMode.equals( FORK_ALWAYS ) || forkMode.equals( FORK_PERTHREAD ) )
118 {
119 return forkMode;
120 }
121 else
122 {
123 throw new IllegalArgumentException( "Fork mode " + forkMode + " is not a legal value" );
124 }
125 }
126
127
128
129
130
131
132
133
134 public OutputStreamFlushableCommandline createCommandLine( List<String> classPath,
135 StartupConfiguration startupConfiguration,
136 int threadNumber )
137 throws SurefireBooterForkException
138 {
139 return createCommandLine( classPath,
140 startupConfiguration.getClassLoaderConfiguration()
141 .isManifestOnlyJarRequestedAndUsable(),
142 startupConfiguration.isShadefire(), startupConfiguration.isProviderMainClass()
143 ? startupConfiguration.getActualClassName()
144 : ForkedBooter.class.getName(), threadNumber );
145 }
146
147 OutputStreamFlushableCommandline createCommandLine( List<String> classPath, boolean useJar, boolean shadefire,
148 String providerThatHasMainMethod, int threadNumber )
149 throws SurefireBooterForkException
150 {
151 OutputStreamFlushableCommandline cli = new OutputStreamFlushableCommandline();
152
153 cli.setExecutable( jvmExecutable );
154
155 if ( argLine != null )
156 {
157 cli.createArg().setLine(
158 replaceThreadNumberPlaceholder( stripNewLines( replacePropertyExpressions( argLine ) ),
159 threadNumber ) );
160 }
161
162 if ( environmentVariables != null )
163 {
164 for ( Map.Entry<String, String> entry : environmentVariables.entrySet() )
165 {
166 String value = entry.getValue();
167 cli.addEnvironment( entry.getKey(), value == null ? "" : value );
168 }
169 }
170
171 if ( getDebugLine() != null && !"".equals( getDebugLine() ) )
172 {
173 cli.createArg().setLine( getDebugLine() );
174 }
175
176 if ( useJar )
177 {
178 File jarFile;
179 try
180 {
181 jarFile = createJar( classPath, providerThatHasMainMethod );
182 }
183 catch ( IOException e )
184 {
185 throw new SurefireBooterForkException( "Error creating archive file", e );
186 }
187
188 cli.createArg().setValue( "-jar" );
189
190 cli.createArg().setValue( jarFile.getAbsolutePath() );
191 }
192 else
193 {
194 cli.addEnvironment( "CLASSPATH", StringUtils.join( classPath.iterator(), File.pathSeparator ) );
195
196 final String forkedBooter =
197 providerThatHasMainMethod != null ? providerThatHasMainMethod : ForkedBooter.class.getName();
198
199 cli.createArg().setValue( shadefire ? new Relocator().relocate( forkedBooter ) : forkedBooter );
200 }
201
202 cli.setWorkingDirectory( getWorkingDirectory( threadNumber ).getAbsolutePath() );
203
204 return cli;
205 }
206
207 private File getWorkingDirectory( int threadNumber )
208 throws SurefireBooterForkException
209 {
210 File cwd = new File( replaceThreadNumberPlaceholder( workingDirectory.getAbsolutePath(), threadNumber ) );
211 if ( !cwd.exists() && !cwd.mkdirs() )
212 {
213 throw new SurefireBooterForkException( "Cannot create workingDirectory " + cwd.getAbsolutePath() );
214 }
215 if ( !cwd.isDirectory() )
216 {
217 throw new SurefireBooterForkException(
218 "WorkingDirectory " + cwd.getAbsolutePath() + " exists and is not a directory" );
219 }
220 return cwd;
221 }
222
223 private String replaceThreadNumberPlaceholder( String argLine, int threadNumber )
224 {
225 return argLine.replace( AbstractSurefireMojo.THREAD_NUMBER_PLACEHOLDER,
226 String.valueOf( threadNumber ) ).replace( AbstractSurefireMojo.FORK_NUMBER_PLACEHOLDER,
227 String.valueOf( threadNumber ) );
228 }
229
230
231
232
233
234
235
236
237 private String replacePropertyExpressions( String argLine )
238 {
239 if ( argLine == null )
240 {
241 return null;
242 }
243
244 for ( Enumeration<?> e = modelProperties.propertyNames(); e.hasMoreElements(); )
245 {
246 String key = e.nextElement().toString();
247 String field = "@{" + key + "}";
248 if ( argLine.contains( field ) )
249 {
250 argLine = argLine.replace( field, modelProperties.getProperty( key, "" ) );
251 }
252 }
253
254 return argLine;
255 }
256
257
258
259
260
261
262
263
264
265
266 private File createJar( List<String> classPath, String startClassName )
267 throws IOException
268 {
269 File file = File.createTempFile( "surefirebooter", ".jar", tempDirectory );
270 if ( !debug )
271 {
272 file.deleteOnExit();
273 }
274 FileOutputStream fos = new FileOutputStream( file );
275 JarOutputStream jos = new JarOutputStream( fos );
276 try
277 {
278 jos.setLevel( JarOutputStream.STORED );
279 JarEntry je = new JarEntry( "META-INF/MANIFEST.MF" );
280 jos.putNextEntry( je );
281
282 Manifest man = new Manifest();
283
284
285
286 StringBuilder cp = new StringBuilder();
287 for ( String el : classPath )
288 {
289
290 cp.append( UrlUtils.getURL( new File( el ) ).toExternalForm() )
291 .append( " " );
292 }
293
294 man.getMainAttributes().putValue( "Manifest-Version", "1.0" );
295 man.getMainAttributes().putValue( "Class-Path", cp.toString().trim() );
296 man.getMainAttributes().putValue( "Main-Class", startClassName );
297
298 man.write( jos );
299 }
300 finally
301 {
302 jos.close();
303 }
304
305 return file;
306 }
307
308 public boolean isDebug()
309 {
310 return debug;
311 }
312
313 public String stripNewLines( String argline )
314 {
315 return argline.replace( "\n", " " ).replace( "\r", " " );
316 }
317
318 public String getDebugLine()
319 {
320 return debugLine;
321 }
322
323 public File getTempDirectory()
324 {
325 return tempDirectory;
326 }
327
328 public int getForkCount()
329 {
330 return forkCount;
331 }
332
333
334 public boolean isReuseForks()
335 {
336 return reuseForks;
337 }
338 }