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