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  import org.apache.maven.surefire.report.LegacyPojoStackTraceWriter;
28  import org.apache.maven.surefire.report.StackTraceWriter;
29  import org.apache.maven.surefire.suite.RunResult;
30  import org.apache.maven.surefire.testset.TestSetFailedException;
31  import org.apache.maven.surefire.util.LazyTestsToRun;
32  
33  /**
34   * The part of the booter that is unique to a forked vm.
35   * <p/>
36   * Deals with deserialization of the booter wire-level protocol
37   * <p/>
38   *
39   * @author Jason van Zyl
40   * @author Emmanuel Venisse
41   * @author Kristian Rosenvold
42   */
43  public class ForkedBooter
44  {
45  
46      /**
47       * This method is invoked when Surefire is forked - this method parses and organizes the arguments passed to it and
48       * then calls the Surefire class' run method. <p/> The system exit code will be 1 if an exception is thrown.
49       *
50       * @param args Commandline arguments
51       * @throws Throwable Upon throwables
52       */
53      public static void main( String[] args )
54          throws Throwable
55      {
56          final PrintStream originalOut = System.out;
57          try
58          {
59              if ( args.length > 1 )
60              {
61                  SystemPropertyManager.setSystemProperties( new File( args[1] ) );
62              }
63  
64              File surefirePropertiesFile = new File( args[0] );
65              InputStream stream = surefirePropertiesFile.exists() ? new FileInputStream( surefirePropertiesFile ) : null;
66              BooterDeserializer booterDeserializer = new BooterDeserializer( stream );
67              ProviderConfiguration providerConfiguration = booterDeserializer.deserialize();
68              final StartupConfiguration startupConfiguration = booterDeserializer.getProviderConfiguration();
69  
70              TypeEncodedValue forkedTestSet = providerConfiguration.getTestForFork();
71              boolean readTestsFromInputStream = providerConfiguration.isReadTestsFromInStream();
72  
73              final ClasspathConfiguration classpathConfiguration = startupConfiguration.getClasspathConfiguration();
74              final ClassLoader testClassLoader = classpathConfiguration.createForkingTestClassLoader(
75                  startupConfiguration.isManifestOnlyJarRequestedAndUsable() );
76  
77              startupConfiguration.writeSurefireTestClasspathProperty();
78  
79              Object testSet;
80              if ( forkedTestSet != null )
81              {
82                  testSet = forkedTestSet.getDecodedValue( testClassLoader );
83              }
84              else if ( readTestsFromInputStream )
85              {
86                  testSet = new LazyTestsToRun( System.in, testClassLoader, originalOut );
87              }
88              else
89              {
90                  testSet = null;
91              }
92  
93              try
94              {
95                  runSuitesInProcess( testSet, testClassLoader, startupConfiguration, providerConfiguration,
96                                      originalOut );
97              }
98              catch ( InvocationTargetException t )
99              {
100 
101                 LegacyPojoStackTraceWriter stackTraceWriter =
102                     new LegacyPojoStackTraceWriter( "test subystem", "no method", t.getTargetException() );
103                 StringBuffer stringBuffer = new StringBuffer();
104                 ForkingRunListener.encode( stringBuffer, stackTraceWriter, false );
105                 originalOut.println( ( (char) ForkingRunListener.BOOTERCODE_ERROR ) + ",0," + stringBuffer.toString() );
106             }
107             catch ( Throwable t )
108             {
109                 StackTraceWriter stackTraceWriter = new LegacyPojoStackTraceWriter( "test subystem", "no method", t );
110                 StringBuffer stringBuffer = new StringBuffer();
111                 ForkingRunListener.encode( stringBuffer, stackTraceWriter, false );
112                 originalOut.println( ( (char) ForkingRunListener.BOOTERCODE_ERROR ) + ",0," + stringBuffer.toString() );
113             }
114             // Say bye.
115             originalOut.println( ( (char) ForkingRunListener.BOOTERCODE_BYE ) + ",0,BYE!" );
116             originalOut.flush();
117             // noinspection CallToSystemExit
118             exit( 0 );
119         }
120         catch ( Throwable t )
121         {
122             // Just throwing does getMessage() and a local trace - we want to call printStackTrace for a full trace
123             // noinspection UseOfSystemOutOrSystemErr
124             t.printStackTrace( System.err );
125             // noinspection ProhibitedExceptionThrown,CallToSystemExit
126             exit( 1 );
127         }
128     }
129 
130     private final static long SYSTEM_EXIT_TIMEOUT = 30 * 1000;
131 
132     private static void exit( final int returnCode )
133     {
134         launchLastDitchDaemonShutdownThread( returnCode );
135         System.exit( returnCode );
136     }
137 
138 
139     private static RunResult runSuitesInProcess( Object testSet, ClassLoader testsClassLoader,
140                                                  StartupConfiguration startupConfiguration,
141                                                  ProviderConfiguration providerConfiguration,
142                                                  PrintStream originalSystemOut )
143         throws SurefireExecutionException, TestSetFailedException, InvocationTargetException
144     {
145         final ClasspathConfiguration classpathConfiguration = startupConfiguration.getClasspathConfiguration();
146         ClassLoader surefireClassLoader = classpathConfiguration.createSurefireClassLoader( testsClassLoader );
147 
148         SurefireReflector surefireReflector = new SurefireReflector( surefireClassLoader );
149 
150         final Object factory =
151             createForkingReporterFactory( surefireReflector, providerConfiguration, originalSystemOut );
152 
153         return ProviderFactory.invokeProvider( testSet, testsClassLoader, surefireClassLoader, factory,
154                                                providerConfiguration, true, startupConfiguration, false );
155     }
156 
157     private static Object createForkingReporterFactory( SurefireReflector surefireReflector,
158                                                         ProviderConfiguration providerConfiguration,
159                                                         PrintStream originalSystemOut )
160     {
161         final Boolean trimStackTrace = providerConfiguration.getReporterConfiguration().isTrimStackTrace();
162         return surefireReflector.createForkingReporterFactory( trimStackTrace, originalSystemOut );
163     }
164 
165     private static void launchLastDitchDaemonShutdownThread( final int returnCode )
166     {
167         Thread lastExit = new Thread( new Runnable()
168         {
169             public void run()
170             {
171                 try
172                 {
173                     Thread.sleep( SYSTEM_EXIT_TIMEOUT );
174                     Runtime.getRuntime().halt( returnCode );
175                 }
176                 catch ( InterruptedException ignore )
177                 {
178                 }
179             }
180         } );
181         lastExit.setDaemon( true );
182         lastExit.start();
183     }
184 
185 }