View Javadoc
1   package org.apache.maven.surefire.booter;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.io.FileInputStream;
24  import java.io.InputStream;
25  import java.io.PrintStream;
26  import java.lang.reflect.InvocationTargetException;
27  
28  import org.apache.maven.surefire.providerapi.ProviderParameters;
29  import org.apache.maven.surefire.providerapi.SurefireProvider;
30  import org.apache.maven.surefire.report.LegacyPojoStackTraceWriter;
31  import org.apache.maven.surefire.report.ReporterFactory;
32  import org.apache.maven.surefire.report.StackTraceWriter;
33  import org.apache.maven.surefire.suite.RunResult;
34  import org.apache.maven.surefire.testset.TestSetFailedException;
35  import org.apache.maven.surefire.util.ReflectionUtils;
36  
37  /**
38   * The part of the booter that is unique to a forked vm.
39   * <p/>
40   * Deals with deserialization of the booter wire-level protocol
41   * <p/>
42   *
43   * @author Jason van Zyl
44   * @author Emmanuel Venisse
45   * @author Kristian Rosenvold
46   */
47  public class ForkedBooter
48  {
49  
50      /**
51       * This method is invoked when Surefire is forked - this method parses and organizes the arguments passed to it and
52       * then calls the Surefire class' run method. <p/> The system exit code will be 1 if an exception is thrown.
53       *
54       * @param args Commandline arguments
55       * @throws Throwable Upon throwables
56       */
57      public static void main( String[] args )
58          throws Throwable
59      {
60          final PrintStream originalOut = System.out;
61          try
62          {
63              if ( args.length > 1 )
64              {
65                  SystemPropertyManager.setSystemProperties( new File( args[1] ) );
66              }
67  
68              File surefirePropertiesFile = new File( args[0] );
69              InputStream stream = surefirePropertiesFile.exists() ? new FileInputStream( surefirePropertiesFile ) : null;
70              BooterDeserializer booterDeserializer = new BooterDeserializer( stream );
71              ProviderConfiguration providerConfiguration = booterDeserializer.deserialize();
72              final StartupConfiguration startupConfiguration = booterDeserializer.getProviderConfiguration();
73  
74              TypeEncodedValue forkedTestSet = providerConfiguration.getTestForFork();
75              boolean readTestsFromInputStream = providerConfiguration.isReadTestsFromInStream();
76  
77              final ClasspathConfiguration classpathConfiguration = startupConfiguration.getClasspathConfiguration();
78              if ( startupConfiguration.isManifestOnlyJarRequestedAndUsable() )
79              {
80                  classpathConfiguration.trickClassPathWhenManifestOnlyClasspath();
81              }
82  
83              Thread.currentThread().getContextClassLoader().setDefaultAssertionStatus(
84                  classpathConfiguration.isEnableAssertions() );
85              startupConfiguration.writeSurefireTestClasspathProperty();
86  
87              Object testSet;
88              if ( forkedTestSet != null )
89              {
90                  testSet = forkedTestSet.getDecodedValue( Thread.currentThread().getContextClassLoader() );
91              }
92              else if ( readTestsFromInputStream )
93              {
94                  testSet = new LazyTestsToRun( System.in, originalOut );
95              }
96              else
97              {
98                  testSet = null;
99              }
100 
101             try
102             {
103                 runSuitesInProcess( testSet, startupConfiguration, providerConfiguration, originalOut );
104             }
105             catch ( InvocationTargetException t )
106             {
107 
108                 LegacyPojoStackTraceWriter stackTraceWriter =
109                     new LegacyPojoStackTraceWriter( "test subystem", "no method", t.getTargetException() );
110                 StringBuilder stringBuilder = new StringBuilder();
111                 ForkingRunListener.encode( stringBuilder, stackTraceWriter, false );
112                 originalOut.println( ( (char) ForkingRunListener.BOOTERCODE_ERROR )
113                                      + ",0," + stringBuilder.toString() );
114             }
115             catch ( Throwable t )
116             {
117                 StackTraceWriter stackTraceWriter = new LegacyPojoStackTraceWriter( "test subystem", "no method", t );
118                 StringBuilder stringBuilder = new StringBuilder();
119                 ForkingRunListener.encode( stringBuilder, stackTraceWriter, false );
120                 originalOut.println( ( (char) ForkingRunListener.BOOTERCODE_ERROR )
121                                      + ",0," + stringBuilder.toString() );
122             }
123             // Say bye.
124             originalOut.println( ( (char) ForkingRunListener.BOOTERCODE_BYE ) + ",0,BYE!" );
125             originalOut.flush();
126             // noinspection CallToSystemExit
127             exit( 0 );
128         }
129         catch ( Throwable t )
130         {
131             // Just throwing does getMessage() and a local trace - we want to call printStackTrace for a full trace
132             // noinspection UseOfSystemOutOrSystemErr
133             t.printStackTrace( System.err );
134             // noinspection ProhibitedExceptionThrown,CallToSystemExit
135             exit( 1 );
136         }
137     }
138 
139     private static final long SYSTEM_EXIT_TIMEOUT = 30 * 1000;
140 
141     private static void exit( final int returnCode )
142     {
143         launchLastDitchDaemonShutdownThread( returnCode );
144         System.exit( returnCode );
145     }
146 
147 
148     private static RunResult runSuitesInProcess( Object testSet, StartupConfiguration startupConfiguration,
149                                                  ProviderConfiguration providerConfiguration,
150                                                  PrintStream originalSystemOut )
151         throws SurefireExecutionException, TestSetFailedException, InvocationTargetException
152     {
153         final ReporterFactory factory = createForkingReporterFactory( providerConfiguration, originalSystemOut );
154 
155         return invokeProviderInSameClassLoader( testSet, factory, providerConfiguration, true, startupConfiguration,
156                                                 false );
157     }
158 
159     private static ReporterFactory createForkingReporterFactory( ProviderConfiguration providerConfiguration,
160                                                                  PrintStream originalSystemOut )
161     {
162         final Boolean trimStackTrace = providerConfiguration.getReporterConfiguration().isTrimStackTrace();
163         return SurefireReflector.createForkingReporterFactoryInCurrentClassLoader( trimStackTrace, originalSystemOut );
164     }
165 
166     @SuppressWarnings( "checkstyle:emptyblock" )
167     private static void launchLastDitchDaemonShutdownThread( final int returnCode )
168     {
169         Thread lastExit = new Thread( new Runnable()
170         {
171             public void run()
172             {
173                 try
174                 {
175                     Thread.sleep( SYSTEM_EXIT_TIMEOUT );
176                     Runtime.getRuntime().halt( returnCode );
177                 }
178                 catch ( InterruptedException ignore )
179                 {
180                 }
181             }
182         } );
183         lastExit.setDaemon( true );
184         lastExit.start();
185     }
186 
187     public static RunResult invokeProviderInSameClassLoader( Object testSet, Object factory,
188                                                              ProviderConfiguration providerConfiguration,
189                                                              boolean insideFork,
190                                                              StartupConfiguration startupConfiguration1,
191                                                              boolean restoreStreams )
192         throws TestSetFailedException, InvocationTargetException
193     {
194         final PrintStream orgSystemOut = System.out;
195         final PrintStream orgSystemErr = System.err;
196         // Note that System.out/System.err are also read in the "ReporterConfiguration" instatiation
197         // in createProvider below. These are the same values as here.
198 
199         final SurefireProvider provider =
200             createProviderInCurrentClassloader( startupConfiguration1, insideFork, providerConfiguration, factory );
201         try
202         {
203             return provider.invoke( testSet );
204         }
205         finally
206         {
207             if ( restoreStreams && System.getSecurityManager() == null )
208             {
209                 System.setOut( orgSystemOut );
210                 System.setErr( orgSystemErr );
211             }
212         }
213     }
214 
215     public static SurefireProvider createProviderInCurrentClassloader( StartupConfiguration startupConfiguration1,
216                                                                        boolean isInsideFork,
217                                                                        ProviderConfiguration providerConfiguration,
218                                                                        Object reporterManagerFactory1 )
219     {
220 
221         BaseProviderFactory bpf = new BaseProviderFactory( (ReporterFactory) reporterManagerFactory1, isInsideFork );
222         bpf.setTestRequest( providerConfiguration.getTestSuiteDefinition() );
223         bpf.setReporterConfiguration( providerConfiguration.getReporterConfiguration() );
224         ClassLoader clasLoader = Thread.currentThread().getContextClassLoader();
225         bpf.setClassLoaders( clasLoader );
226         bpf.setTestArtifactInfo( providerConfiguration.getTestArtifact() );
227         bpf.setProviderProperties( providerConfiguration.getProviderProperties() );
228         bpf.setRunOrderParameters( providerConfiguration.getRunOrderParameters() );
229         bpf.setDirectoryScannerParameters( providerConfiguration.getDirScannerParams() );
230         return (SurefireProvider) ReflectionUtils.instantiateOneArg( clasLoader,
231                                                                      startupConfiguration1.getActualClassName(),
232                                                                      ProviderParameters.class, bpf );
233     }
234 }