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.PrintStream;
24  import java.lang.reflect.Constructor;
25  import java.lang.reflect.InvocationHandler;
26  import java.lang.reflect.Method;
27  import java.util.ArrayList;
28  import java.util.Collection;
29  import java.util.List;
30  import java.util.Map;
31  
32  import org.apache.maven.surefire.cli.CommandLineOption;
33  import org.apache.maven.surefire.providerapi.ProviderParameters;
34  import org.apache.maven.surefire.report.ReporterConfiguration;
35  import org.apache.maven.surefire.report.ReporterFactory;
36  import org.apache.maven.surefire.suite.RunResult;
37  import org.apache.maven.surefire.testset.DirectoryScannerParameters;
38  import org.apache.maven.surefire.testset.RunOrderParameters;
39  import org.apache.maven.surefire.testset.TestArtifactInfo;
40  import org.apache.maven.surefire.testset.TestListResolver;
41  import org.apache.maven.surefire.testset.TestRequest;
42  import org.apache.maven.surefire.util.RunOrder;
43  import org.apache.maven.surefire.util.SurefireReflectionException;
44  
45  import static java.util.Collections.checkedList;
46  
47  import static org.apache.maven.surefire.util.ReflectionUtils.getConstructor;
48  import static org.apache.maven.surefire.util.ReflectionUtils.getMethod;
49  import static org.apache.maven.surefire.util.ReflectionUtils.invokeGetter;
50  import static org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray;
51  import static org.apache.maven.surefire.util.ReflectionUtils.instantiateOneArg;
52  import static org.apache.maven.surefire.util.ReflectionUtils.invokeSetter;
53  import static org.apache.maven.surefire.util.ReflectionUtils.instantiateTwoArgs;
54  import static org.apache.maven.surefire.util.ReflectionUtils.newInstance;
55  
56  /**
57   * Does reflection based invocation of the surefire methods.
58   * <p/>
59   * This is to avoid complications with linkage issues
60   *
61   * @author Kristian Rosenvold
62   */
63  public class SurefireReflector
64  {
65      private final ClassLoader surefireClassLoader;
66  
67      private final Class<?> reporterConfiguration;
68  
69      private final Class<?> testRequest;
70  
71      private final Class<?> testArtifactInfo;
72  
73      private final Class<?> testArtifactInfoAware;
74  
75      private final Class<?> directoryScannerParameters;
76  
77      private final Class<?> runOrderParameters;
78  
79      private final Class<?> directoryScannerParametersAware;
80  
81      private final Class<?> testSuiteDefinitionAware;
82  
83      private final Class<?> testClassLoaderAware;
84  
85      private final Class<?> reporterConfigurationAware;
86  
87      private final Class<?> providerPropertiesAware;
88  
89      private final Class<?> runResult;
90  
91      private final Class<?> booterParameters;
92  
93      private final Class<?> reporterFactory;
94  
95      private final Class<?> testListResolver;
96  
97      private final Class<?> mainCliOptions;
98  
99      private final Class<Enum> commandLineOptionsClass;
100 
101     private final Class<?> shutdownAwareClass;
102 
103     private final Class<Enum> shutdownClass;
104 
105 
106     @SuppressWarnings( "unchecked" )
107     public SurefireReflector( ClassLoader surefireClassLoader )
108     {
109         this.surefireClassLoader = surefireClassLoader;
110         try
111         {
112             reporterConfiguration = surefireClassLoader.loadClass( ReporterConfiguration.class.getName() );
113             testRequest = surefireClassLoader.loadClass( TestRequest.class.getName() );
114             testArtifactInfo = surefireClassLoader.loadClass( TestArtifactInfo.class.getName() );
115             testArtifactInfoAware = surefireClassLoader.loadClass( TestArtifactInfoAware.class.getName() );
116             directoryScannerParameters = surefireClassLoader.loadClass( DirectoryScannerParameters.class.getName() );
117             runOrderParameters = surefireClassLoader.loadClass( RunOrderParameters.class.getName() );
118             directoryScannerParametersAware =
119                 surefireClassLoader.loadClass( DirectoryScannerParametersAware.class.getName() );
120             testSuiteDefinitionAware = surefireClassLoader.loadClass( TestRequestAware.class.getName() );
121             testClassLoaderAware = surefireClassLoader.loadClass( SurefireClassLoadersAware.class.getName() );
122             reporterConfigurationAware = surefireClassLoader.loadClass( ReporterConfigurationAware.class.getName() );
123             providerPropertiesAware = surefireClassLoader.loadClass( ProviderPropertiesAware.class.getName() );
124             reporterFactory = surefireClassLoader.loadClass( ReporterFactory.class.getName() );
125             runResult = surefireClassLoader.loadClass( RunResult.class.getName() );
126             booterParameters = surefireClassLoader.loadClass( ProviderParameters.class.getName() );
127             testListResolver = surefireClassLoader.loadClass( TestListResolver.class.getName() );
128             mainCliOptions = surefireClassLoader.loadClass( MainCliOptionsAware.class.getName() );
129             commandLineOptionsClass = (Class<Enum>) surefireClassLoader.loadClass( CommandLineOption.class.getName() );
130             shutdownAwareClass = surefireClassLoader.loadClass( ShutdownAware.class.getName() );
131             shutdownClass = (Class<Enum>) surefireClassLoader.loadClass( Shutdown.class.getName() );
132         }
133         catch ( ClassNotFoundException e )
134         {
135             throw new SurefireReflectionException( e );
136         }
137     }
138 
139     public Object convertIfRunResult( Object result )
140     {
141         if ( result == null || !isRunResult( result ) )
142         {
143             return result;
144         }
145         int getCompletedCount1 = (Integer) invokeGetter( result, "getCompletedCount" );
146         int getErrors = (Integer) invokeGetter( result, "getErrors" );
147         int getSkipped = (Integer) invokeGetter( result, "getSkipped" );
148         int getFailures = (Integer) invokeGetter( result, "getFailures" );
149         return new RunResult( getCompletedCount1, getErrors, getFailures, getSkipped );
150 
151     }
152 
153     /**
154      * @noinspection UnusedDeclaration
155      */
156     class ClassLoaderProxy
157         implements InvocationHandler
158     {
159         private final Object target;
160 
161         /**
162          * @param delegate a target
163          * @noinspection UnusedDeclaration
164          */
165         public ClassLoaderProxy( Object delegate )
166         {
167             this.target = delegate;
168         }
169 
170         public Object invoke( Object proxy, Method method, Object[] args )
171             throws Throwable
172         {
173             Method delegateMethod = target.getClass().getMethod( method.getName(), method.getParameterTypes() );
174             return delegateMethod.invoke( target, args );
175         }
176     }
177 
178     Object createTestRequest( TestRequest suiteDefinition )
179     {
180         if ( suiteDefinition == null )
181         {
182             return null;
183         }
184         else
185         {
186             Object resolver = createTestListResolver( suiteDefinition.getTestListResolver() );
187             Class<?>[] arguments = { List.class, File.class, testListResolver, int.class };
188             Constructor constructor = getConstructor( testRequest, arguments );
189             return newInstance( constructor,
190                                 suiteDefinition.getSuiteXmlFiles(),
191                                 suiteDefinition.getTestSourceDirectory(),
192                                 resolver,
193                                 suiteDefinition.getRerunFailingTestsCount() );
194         }
195     }
196 
197     Object createTestListResolver( TestListResolver resolver )
198     {
199         if ( resolver == null )
200         {
201             return null;
202         }
203         else
204         {
205             Constructor constructor = getConstructor( testListResolver, String.class );
206             return newInstance( constructor, resolver.getPluginParameterTest() );
207         }
208     }
209 
210     Object createDirectoryScannerParameters( DirectoryScannerParameters directoryScannerParameters )
211     {
212         if ( directoryScannerParameters == null )
213         {
214             return null;
215         }
216         //Can't use the constructor with the RunOrder parameter. Using it causes some integration tests to fail.
217         Class<?>[] arguments = { File.class, List.class, List.class, List.class, boolean.class, String.class };
218         Constructor constructor = getConstructor( this.directoryScannerParameters, arguments );
219         return newInstance( constructor,
220                             directoryScannerParameters.getTestClassesDirectory(),
221                             directoryScannerParameters.getIncludes(),
222                             directoryScannerParameters.getExcludes(),
223                             directoryScannerParameters.getSpecificTests(),
224                             directoryScannerParameters.isFailIfNoTests(),
225                             RunOrder.asString( directoryScannerParameters.getRunOrder() ) );
226     }
227 
228 
229     Object createRunOrderParameters( RunOrderParameters runOrderParameters )
230     {
231         if ( runOrderParameters == null )
232         {
233             return null;
234         }
235         //Can't use the constructor with the RunOrder parameter. Using it causes some integration tests to fail.
236         Class<?>[] arguments = { String.class, String.class };
237         Constructor constructor = getConstructor( this.runOrderParameters, arguments );
238         File runStatisticsFile = runOrderParameters.getRunStatisticsFile();
239         return newInstance( constructor, RunOrder.asString( runOrderParameters.getRunOrder() ),
240                             runStatisticsFile == null ? null : runStatisticsFile.getAbsolutePath() );
241     }
242 
243     Object createTestArtifactInfo( TestArtifactInfo testArtifactInfo )
244     {
245         if ( testArtifactInfo == null )
246         {
247             return null;
248         }
249         Class<?>[] arguments = { String.class, String.class };
250         Constructor constructor = getConstructor( this.testArtifactInfo, arguments );
251         return newInstance( constructor, testArtifactInfo.getVersion(), testArtifactInfo.getClassifier() );
252     }
253 
254 
255     Object createReporterConfiguration( ReporterConfiguration reporterConfig )
256     {
257         Constructor constructor = getConstructor( reporterConfiguration, File.class, boolean.class );
258         return newInstance( constructor, reporterConfig.getReportsDirectory(), reporterConfig.isTrimStackTrace() );
259     }
260 
261     public static ReporterFactory createForkingReporterFactoryInCurrentClassLoader( boolean trimStackTrace,
262                                                                                     PrintStream originalSystemOut )
263     {
264         return new ForkingReporterFactory( trimStackTrace, originalSystemOut );
265     }
266 
267     public Object createBooterConfiguration( ClassLoader surefireClassLoader, Object factoryInstance,
268                                              boolean insideFork )
269     {
270         return instantiateTwoArgs( surefireClassLoader, BaseProviderFactory.class.getName(),
271                                    reporterFactory, factoryInstance, boolean.class, insideFork );
272     }
273 
274     public Object instantiateProvider( String providerClassName, Object booterParameters )
275     {
276         return instantiateOneArg( surefireClassLoader, providerClassName, this.booterParameters, booterParameters );
277     }
278 
279     public void setIfDirScannerAware( Object o, DirectoryScannerParameters dirScannerParams )
280     {
281         if ( directoryScannerParametersAware.isAssignableFrom( o.getClass() ) )
282         {
283             setDirectoryScannerParameters( o, dirScannerParams );
284         }
285     }
286 
287     public void setMainCliOptions( Object o, List<CommandLineOption> options )
288     {
289         if ( mainCliOptions.isAssignableFrom( o.getClass() ) )
290         {
291             List<Enum> newOptions = checkedList( new ArrayList<Enum>( options.size() ), commandLineOptionsClass );
292             Collection<Integer> ordinals = toOrdinals( options );
293             for ( Enum e : commandLineOptionsClass.getEnumConstants() )
294             {
295                 if ( ordinals.contains( e.ordinal() ) )
296                 {
297                     newOptions.add( e );
298                 }
299             }
300             invokeSetter( o, "setMainCliOptions", List.class, newOptions );
301         }
302     }
303 
304     public void setSkipAfterFailureCount( Object o, int skipAfterFailureCount )
305     {
306         invokeSetter( o, "setSkipAfterFailureCount", int.class, skipAfterFailureCount );
307     }
308 
309     public void setShutdown( Object o, Shutdown shutdown )
310     {
311         if ( shutdownAwareClass.isAssignableFrom( o.getClass() ) )
312         {
313             for ( Enum e : shutdownClass.getEnumConstants() )
314             {
315                 if ( shutdown.ordinal() == e.ordinal() )
316                 {
317                     invokeSetter( o, "setShutdown", shutdownClass, e );
318                     break;
319                 }
320             }
321         }
322     }
323 
324     public void setDirectoryScannerParameters( Object o, DirectoryScannerParameters dirScannerParams )
325     {
326         Object param = createDirectoryScannerParameters( dirScannerParams );
327         invokeSetter( o, "setDirectoryScannerParameters", directoryScannerParameters, param );
328     }
329 
330     public void setRunOrderParameters( Object o, RunOrderParameters runOrderParameters )
331     {
332         Object param = createRunOrderParameters( runOrderParameters );
333         invokeSetter( o, "setRunOrderParameters", this.runOrderParameters, param );
334     }
335 
336     public void setTestSuiteDefinitionAware( Object o, TestRequest testSuiteDefinition2 )
337     {
338         if ( testSuiteDefinitionAware.isAssignableFrom( o.getClass() ) )
339         {
340             setTestSuiteDefinition( o, testSuiteDefinition2 );
341         }
342     }
343 
344     void setTestSuiteDefinition( Object o, TestRequest testSuiteDefinition1 )
345     {
346         Object param = createTestRequest( testSuiteDefinition1 );
347         invokeSetter( o, "setTestRequest", testRequest, param );
348     }
349 
350     public void setProviderPropertiesAware( Object o, Map<String, String> properties )
351     {
352         if ( providerPropertiesAware.isAssignableFrom( o.getClass() ) )
353         {
354             setProviderProperties( o, properties );
355         }
356     }
357 
358     void setProviderProperties( Object o, Map<String, String> providerProperties )
359     {
360         invokeSetter( o, "setProviderProperties", Map.class, providerProperties );
361     }
362 
363     public void setReporterConfigurationAware( Object o, ReporterConfiguration reporterConfiguration1 )
364     {
365         if ( reporterConfigurationAware.isAssignableFrom( o.getClass() ) )
366         {
367             setReporterConfiguration( o, reporterConfiguration1 );
368         }
369     }
370 
371 
372     void setReporterConfiguration( Object o, ReporterConfiguration reporterConfiguration )
373     {
374         Object param = createReporterConfiguration( reporterConfiguration );
375         invokeSetter( o, "setReporterConfiguration", this.reporterConfiguration, param );
376     }
377 
378     public void setTestClassLoaderAware( Object o, ClassLoader testClassLoader )
379     {
380         if ( testClassLoaderAware.isAssignableFrom( o.getClass() ) )
381         {
382             setTestClassLoader( o, testClassLoader );
383         }
384     }
385 
386     void setTestClassLoader( Object o, ClassLoader testClassLoader )
387     {
388         Method setter = getMethod( o, "setClassLoaders", ClassLoader.class );
389         invokeMethodWithArray( o, setter, testClassLoader );
390     }
391 
392     public void setTestArtifactInfoAware( Object o, TestArtifactInfo testArtifactInfo1 )
393     {
394         if ( testArtifactInfoAware.isAssignableFrom( o.getClass() ) )
395         {
396             setTestArtifactInfo( o, testArtifactInfo1 );
397         }
398     }
399 
400     void setTestArtifactInfo( Object o, TestArtifactInfo testArtifactInfo )
401     {
402         Object param = createTestArtifactInfo( testArtifactInfo );
403         invokeSetter( o, "setTestArtifactInfo", this.testArtifactInfo, param );
404     }
405 
406     private boolean isRunResult( Object o )
407     {
408         return runResult.isAssignableFrom( o.getClass() );
409     }
410 
411     private static Collection<Integer> toOrdinals( Collection<? extends Enum> enums )
412     {
413         Collection<Integer> ordinals = new ArrayList<Integer>( enums.size() );
414         for ( Enum e : enums )
415         {
416             ordinals.add( e.ordinal() );
417         }
418         return ordinals;
419     }
420 
421 }