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