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                 StringBuffer stringBuffer = new StringBuffer();
111                 ForkingRunListener.encode( stringBuffer, stackTraceWriter, false );
112                 originalOut.println( ( (char) ForkingRunListener.BOOTERCODE_ERROR ) + ",0," + stringBuffer.toString() );
113             }
114             catch ( Throwable t )
115             {
116                 StackTraceWriter stackTraceWriter = new LegacyPojoStackTraceWriter( "test subystem", "no method", t );
117                 StringBuffer stringBuffer = new StringBuffer();
118                 ForkingRunListener.encode( stringBuffer, stackTraceWriter, false );
119                 originalOut.println( ( (char) ForkingRunListener.BOOTERCODE_ERROR ) + ",0," + stringBuffer.toString() );
120             }
121             // Say bye.
122             originalOut.println( ( (char) ForkingRunListener.BOOTERCODE_BYE ) + ",0,BYE!" );
123             originalOut.flush();
124             // noinspection CallToSystemExit
125             exit( 0 );
126         }
127         catch ( Throwable t )
128         {
129             // Just throwing does getMessage() and a local trace - we want to call printStackTrace for a full trace
130             // noinspection UseOfSystemOutOrSystemErr
131             t.printStackTrace( System.err );
132             // noinspection ProhibitedExceptionThrown,CallToSystemExit
133             exit( 1 );
134         }
135     }
136 
137     private final static long SYSTEM_EXIT_TIMEOUT = 30 * 1000;
138 
139     private static void exit( final int returnCode )
140     {
141         launchLastDitchDaemonShutdownThread( returnCode );
142         System.exit( returnCode );
143     }
144 
145 
146     private static RunResult runSuitesInProcess( Object testSet, StartupConfiguration startupConfiguration,
147                                                  ProviderConfiguration providerConfiguration,
148                                                  PrintStream originalSystemOut )
149         throws SurefireExecutionException, TestSetFailedException, InvocationTargetException
150     {
151         final ReporterFactory factory = createForkingReporterFactory( providerConfiguration, originalSystemOut );
152 
153         return invokeProviderInSameClassLoader( testSet, factory, providerConfiguration, true, startupConfiguration,
154                                                 false );
155     }
156 
157     private static ReporterFactory createForkingReporterFactory( ProviderConfiguration providerConfiguration,
158                                                                  PrintStream originalSystemOut )
159     {
160         final Boolean trimStackTrace = providerConfiguration.getReporterConfiguration().isTrimStackTrace();
161         return SurefireReflector.createForkingReporterFactoryInCurrentClassLoader( trimStackTrace, originalSystemOut );
162     }
163 
164     private static void launchLastDitchDaemonShutdownThread( final int returnCode )
165     {
166         Thread lastExit = new Thread( new Runnable()
167         {
168             public void run()
169             {
170                 try
171                 {
172                     Thread.sleep( SYSTEM_EXIT_TIMEOUT );
173                     Runtime.getRuntime().halt( returnCode );
174                 }
175                 catch ( InterruptedException ignore )
176                 {
177                 }
178             }
179         } );
180         lastExit.setDaemon( true );
181         lastExit.start();
182     }
183 
184     public static RunResult invokeProviderInSameClassLoader( Object testSet, Object factory,
185                                                              ProviderConfiguration providerConfiguration,
186                                                              boolean insideFork,
187                                                              StartupConfiguration startupConfiguration1,
188                                                              boolean restoreStreams )
189         throws TestSetFailedException, InvocationTargetException
190     {
191         final PrintStream orgSystemOut = System.out;
192         final PrintStream orgSystemErr = System.err;
193         // Note that System.out/System.err are also read in the "ReporterConfiguration" instatiation
194         // in createProvider below. These are the same values as here.
195 
196         final SurefireProvider provider =
197             createProviderInCurrentClassloader( startupConfiguration1, insideFork, providerConfiguration, factory );
198         try
199         {
200             return provider.invoke( testSet );
201         }
202         finally
203         {
204             if ( restoreStreams && System.getSecurityManager() == null )
205             {
206                 System.setOut( orgSystemOut );
207                 System.setErr( orgSystemErr );
208             }
209         }
210     }
211 
212     public static SurefireProvider createProviderInCurrentClassloader( StartupConfiguration startupConfiguration1,
213                                                                        boolean isInsideFork,
214                                                                        ProviderConfiguration providerConfiguration,
215                                                                        Object reporterManagerFactory1 )
216     {
217 
218         BaseProviderFactory bpf = new BaseProviderFactory( (ReporterFactory) reporterManagerFactory1, isInsideFork );
219         bpf.setTestRequest( providerConfiguration.getTestSuiteDefinition() );
220         bpf.setReporterConfiguration( providerConfiguration.getReporterConfiguration() );
221         ClassLoader clasLoader = Thread.currentThread().getContextClassLoader();
222         bpf.setClassLoaders( clasLoader );
223         bpf.setTestArtifactInfo( providerConfiguration.getTestArtifact() );
224         bpf.setProviderProperties( providerConfiguration.getProviderProperties() );
225         bpf.setRunOrderParameters( providerConfiguration.getRunOrderParameters() );
226         bpf.setDirectoryScannerParameters( providerConfiguration.getDirScannerParams() );
227         return (SurefireProvider) ReflectionUtils.instantiateOneArg( clasLoader,
228                                                                      startupConfiguration1.getActualClassName(),
229                                                                      ProviderParameters.class, bpf );
230     }
231 }