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( workingDirectory.getAbsolutePath() );
205
206 return cli;
207 }
208
209 private String replaceThreadNumberPlaceholder( String argLine, int threadNumber )
210 {
211 return argLine.replace( AbstractSurefireMojo.THREAD_NUMBER_PLACEHOLDER,
212 String.valueOf( threadNumber ) ).replace( AbstractSurefireMojo.FORK_NUMBER_PLACEHOLDER,
213 String.valueOf( threadNumber ) );
214 }
215
216
217
218
219
220
221
222
223 private String replacePropertyExpressions( String argLine )
224 {
225 if ( argLine == null )
226 {
227 return null;
228 }
229
230 for ( Enumeration<?> e = modelProperties.propertyNames(); e.hasMoreElements(); )
231 {
232 String key = e.nextElement().toString();
233 String field = "@{" + key + "}";
234 if ( argLine.contains( field ) )
235 {
236 argLine = argLine.replace( field, modelProperties.getProperty( key, "" ) );
237 }
238 }
239
240 return argLine;
241 }
242
243
244
245
246
247
248
249
250
251
252 private File createJar( List<String> classPath, String startClassName )
253 throws IOException
254 {
255 File file = File.createTempFile( "surefirebooter", ".jar", tempDirectory );
256 if ( !debug )
257 {
258 file.deleteOnExit();
259 }
260 FileOutputStream fos = new FileOutputStream( file );
261 JarOutputStream jos = new JarOutputStream( fos );
262 jos.setLevel( JarOutputStream.STORED );
263 JarEntry je = new JarEntry( "META-INF/MANIFEST.MF" );
264 jos.putNextEntry( je );
265
266 Manifest man = new Manifest();
267
268
269
270 String cp = "";
271 for ( String el : classPath )
272 {
273
274 cp += UrlUtils.getURL( new File( el ) ).toExternalForm() + " ";
275 }
276
277 man.getMainAttributes().putValue( "Manifest-Version", "1.0" );
278 man.getMainAttributes().putValue( "Class-Path", cp.trim() );
279 man.getMainAttributes().putValue( "Main-Class", startClassName );
280
281 man.write( jos );
282 jos.close();
283
284 return file;
285 }
286
287 public boolean isDebug()
288 {
289 return debug;
290 }
291
292 public String stripNewLines( String argline )
293 {
294 return argline.replace( "\n", " " ).replace( "\r", " " );
295 }
296
297 public String getDebugLine()
298 {
299 return debugLine;
300 }
301
302 public File getTempDirectory()
303 {
304 return tempDirectory;
305 }
306
307 public int getForkCount()
308 {
309 return forkCount;
310 }
311
312
313 public boolean isReuseForks()
314 {
315 return reuseForks;
316 }
317 }