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 java.io.File;
23 import java.io.FileOutputStream;
24 import java.io.IOException;
25 import java.util.Enumeration;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Properties;
29 import java.util.jar.JarEntry;
30 import java.util.jar.JarOutputStream;
31 import java.util.jar.Manifest;
32
33 import org.apache.maven.plugin.surefire.AbstractSurefireMojo;
34 import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.OutputStreamFlushableCommandline;
35 import org.apache.maven.plugin.surefire.util.Relocator;
36 import org.apache.maven.shared.utils.StringUtils;
37 import org.apache.maven.surefire.booter.Classpath;
38 import org.apache.maven.surefire.booter.ForkedBooter;
39 import org.apache.maven.surefire.booter.StartupConfiguration;
40 import org.apache.maven.surefire.booter.SurefireBooterForkException;
41 import org.apache.maven.surefire.util.UrlUtils;
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
165 for ( String key : environmentVariables.keySet() )
166 {
167 String value = environmentVariables.get( key );
168
169 cli.addEnvironment( key, value == null ? "" : value );
170 }
171 }
172
173 if ( getDebugLine() != null && !"".equals( getDebugLine() ) )
174 {
175 cli.createArg().setLine( getDebugLine() );
176 }
177
178 if ( useJar )
179 {
180 File jarFile;
181 try
182 {
183 jarFile = createJar( classPath, providerThatHasMainMethod );
184 }
185 catch ( IOException e )
186 {
187 throw new SurefireBooterForkException( "Error creating archive file", e );
188 }
189
190 cli.createArg().setValue( "-jar" );
191
192 cli.createArg().setValue( jarFile.getAbsolutePath() );
193 }
194 else
195 {
196 cli.addEnvironment( "CLASSPATH", StringUtils.join( classPath.iterator(), File.pathSeparator ) );
197
198 final String forkedBooter =
199 providerThatHasMainMethod != null ? providerThatHasMainMethod : ForkedBooter.class.getName();
200
201 cli.createArg().setValue( shadefire ? new Relocator().relocate( forkedBooter ) : forkedBooter );
202 }
203
204 cli.setWorkingDirectory( getWorkingDirectory( threadNumber ).getAbsolutePath() );
205
206 return cli;
207 }
208
209 private File getWorkingDirectory( int threadNumber )
210 throws SurefireBooterForkException
211 {
212 File cwd = new File( replaceThreadNumberPlaceholder( workingDirectory.getAbsolutePath(), threadNumber ) );
213 if ( !cwd.exists() && !cwd.mkdirs() )
214 {
215 throw new SurefireBooterForkException( "Cannot create workingDirectory " + cwd.getAbsolutePath() );
216 }
217 if ( !cwd.isDirectory() )
218 {
219 throw new SurefireBooterForkException(
220 "WorkingDirectory " + cwd.getAbsolutePath() + " exists and is not a directory" );
221 }
222 return cwd;
223 }
224
225 private String replaceThreadNumberPlaceholder( String argLine, int threadNumber )
226 {
227 return argLine.replace( AbstractSurefireMojo.THREAD_NUMBER_PLACEHOLDER,
228 String.valueOf( threadNumber ) ).replace( AbstractSurefireMojo.FORK_NUMBER_PLACEHOLDER,
229 String.valueOf( threadNumber ) );
230 }
231
232
233
234
235
236
237
238
239 private String replacePropertyExpressions( String argLine )
240 {
241 if ( argLine == null )
242 {
243 return null;
244 }
245
246 for ( Enumeration<?> e = modelProperties.propertyNames(); e.hasMoreElements(); )
247 {
248 String key = e.nextElement().toString();
249 String field = "@{" + key + "}";
250 if ( argLine.contains( field ) )
251 {
252 argLine = argLine.replace( field, modelProperties.getProperty( key, "" ) );
253 }
254 }
255
256 return argLine;
257 }
258
259
260
261
262
263
264
265
266
267
268 private File createJar( List<String> classPath, String startClassName )
269 throws IOException
270 {
271 File file = File.createTempFile( "surefirebooter", ".jar", tempDirectory );
272 if ( !debug )
273 {
274 file.deleteOnExit();
275 }
276 FileOutputStream fos = new FileOutputStream( file );
277 JarOutputStream jos = new JarOutputStream( fos );
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 String cp = "";
287 for ( String el : classPath )
288 {
289
290 cp += UrlUtils.getURL( new File( el ) ).toExternalForm() + " ";
291 }
292
293 man.getMainAttributes().putValue( "Manifest-Version", "1.0" );
294 man.getMainAttributes().putValue( "Class-Path", cp.trim() );
295 man.getMainAttributes().putValue( "Main-Class", startClassName );
296
297 man.write( jos );
298 jos.close();
299
300 return file;
301 }
302
303 public boolean isDebug()
304 {
305 return debug;
306 }
307
308 public String stripNewLines( String argline )
309 {
310 return argline.replace( "\n", " " ).replace( "\r", " " );
311 }
312
313 public String getDebugLine()
314 {
315 return debugLine;
316 }
317
318 public File getTempDirectory()
319 {
320 return tempDirectory;
321 }
322
323 public int getForkCount()
324 {
325 return forkCount;
326 }
327
328
329 public boolean isReuseForks()
330 {
331 return reuseForks;
332 }
333 }