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 public ForkConfiguration( Classpath bootClasspathConfiguration, File tmpDir, String debugLine, String jvmExecutable,
83 File workingDirectory, Properties modelProperties, String argLine, Map<String, String> environmentVariables,
84 boolean debugEnabled, int forkCount, boolean reuseForks )
85 {
86 this.bootClasspathConfiguration = bootClasspathConfiguration;
87 this.tempDirectory = tmpDir;
88 this.debugLine = debugLine;
89 this.jvmExecutable = jvmExecutable;
90 this.workingDirectory = workingDirectory;
91 this.modelProperties = modelProperties;
92 this.argLine = argLine;
93 this.environmentVariables = environmentVariables;
94 this.debug = debugEnabled;
95 this.forkCount = forkCount;
96 this.reuseForks = reuseForks;
97 }
98
99 public Classpath getBootClasspath()
100 {
101 return bootClasspathConfiguration;
102 }
103
104 public static String getEffectiveForkMode( String forkMode )
105 {
106 if ( "pertest".equalsIgnoreCase( forkMode ) )
107 {
108 return FORK_ALWAYS;
109 }
110 else if ( "none".equalsIgnoreCase( forkMode ) )
111 {
112 return FORK_NEVER;
113 }
114 else if ( forkMode.equals( FORK_NEVER ) || forkMode.equals( FORK_ONCE ) ||
115 forkMode.equals( FORK_ALWAYS ) || forkMode.equals( FORK_PERTHREAD ) )
116 {
117 return forkMode;
118 }
119 else
120 {
121 throw new IllegalArgumentException( "Fork mode " + forkMode + " is not a legal value" );
122 }
123 }
124
125
126
127
128
129
130
131
132 public OutputStreamFlushableCommandline createCommandLine( List<String> classPath,
133 StartupConfiguration startupConfiguration,
134 int threadNumber )
135 throws SurefireBooterForkException
136 {
137 return createCommandLine( classPath,
138 startupConfiguration.getClassLoaderConfiguration().isManifestOnlyJarRequestedAndUsable(),
139 startupConfiguration.isShadefire(), startupConfiguration.isProviderMainClass()
140 ? startupConfiguration.getActualClassName()
141 : ForkedBooter.class.getName(), threadNumber );
142 }
143
144 OutputStreamFlushableCommandline createCommandLine( List<String> classPath, boolean useJar, boolean shadefire,
145 String providerThatHasMainMethod, int threadNumber )
146 throws SurefireBooterForkException
147 {
148 OutputStreamFlushableCommandline cli = new OutputStreamFlushableCommandline();
149
150 cli.setExecutable( jvmExecutable );
151
152 if ( argLine != null )
153 {
154 cli.createArg().setLine( replaceThreadNumberPlaceholder( stripNewLines( replacePropertyExpressions( argLine ) ), threadNumber ) );
155 }
156
157 if ( environmentVariables != null )
158 {
159
160 for ( String key : environmentVariables.keySet() )
161 {
162 String value = environmentVariables.get( key );
163
164 cli.addEnvironment( key, value );
165 }
166 }
167
168 if ( getDebugLine() != null && !"".equals( getDebugLine() ) )
169 {
170 cli.createArg().setLine( getDebugLine() );
171 }
172
173 if ( useJar )
174 {
175 File jarFile;
176 try
177 {
178 jarFile = createJar( classPath, providerThatHasMainMethod );
179 }
180 catch ( IOException e )
181 {
182 throw new SurefireBooterForkException( "Error creating archive file", e );
183 }
184
185 cli.createArg().setValue( "-jar" );
186
187 cli.createArg().setValue( jarFile.getAbsolutePath() );
188 }
189 else
190 {
191 cli.addEnvironment( "CLASSPATH", StringUtils.join( classPath.iterator(), File.pathSeparator ) );
192
193 final String forkedBooter =
194 providerThatHasMainMethod != null ? providerThatHasMainMethod : ForkedBooter.class.getName();
195
196 cli.createArg().setValue( shadefire ? new Relocator().relocate( forkedBooter ) : forkedBooter );
197 }
198
199 cli.setWorkingDirectory( workingDirectory.getAbsolutePath() );
200
201 return cli;
202 }
203
204 private String replaceThreadNumberPlaceholder( String argLine, int threadNumber )
205 {
206 return argLine.replace( AbstractSurefireMojo.THREAD_NUMBER_PLACEHOLDER,
207 String.valueOf( threadNumber ) ).replace( AbstractSurefireMojo.FORK_NUMBER_PLACEHOLDER,
208 String.valueOf( threadNumber ) );
209 }
210
211
212
213
214
215
216
217
218 private String replacePropertyExpressions( String argLine )
219 {
220 if ( argLine == null ) {
221 return null;
222 }
223
224 for ( Enumeration<?> e = modelProperties.propertyNames(); e.hasMoreElements(); ) {
225 String key = e.nextElement().toString();
226 String field = "@{" + key + "}";
227 if ( argLine.contains(field) ) {
228 argLine = argLine.replace(field, modelProperties.getProperty(key, ""));
229 }
230 }
231
232 return argLine;
233 }
234
235
236
237
238
239
240
241
242
243
244 private File createJar( List<String> classPath, String startClassName )
245 throws IOException
246 {
247 File file = File.createTempFile( "surefirebooter", ".jar", tempDirectory );
248 if ( !debug )
249 {
250 file.deleteOnExit();
251 }
252 FileOutputStream fos = new FileOutputStream( file );
253 JarOutputStream jos = new JarOutputStream( fos );
254 jos.setLevel( JarOutputStream.STORED );
255 JarEntry je = new JarEntry( "META-INF/MANIFEST.MF" );
256 jos.putNextEntry( je );
257
258 Manifest man = new Manifest();
259
260
261
262 String cp = "";
263 for ( String el : classPath )
264 {
265
266 cp += UrlUtils.getURL( new File( el ) ).toExternalForm() + " ";
267 }
268
269 man.getMainAttributes().putValue( "Manifest-Version", "1.0" );
270 man.getMainAttributes().putValue( "Class-Path", cp.trim() );
271 man.getMainAttributes().putValue( "Main-Class", startClassName );
272
273 man.write( jos );
274 jos.close();
275
276 return file;
277 }
278
279 public boolean isDebug()
280 {
281 return debug;
282 }
283
284 public String stripNewLines( String argline )
285 {
286 return argline.replace( "\n", " " ).replace( "\r", " " );
287 }
288
289 public String getDebugLine()
290 {
291 return debugLine;
292 }
293
294 public File getTempDirectory()
295 {
296 return tempDirectory;
297 }
298
299 public int getForkCount()
300 {
301 return forkCount;
302 }
303
304
305 public boolean isReuseForks()
306 {
307 return reuseForks;
308 }
309 }