General defensiveness about the bootstrap ClassLoader (i.e. null ClassLoader)
Issue: SPR-11721
(cherry picked from commit 59cef3c)
This commit is contained in:
@@ -23,6 +23,7 @@ import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -96,8 +97,7 @@ public class TestContextManager {
|
||||
* @see #registerTestExecutionListeners(List)
|
||||
*/
|
||||
public TestContextManager(Class<?> testClass) {
|
||||
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate = new DefaultCacheAwareContextLoaderDelegate(
|
||||
contextCache);
|
||||
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate = new DefaultCacheAwareContextLoaderDelegate(contextCache);
|
||||
BootstrapContext bootstrapContext = new DefaultBootstrapContext(testClass, cacheAwareContextLoaderDelegate);
|
||||
this.testContextBootstrapper = BootstrapUtils.resolveTestContextBootstrapper(bootstrapContext);
|
||||
this.testContext = new DefaultTestContext(testContextBootstrapper);
|
||||
@@ -148,8 +148,7 @@ public class TestContextManager {
|
||||
* registered for this {@code TestContextManager} in reverse order.
|
||||
*/
|
||||
private List<TestExecutionListener> getReversedTestExecutionListeners() {
|
||||
List<TestExecutionListener> listenersReversed = new ArrayList<TestExecutionListener>(
|
||||
getTestExecutionListeners());
|
||||
List<TestExecutionListener> listenersReversed = new ArrayList<TestExecutionListener>(getTestExecutionListeners());
|
||||
Collections.reverse(listenersReversed);
|
||||
return listenersReversed;
|
||||
}
|
||||
@@ -168,7 +167,7 @@ public class TestContextManager {
|
||||
* @see #getTestExecutionListeners()
|
||||
*/
|
||||
public void beforeTestClass() throws Exception {
|
||||
final Class<?> testClass = getTestContext().getTestClass();
|
||||
Class<?> testClass = getTestContext().getTestClass();
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("beforeTestClass(): class [" + testClass + "]");
|
||||
}
|
||||
@@ -179,8 +178,8 @@ public class TestContextManager {
|
||||
testExecutionListener.beforeTestClass(getTestContext());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.warn("Caught exception while allowing TestExecutionListener [" + testExecutionListener
|
||||
+ "] to process 'before class' callback for test class [" + testClass + "]", ex);
|
||||
logger.warn("Caught exception while allowing TestExecutionListener [" + testExecutionListener +
|
||||
"] to process 'before class' callback for test class [" + testClass + "]", ex);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
@@ -212,8 +211,8 @@ public class TestContextManager {
|
||||
testExecutionListener.prepareTestInstance(getTestContext());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.error("Caught exception while allowing TestExecutionListener [" + testExecutionListener
|
||||
+ "] to prepare test instance [" + testInstance + "]", ex);
|
||||
logger.error("Caught exception while allowing TestExecutionListener [" + testExecutionListener +
|
||||
"] to prepare test instance [" + testInstance + "]", ex);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
@@ -249,9 +248,9 @@ public class TestContextManager {
|
||||
testExecutionListener.beforeTestMethod(getTestContext());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.warn("Caught exception while allowing TestExecutionListener [" + testExecutionListener
|
||||
+ "] to process 'before' execution of test method [" + testMethod + "] for test instance ["
|
||||
+ testInstance + "]", ex);
|
||||
logger.warn("Caught exception while allowing TestExecutionListener [" + testExecutionListener +
|
||||
"] to process 'before' execution of test method [" + testMethod + "] for test instance [" +
|
||||
testInstance + "]", ex);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
@@ -297,9 +296,9 @@ public class TestContextManager {
|
||||
testExecutionListener.afterTestMethod(getTestContext());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.warn("Caught exception while allowing TestExecutionListener [" + testExecutionListener
|
||||
+ "] to process 'after' execution for test: method [" + testMethod + "], instance ["
|
||||
+ testInstance + "], exception [" + exception + "]", ex);
|
||||
logger.warn("Caught exception while allowing TestExecutionListener [" + testExecutionListener +
|
||||
"] to process 'after' execution for test: method [" + testMethod + "], instance [" +
|
||||
testInstance + "], exception [" + exception + "]", ex);
|
||||
if (afterTestMethodException == null) {
|
||||
afterTestMethodException = ex;
|
||||
}
|
||||
@@ -325,7 +324,7 @@ public class TestContextManager {
|
||||
* @see #getTestExecutionListeners()
|
||||
*/
|
||||
public void afterTestClass() throws Exception {
|
||||
final Class<?> testClass = getTestContext().getTestClass();
|
||||
Class<?> testClass = getTestContext().getTestClass();
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("afterTestClass(): class [" + testClass + "]");
|
||||
}
|
||||
@@ -339,8 +338,8 @@ public class TestContextManager {
|
||||
testExecutionListener.afterTestClass(getTestContext());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.warn("Caught exception while allowing TestExecutionListener [" + testExecutionListener
|
||||
+ "] to process 'after class' callback for test class [" + testClass + "]", ex);
|
||||
logger.warn("Caught exception while allowing TestExecutionListener [" + testExecutionListener +
|
||||
"] to process 'after class' callback for test class [" + testClass + "]", ex);
|
||||
if (afterTestClassException == null) {
|
||||
afterTestClassException = ex;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
@@ -122,8 +123,8 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
||||
Class<? extends TestExecutionListener>[] listenerClasses = (Class<? extends TestExecutionListener>[]) annAttrs.getClassArray("listeners");
|
||||
if (!ObjectUtils.isEmpty(valueListenerClasses) && !ObjectUtils.isEmpty(listenerClasses)) {
|
||||
String msg = String.format(
|
||||
"Class [%s] has been configured with @TestExecutionListeners' 'value' [%s] "
|
||||
+ "and 'listeners' [%s] attributes. Use one or the other, but not both.",
|
||||
"Class [%s] has been configured with @TestExecutionListeners' 'value' [%s] " +
|
||||
"and 'listeners' [%s] attributes. Use one or the other, but not both.",
|
||||
declaringClass, ObjectUtils.nullSafeToString(valueListenerClasses),
|
||||
ObjectUtils.nullSafeToString(listenerClasses));
|
||||
logger.error(msg);
|
||||
@@ -149,9 +150,9 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
||||
}
|
||||
catch (NoClassDefFoundError err) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info(String.format("Could not instantiate TestExecutionListener [%s]. "
|
||||
+ "Specify custom listener classes or make the default listener classes "
|
||||
+ "(and their dependencies) available.", listenerClass.getName()));
|
||||
logger.info(String.format("Could not instantiate TestExecutionListener [%s]. " +
|
||||
"Specify custom listener classes or make the default listener classes " +
|
||||
"(and their dependencies) available.", listenerClass.getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -169,15 +170,15 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Set<Class<? extends TestExecutionListener>> getDefaultTestExecutionListenerClasses() {
|
||||
Set<Class<? extends TestExecutionListener>> defaultListenerClasses = new LinkedHashSet<Class<? extends TestExecutionListener>>();
|
||||
ClassLoader cl = getClass().getClassLoader();
|
||||
for (String className : getDefaultTestExecutionListenerClassNames()) {
|
||||
try {
|
||||
defaultListenerClasses.add((Class<? extends TestExecutionListener>) getClass().getClassLoader().loadClass(
|
||||
className));
|
||||
defaultListenerClasses.add((Class<? extends TestExecutionListener>) ClassUtils.forName(className, cl));
|
||||
}
|
||||
catch (Throwable t) {
|
||||
catch (Throwable ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Could not load default TestExecutionListener class [" + className
|
||||
+ "]. Specify custom listener classes or make the default listener classes available.", t);
|
||||
logger.debug("Could not load default TestExecutionListener class [" + className +
|
||||
"]. Specify custom listener classes or make the default listener classes available.", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -187,7 +188,7 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public final MergedContextConfiguration buildMergedContextConfiguration() {
|
||||
Class<?> testClass = getBootstrapContext().getTestClass();
|
||||
@@ -238,7 +239,6 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
||||
* Build the {@link MergedContextConfiguration merged context configuration}
|
||||
* for the supplied {@link Class testClass}, context configuration attributes,
|
||||
* and parent context configuration.
|
||||
*
|
||||
* @param testClass the test class for which the {@code MergedContextConfiguration}
|
||||
* should be built (must not be {@code null})
|
||||
* @param configAttributesList the list of context configuration attributes for the
|
||||
@@ -303,13 +303,11 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
||||
* Resolve the {@link ContextLoader} {@linkplain Class class} to use for the
|
||||
* supplied list of {@link ContextConfigurationAttributes} and then instantiate
|
||||
* and return that {@code ContextLoader}.
|
||||
*
|
||||
* <p>If the user has not explicitly declared which loader to use, the value
|
||||
* returned from {@link #getDefaultContextLoaderClass} will be used as the
|
||||
* default context loader class. For details on the class resolution process,
|
||||
* see {@link #resolveExplicitContextLoaderClass} and
|
||||
* {@link #getDefaultContextLoaderClass}.
|
||||
*
|
||||
* @param testClass the test class for which the {@code ContextLoader} should be
|
||||
* resolved; must not be {@code null}
|
||||
* @param configAttributesList the list of configuration attributes to process; must
|
||||
@@ -320,6 +318,7 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
||||
*/
|
||||
private ContextLoader resolveContextLoader(Class<?> testClass,
|
||||
List<ContextConfigurationAttributes> configAttributesList) {
|
||||
|
||||
Assert.notNull(testClass, "Class must not be null");
|
||||
Assert.notEmpty(configAttributesList, "ContextConfigurationAttributes list must not be empty");
|
||||
|
||||
@@ -327,21 +326,17 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
||||
if (contextLoaderClass == null) {
|
||||
contextLoaderClass = getDefaultContextLoaderClass(testClass);
|
||||
}
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(String.format("Using ContextLoader class [%s] for test class [%s]",
|
||||
contextLoaderClass.getName(), testClass.getName()));
|
||||
}
|
||||
|
||||
return BeanUtils.instantiateClass(contextLoaderClass, ContextLoader.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the {@link ContextLoader} {@linkplain Class class} to use for the supplied
|
||||
* list of {@link ContextConfigurationAttributes}.
|
||||
*
|
||||
* <p>Beginning with the first level in the context configuration attributes hierarchy:
|
||||
*
|
||||
* <ol>
|
||||
* <li>If the {@link ContextConfigurationAttributes#getContextLoaderClass()
|
||||
* contextLoaderClass} property of {@link ContextConfigurationAttributes} is
|
||||
@@ -350,7 +345,6 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
||||
* level in the hierarchy, traverse to the next level in the hierarchy and return to
|
||||
* step #1.</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param configAttributesList the list of configuration attributes to process;
|
||||
* must not be {@code null} or <em>empty</em>; must be ordered <em>bottom-up</em>
|
||||
* (i.e., as if we were traversing up the class hierarchy)
|
||||
@@ -361,14 +355,13 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
||||
*/
|
||||
private Class<? extends ContextLoader> resolveExplicitContextLoaderClass(
|
||||
List<ContextConfigurationAttributes> configAttributesList) {
|
||||
Assert.notEmpty(configAttributesList, "ContextConfigurationAttributes list must not be empty");
|
||||
|
||||
Assert.notEmpty(configAttributesList, "ContextConfigurationAttributes list must not be empty");
|
||||
for (ContextConfigurationAttributes configAttributes : configAttributesList) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(String.format("Resolving ContextLoader for context configuration attributes %s",
|
||||
configAttributes));
|
||||
}
|
||||
|
||||
Class<? extends ContextLoader> contextLoaderClass = configAttributes.getContextLoaderClass();
|
||||
if (!ContextLoader.class.equals(contextLoaderClass)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
@@ -379,7 +372,6 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
||||
return contextLoaderClass;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -405,12 +397,10 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
||||
/**
|
||||
* Build a {@link MergedContextConfiguration} instance from the supplied,
|
||||
* merged values.
|
||||
*
|
||||
* <p>Concrete subclasses typically will only need to instantiate
|
||||
* {@link MergedContextConfiguration} (or a specialized subclass thereof)
|
||||
* from the provided values; further processing and merging of values is likely
|
||||
* unnecessary.
|
||||
*
|
||||
* @param testClass the test class for which the {@code MergedContextConfiguration}
|
||||
* should be built (must not be {@code null})
|
||||
* @param locations the merged resource locations
|
||||
@@ -425,9 +415,7 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
||||
* @return the fully initialized {@code MergedContextConfiguration}
|
||||
*/
|
||||
protected abstract MergedContextConfiguration buildMergedContextConfiguration(
|
||||
Class<?> testClass,
|
||||
String[] locations,
|
||||
Class<?>[] classes,
|
||||
Class<?> testClass, String[] locations, Class<?>[] classes,
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses,
|
||||
String[] activeProfiles, ContextLoader contextLoader,
|
||||
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate, MergedContextConfiguration parentConfig);
|
||||
|
||||
Reference in New Issue
Block a user