View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.surefire.booter;
20  
21  import java.io.PrintStream;
22  import java.lang.reflect.InvocationTargetException;
23  import java.lang.reflect.Method;
24  
25  import org.apache.maven.surefire.api.provider.SurefireProvider;
26  import org.apache.maven.surefire.api.report.ReporterException;
27  import org.apache.maven.surefire.api.suite.RunResult;
28  import org.apache.maven.surefire.api.testset.TestSetFailedException;
29  
30  import static org.apache.maven.surefire.api.util.ReflectionUtils.getMethod;
31  import static org.apache.maven.surefire.api.util.ReflectionUtils.invokeGetter;
32  import static org.apache.maven.surefire.api.util.ReflectionUtils.invokeMethodWithArray;
33  import static org.apache.maven.surefire.api.util.ReflectionUtils.invokeMethodWithArray2;
34  import static org.apache.maven.surefire.api.util.internal.ObjectUtils.isSecurityManagerSupported;
35  
36  /**
37   * Creates the surefire provider.
38   * <br>
39   *
40   * @author Kristian Rosenvold
41   */
42  public class ProviderFactory {
43      private final StartupConfiguration startupConfiguration;
44  
45      private final ProviderConfiguration providerConfiguration;
46  
47      private final ClassLoader classLoader;
48  
49      private final SurefireReflector surefireReflector;
50  
51      private final Object reporterManagerFactory;
52  
53      private static final Class<?>[] INVOKE_PARAMETERS = {Object.class};
54  
55      private static final Class<?>[] INVOKE_EMPTY_PARAMETER_TYPES = {};
56  
57      private static final Object[] INVOKE_EMPTY_PARAMETERS = {};
58  
59      public ProviderFactory(
60              StartupConfiguration startupConfiguration,
61              ProviderConfiguration providerConfiguration,
62              ClassLoader testsClassLoader,
63              Object reporterManagerFactory) {
64          this.providerConfiguration = providerConfiguration;
65          this.startupConfiguration = startupConfiguration;
66          this.surefireReflector = new SurefireReflector(testsClassLoader);
67          this.classLoader = testsClassLoader;
68          this.reporterManagerFactory = reporterManagerFactory;
69      }
70  
71      public static RunResult invokeProvider(
72              Object testSet,
73              ClassLoader testsClassLoader,
74              Object factory,
75              ProviderConfiguration providerConfiguration,
76              boolean insideFork,
77              StartupConfiguration startupConfig,
78              boolean restoreStreams)
79              throws TestSetFailedException, InvocationTargetException {
80          final PrintStream orgSystemOut = System.out;
81          final PrintStream orgSystemErr = System.err;
82          // Note that System.out/System.err are also read in the "ReporterConfiguration" instantiation
83          // in createProvider below. These are the same values as here.
84  
85          try {
86              return new ProviderFactory(startupConfig, providerConfiguration, testsClassLoader, factory)
87                      .createProvider(insideFork)
88                      .invoke(testSet);
89          } finally {
90              if (restoreStreams && (!isSecurityManagerSupported() || System.getSecurityManager() == null)) {
91                  System.setOut(orgSystemOut);
92                  System.setErr(orgSystemErr);
93              }
94          }
95      }
96  
97      public SurefireProvider createProvider(boolean isInsideFork) {
98          final Thread currentThread = Thread.currentThread();
99          final ClassLoader systemClassLoader = currentThread.getContextClassLoader();
100         currentThread.setContextClassLoader(classLoader);
101         // Note: Duplicated in ForkedBooter#createProviderInCurrentClassloader
102         Object o = surefireReflector.createBooterConfiguration(classLoader, isInsideFork);
103         surefireReflector.setReporterFactoryAware(o, reporterManagerFactory);
104         surefireReflector.setTestSuiteDefinitionAware(o, providerConfiguration.getTestSuiteDefinition());
105         surefireReflector.setProviderPropertiesAware(o, providerConfiguration.getProviderProperties());
106         surefireReflector.setReporterConfigurationAware(o, providerConfiguration.getReporterConfiguration());
107         surefireReflector.setTestClassLoaderAware(o, classLoader);
108         surefireReflector.setTestArtifactInfoAware(o, providerConfiguration.getTestArtifact());
109         surefireReflector.setRunOrderParameters(o, providerConfiguration.getRunOrderParameters());
110         surefireReflector.setIfDirScannerAware(o, providerConfiguration.getDirScannerParams());
111         surefireReflector.setMainCliOptions(o, providerConfiguration.getMainCliOptions());
112         surefireReflector.setSkipAfterFailureCount(o, providerConfiguration.getSkipAfterFailureCount());
113         if (isInsideFork) {
114             surefireReflector.setSystemExitTimeout(o, providerConfiguration.getSystemExitTimeout());
115         }
116 
117         Object provider = surefireReflector.instantiateProvider(startupConfiguration.getActualClassName(), o);
118         currentThread.setContextClassLoader(systemClassLoader);
119 
120         return new ProviderProxy(provider, classLoader);
121     }
122 
123     private final class ProviderProxy implements SurefireProvider {
124         private final Object providerInOtherClassLoader;
125 
126         private final ClassLoader testsClassLoader;
127 
128         private ProviderProxy(Object providerInOtherClassLoader, ClassLoader testsClassLoader) {
129             this.providerInOtherClassLoader = providerInOtherClassLoader;
130             this.testsClassLoader = testsClassLoader;
131         }
132 
133         @Override
134         public Iterable<Class<?>> getSuites() {
135             ClassLoader current = swapClassLoader(testsClassLoader);
136             try {
137                 return invokeGetter(providerInOtherClassLoader, "getSuites");
138             } finally {
139                 Thread.currentThread().setContextClassLoader(current);
140             }
141         }
142 
143         @Override
144         public RunResult invoke(Object forkTestSet) throws ReporterException, InvocationTargetException {
145             ClassLoader current = swapClassLoader(testsClassLoader);
146             try {
147                 Method invoke = getMethod(providerInOtherClassLoader.getClass(), "invoke", INVOKE_PARAMETERS);
148                 Object result = invokeMethodWithArray2(providerInOtherClassLoader, invoke, forkTestSet);
149                 return (RunResult) surefireReflector.convertIfRunResult(result);
150             } finally {
151                 if (System.getSecurityManager() == null) {
152                     Thread.currentThread().setContextClassLoader(current);
153                 }
154             }
155         }
156 
157         private ClassLoader swapClassLoader(ClassLoader newClassLoader) {
158             ClassLoader current = Thread.currentThread().getContextClassLoader();
159             Thread.currentThread().setContextClassLoader(newClassLoader);
160             return current;
161         }
162 
163         @Override
164         public void cancel() {
165             Class<?> providerType = providerInOtherClassLoader.getClass();
166             Method invoke = getMethod(providerType, "cancel", INVOKE_EMPTY_PARAMETER_TYPES);
167             invokeMethodWithArray(providerInOtherClassLoader, invoke, INVOKE_EMPTY_PARAMETERS);
168         }
169     }
170 }