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.List;
28  import java.util.Properties;
29  import org.apache.maven.surefire.providerapi.ProviderParameters;
30  import org.apache.maven.surefire.report.ReporterConfiguration;
31  import org.apache.maven.surefire.report.ReporterFactory;
32  import org.apache.maven.surefire.suite.RunResult;
33  import org.apache.maven.surefire.testset.DirectoryScannerParameters;
34  import org.apache.maven.surefire.testset.RunOrderParameters;
35  import org.apache.maven.surefire.testset.TestArtifactInfo;
36  import org.apache.maven.surefire.testset.TestRequest;
37  import org.apache.maven.surefire.util.ReflectionUtils;
38  import org.apache.maven.surefire.util.RunOrder;
39  import org.apache.maven.surefire.util.SurefireReflectionException;
40  
41  /**
42   * Does reflection based invocation of the surefire methods.
43   * <p/>
44   * This is to avoid complications with linkage issues
45   *
46   * @author Kristian Rosenvold
47   */
48  public class SurefireReflector
49  {
50      private final ClassLoader surefireClassLoader;
51  
52      private final Class reporterConfiguration;
53  
54      private final Class testRequest;
55  
56      private final Class testArtifactInfo;
57  
58      private final Class testArtifactInfoAware;
59  
60      private final Class directoryScannerParameters;
61  
62      private final Class runOrderParameters;
63  
64      private final Class directoryScannerParametersAware;
65  
66      private final Class testSuiteDefinitionAware;
67  
68      private final Class testClassLoaderAware;
69  
70      private final Class reporterConfigurationAware;
71  
72      private final Class providerPropertiesAware;
73  
74      private final Class runResult;
75  
76      private final Class booterParameters;
77  
78      private final Class reporterFactory;
79  
80  
81      public SurefireReflector( ClassLoader surefireClassLoader )
82      {
83          this.surefireClassLoader = surefireClassLoader;
84          try
85          {
86              reporterConfiguration = surefireClassLoader.loadClass( ReporterConfiguration.class.getName() );
87              testRequest = surefireClassLoader.loadClass( TestRequest.class.getName() );
88              testArtifactInfo = surefireClassLoader.loadClass( TestArtifactInfo.class.getName() );
89              testArtifactInfoAware = surefireClassLoader.loadClass( TestArtifactInfoAware.class.getName() );
90              directoryScannerParameters = surefireClassLoader.loadClass( DirectoryScannerParameters.class.getName() );
91              runOrderParameters = surefireClassLoader.loadClass( RunOrderParameters.class.getName() );
92              directoryScannerParametersAware =
93                  surefireClassLoader.loadClass( DirectoryScannerParametersAware.class.getName() );
94              testSuiteDefinitionAware = surefireClassLoader.loadClass( TestRequestAware.class.getName() );
95              testClassLoaderAware = surefireClassLoader.loadClass( SurefireClassLoadersAware.class.getName() );
96              reporterConfigurationAware = surefireClassLoader.loadClass( ReporterConfigurationAware.class.getName() );
97              providerPropertiesAware = surefireClassLoader.loadClass( ProviderPropertiesAware.class.getName() );
98              reporterFactory = surefireClassLoader.loadClass( ReporterFactory.class.getName() );
99              runResult = surefireClassLoader.loadClass( RunResult.class.getName() );
100             booterParameters = surefireClassLoader.loadClass( ProviderParameters.class.getName() );
101         }
102         catch ( ClassNotFoundException e )
103         {
104             throw new SurefireReflectionException( e );
105         }
106     }
107 
108     public Object convertIfRunResult( Object result )
109     {
110         if ( result == null || !isRunResult( result ) )
111         {
112             return result;
113         }
114         final Integer getCompletedCount1 = (Integer) ReflectionUtils.invokeGetter( result, "getCompletedCount" );
115         final Integer getErrors = (Integer) ReflectionUtils.invokeGetter( result, "getErrors" );
116         final Integer getSkipped = (Integer) ReflectionUtils.invokeGetter( result, "getSkipped" );
117         final Integer getFailures = (Integer) ReflectionUtils.invokeGetter( result, "getFailures" );
118         return new RunResult( getCompletedCount1, getErrors, getFailures, getSkipped );
119 
120     }
121 
122 
123     /**
124      * @noinspection UnusedDeclaration
125      */
126     class ClassLoaderProxy
127         implements InvocationHandler
128     {
129         private final Object target;
130 
131         /**
132          * @param delegate a target
133          * @noinspection UnusedDeclaration
134          */
135         public ClassLoaderProxy( Object delegate )
136         {
137             this.target = delegate;
138         }
139 
140         public Object invoke( Object proxy, Method method, Object[] args )
141             throws Throwable
142         {
143             Method delegateMethod = target.getClass().getMethod( method.getName(), method.getParameterTypes() );
144             return delegateMethod.invoke( target, args );
145         }
146     }
147 
148 
149     Object createTestRequest( TestRequest suiteDefinition )
150     {
151         if ( suiteDefinition == null )
152         {
153             return null;
154         }
155         Class[] arguments = { List.class, File.class, String.class, String.class, int.class };
156         Constructor constructor = ReflectionUtils.getConstructor( this.testRequest, arguments );
157         return ReflectionUtils.newInstance( constructor, new Object[]{ suiteDefinition.getSuiteXmlFiles(),
158             suiteDefinition.getTestSourceDirectory(), suiteDefinition.getRequestedTest(),
159             suiteDefinition.getRequestedTestMethod(), suiteDefinition.getRerunFailingTestsCount() } );
160     }
161 
162 
163     Object createDirectoryScannerParameters( DirectoryScannerParameters directoryScannerParameters )
164     {
165         if ( directoryScannerParameters == null )
166         {
167             return null;
168         }
169         //Can't use the constructor with the RunOrder parameter. Using it causes some integration tests to fail.
170         Class[] arguments = { File.class, List.class, List.class, List.class, Boolean.class, String.class };
171         Constructor constructor = ReflectionUtils.getConstructor( this.directoryScannerParameters, arguments );
172         return ReflectionUtils.newInstance( constructor,
173                                             new Object[]{ directoryScannerParameters.getTestClassesDirectory(),
174                                                 directoryScannerParameters.getIncludes(),
175                                                 directoryScannerParameters.getExcludes(),
176                                                 directoryScannerParameters.getSpecificTests(),
177                                                 directoryScannerParameters.isFailIfNoTests(),
178                                                 RunOrder.asString( directoryScannerParameters.getRunOrder() ) } );
179     }
180 
181 
182     Object createRunOrderParameters( RunOrderParameters runOrderParameters )
183     {
184         if ( runOrderParameters == null )
185         {
186             return null;
187         }
188         //Can't use the constructor with the RunOrder parameter. Using it causes some integration tests to fail.
189         Class[] arguments = { String.class, String.class };
190         Constructor constructor = ReflectionUtils.getConstructor( this.runOrderParameters, arguments );
191         final File runStatisticsFile = runOrderParameters.getRunStatisticsFile();
192         return ReflectionUtils.newInstance( constructor,
193                                             new Object[]{ RunOrder.asString( runOrderParameters.getRunOrder() ),
194                                                 runStatisticsFile != null
195                                                     ? runStatisticsFile.getAbsolutePath()
196                                                     : null } );
197     }
198 
199     Object createTestArtifactInfo( TestArtifactInfo testArtifactInfo )
200     {
201         if ( testArtifactInfo == null )
202         {
203             return null;
204         }
205         Class[] arguments = { String.class, String.class };
206         Constructor constructor = ReflectionUtils.getConstructor( this.testArtifactInfo, arguments );
207         return ReflectionUtils.newInstance( constructor, new Object[]{ testArtifactInfo.getVersion(),
208             testArtifactInfo.getClassifier() } );
209     }
210 
211 
212     Object createReporterConfiguration( ReporterConfiguration reporterConfiguration )
213     {
214         Constructor constructor =
215             ReflectionUtils.getConstructor( this.reporterConfiguration, new Class[]{ File.class, Boolean.class } );
216         return ReflectionUtils.newInstance( constructor, new Object[]{ reporterConfiguration.getReportsDirectory(),
217             reporterConfiguration.isTrimStackTrace() } );
218     }
219 
220     public static ReporterFactory createForkingReporterFactoryInCurrentClassLoader( Boolean trimStackTrace,
221                                                                                     PrintStream originalSystemOut )
222     {
223         return new ForkingReporterFactory( trimStackTrace, originalSystemOut );
224     }
225 
226     public Object createBooterConfiguration( ClassLoader surefireClassLoader, Object factoryInstance,
227                                              boolean insideFork )
228     {
229         return ReflectionUtils.instantiateTwoArgs( surefireClassLoader, BaseProviderFactory.class.getName(),
230                                                    reporterFactory, factoryInstance, Boolean.class,
231                                                    insideFork ? Boolean.TRUE : Boolean.FALSE );
232     }
233 
234     public Object instantiateProvider( String providerClassName, Object booterParameters )
235     {
236         return ReflectionUtils.instantiateOneArg( surefireClassLoader, providerClassName, this.booterParameters,
237                                                   booterParameters );
238     }
239 
240     public void setIfDirScannerAware( Object o, DirectoryScannerParameters dirScannerParams )
241     {
242         if ( directoryScannerParametersAware.isAssignableFrom( o.getClass() ) )
243         {
244             setDirectoryScannerParameters( o, dirScannerParams );
245         }
246     }
247 
248     public void setDirectoryScannerParameters( Object o, DirectoryScannerParameters dirScannerParams )
249     {
250         final Object param = createDirectoryScannerParameters( dirScannerParams );
251         ReflectionUtils.invokeSetter( o, "setDirectoryScannerParameters", this.directoryScannerParameters, param );
252     }
253 
254     public void setRunOrderParameters( Object o, RunOrderParameters runOrderParameters )
255     {
256         final Object param = createRunOrderParameters( runOrderParameters );
257         ReflectionUtils.invokeSetter( o, "setRunOrderParameters", this.runOrderParameters, param );
258     }
259 
260 
261     public void setTestSuiteDefinitionAware( Object o, TestRequest testSuiteDefinition2 )
262     {
263         if ( testSuiteDefinitionAware.isAssignableFrom( o.getClass() ) )
264         {
265             setTestSuiteDefinition( o, testSuiteDefinition2 );
266         }
267     }
268 
269     void setTestSuiteDefinition( Object o, TestRequest testSuiteDefinition1 )
270     {
271         final Object param = createTestRequest( testSuiteDefinition1 );
272         ReflectionUtils.invokeSetter( o, "setTestRequest", this.testRequest, param );
273     }
274 
275     public void setProviderPropertiesAware( Object o, Properties properties )
276     {
277         if ( providerPropertiesAware.isAssignableFrom( o.getClass() ) )
278         {
279             setProviderProperties( o, properties );
280         }
281     }
282 
283     void setProviderProperties( Object o, Properties providerProperties )
284     {
285         ReflectionUtils.invokeSetter( o, "setProviderProperties", Properties.class, providerProperties );
286     }
287 
288     public void setReporterConfigurationAware( Object o, ReporterConfiguration reporterConfiguration1 )
289     {
290         if ( reporterConfigurationAware.isAssignableFrom( o.getClass() ) )
291         {
292             setReporterConfiguration( o, reporterConfiguration1 );
293         }
294     }
295 
296 
297     void setReporterConfiguration( Object o, ReporterConfiguration reporterConfiguration )
298     {
299         final Object param = createReporterConfiguration( reporterConfiguration );
300         ReflectionUtils.invokeSetter( o, "setReporterConfiguration", this.reporterConfiguration, param );
301     }
302 
303     public void setTestClassLoaderAware( Object o, ClassLoader testClassLoader )
304     {
305         if ( testClassLoaderAware.isAssignableFrom( o.getClass() ) )
306         {
307             setTestClassLoader( o, testClassLoader );
308         }
309     }
310 
311     void setTestClassLoader( Object o, ClassLoader testClassLoader )
312     {
313         final Method setter =
314             ReflectionUtils.getMethod( o, "setClassLoaders", new Class[]{ ClassLoader.class } );
315         ReflectionUtils.invokeMethodWithArray( o, setter, new Object[]{ testClassLoader } );
316     }
317 
318     public void setTestArtifactInfoAware( Object o, TestArtifactInfo testArtifactInfo1 )
319     {
320         if ( testArtifactInfoAware.isAssignableFrom( o.getClass() ) )
321         {
322             setTestArtifactInfo( o, testArtifactInfo1 );
323         }
324     }
325 
326     void setTestArtifactInfo( Object o, TestArtifactInfo testArtifactInfo )
327     {
328         final Object param = createTestArtifactInfo( testArtifactInfo );
329         ReflectionUtils.invokeSetter( o, "setTestArtifactInfo", this.testArtifactInfo, param );
330     }
331 
332     private boolean isRunResult( Object o )
333     {
334         return runResult.isAssignableFrom( o.getClass() );
335     }
336 
337 }