Refined check for NoClassDefFoundError in getTestExecutionListeners()
Issue: SPR-11804
This commit is contained in:
@@ -27,6 +27,7 @@ import java.util.Set;
|
|||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
import org.springframework.beans.BeanInstantiationException;
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.context.ApplicationContextInitializer;
|
import org.springframework.context.ApplicationContextInitializer;
|
||||||
import org.springframework.context.ConfigurableApplicationContext;
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
@@ -57,9 +58,10 @@ import org.springframework.util.StringUtils;
|
|||||||
* <p>Concrete subclasses typically will only need to provide implementations for
|
* <p>Concrete subclasses typically will only need to provide implementations for
|
||||||
* the following {@code abstract} methods:
|
* the following {@code abstract} methods:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>{@link #getDefaultTestExecutionListenerClassNames()}
|
* <li>{@link #getDefaultTestExecutionListenerClassNames}
|
||||||
* <li>{@link #getDefaultContextLoaderClass(Class)}
|
* <li>{@link #getDefaultContextLoaderClass}
|
||||||
* <li>{@link #buildMergedContextConfiguration(Class, String[], Class[], Set, String[], ContextLoader, CacheAwareContextLoaderDelegate, MergedContextConfiguration)}
|
* <li>{@link #buildMergedContextConfiguration}
|
||||||
|
* </ul>
|
||||||
*
|
*
|
||||||
* @author Sam Brannen
|
* @author Sam Brannen
|
||||||
* @since 4.1
|
* @since 4.1
|
||||||
@@ -111,24 +113,22 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
|||||||
// Traverse the class hierarchy...
|
// Traverse the class hierarchy...
|
||||||
while (descriptor != null) {
|
while (descriptor != null) {
|
||||||
Class<?> declaringClass = descriptor.getDeclaringClass();
|
Class<?> declaringClass = descriptor.getDeclaringClass();
|
||||||
|
|
||||||
AnnotationAttributes annAttrs = descriptor.getAnnotationAttributes();
|
AnnotationAttributes annAttrs = descriptor.getAnnotationAttributes();
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace(String.format(
|
logger.trace(String.format("Retrieved @TestExecutionListeners attributes [%s] for declaring class [%s].",
|
||||||
"Retrieved @TestExecutionListeners attributes [%s] for declaring class [%s].", annAttrs,
|
annAttrs, declaringClass));
|
||||||
declaringClass));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Class<? extends TestExecutionListener>[] valueListenerClasses = (Class<? extends TestExecutionListener>[]) annAttrs.getClassArray("value");
|
Class<? extends TestExecutionListener>[] valueListenerClasses =
|
||||||
Class<? extends TestExecutionListener>[] listenerClasses = (Class<? extends TestExecutionListener>[]) annAttrs.getClassArray("listeners");
|
(Class<? extends TestExecutionListener>[]) annAttrs.getClassArray("value");
|
||||||
|
Class<? extends TestExecutionListener>[] listenerClasses =
|
||||||
|
(Class<? extends TestExecutionListener>[]) annAttrs.getClassArray("listeners");
|
||||||
if (!ObjectUtils.isEmpty(valueListenerClasses) && !ObjectUtils.isEmpty(listenerClasses)) {
|
if (!ObjectUtils.isEmpty(valueListenerClasses) && !ObjectUtils.isEmpty(listenerClasses)) {
|
||||||
String msg = String.format(
|
throw new IllegalStateException(
|
||||||
"Class [%s] has been configured with @TestExecutionListeners' 'value' [%s] " +
|
String.format("Class [%s] has been configured with @TestExecutionListeners' 'value' [%s]" +
|
||||||
"and 'listeners' [%s] attributes. Use one or the other, but not both.",
|
" and 'listeners' [%s] attributes. Use one or the other, but not both.",
|
||||||
declaringClass, ObjectUtils.nullSafeToString(valueListenerClasses),
|
declaringClass, ObjectUtils.nullSafeToString(valueListenerClasses),
|
||||||
ObjectUtils.nullSafeToString(listenerClasses));
|
ObjectUtils.nullSafeToString(listenerClasses)));
|
||||||
logger.error(msg);
|
|
||||||
throw new IllegalStateException(msg);
|
|
||||||
}
|
}
|
||||||
else if (!ObjectUtils.isEmpty(valueListenerClasses)) {
|
else if (!ObjectUtils.isEmpty(valueListenerClasses)) {
|
||||||
listenerClasses = valueListenerClasses;
|
listenerClasses = valueListenerClasses;
|
||||||
@@ -137,22 +137,31 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
|||||||
if (listenerClasses != null) {
|
if (listenerClasses != null) {
|
||||||
classesList.addAll(0, Arrays.<Class<? extends TestExecutionListener>> asList(listenerClasses));
|
classesList.addAll(0, Arrays.<Class<? extends TestExecutionListener>> asList(listenerClasses));
|
||||||
}
|
}
|
||||||
|
|
||||||
descriptor = (annAttrs.getBoolean("inheritListeners") ? MetaAnnotationUtils.findAnnotationDescriptor(
|
descriptor = (annAttrs.getBoolean("inheritListeners") ? MetaAnnotationUtils.findAnnotationDescriptor(
|
||||||
descriptor.getRootDeclaringClass().getSuperclass(), annotationType) : null);
|
descriptor.getRootDeclaringClass().getSuperclass(), annotationType) : null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<TestExecutionListener> listeners = new ArrayList<TestExecutionListener>(classesList.size());
|
List<TestExecutionListener> listeners = new ArrayList<TestExecutionListener>(classesList.size());
|
||||||
for (Class<? extends TestExecutionListener> listenerClass : classesList) {
|
for (Class<? extends TestExecutionListener> listenerClass : classesList) {
|
||||||
|
NoClassDefFoundError ncdfe = null;
|
||||||
try {
|
try {
|
||||||
listeners.add(BeanUtils.instantiateClass(listenerClass));
|
listeners.add(BeanUtils.instantiateClass(listenerClass));
|
||||||
}
|
}
|
||||||
catch (NoClassDefFoundError err) {
|
catch (NoClassDefFoundError err) {
|
||||||
|
ncdfe = err;
|
||||||
|
}
|
||||||
|
catch (BeanInstantiationException ex) {
|
||||||
|
if (ex.getCause() instanceof NoClassDefFoundError) {
|
||||||
|
ncdfe = (NoClassDefFoundError) ex.getCause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ncdfe != null) {
|
||||||
if (logger.isInfoEnabled()) {
|
if (logger.isInfoEnabled()) {
|
||||||
logger.info(String.format("Could not instantiate TestExecutionListener [%s]. " +
|
logger.info(String.format("Could not instantiate TestExecutionListener [%s]. " +
|
||||||
"Specify custom listener classes or make the default listener classes " +
|
"Specify custom listener classes or make the default listener classes " +
|
||||||
"(and their dependencies) available.", listenerClass.getName()));
|
"(and their dependencies) available. Offending class: [%s]",
|
||||||
|
listenerClass.getName(), ncdfe.getMessage()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -197,16 +206,15 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
|||||||
if (MetaAnnotationUtils.findAnnotationDescriptorForTypes(testClass, ContextConfiguration.class,
|
if (MetaAnnotationUtils.findAnnotationDescriptorForTypes(testClass, ContextConfiguration.class,
|
||||||
ContextHierarchy.class) == null) {
|
ContextHierarchy.class) == null) {
|
||||||
if (logger.isInfoEnabled()) {
|
if (logger.isInfoEnabled()) {
|
||||||
logger.info(String.format(
|
logger.info(String.format("Neither @ContextConfiguration nor @ContextHierarchy found for test class [%s]",
|
||||||
"Neither @ContextConfiguration nor @ContextHierarchy found for test class [%s]",
|
testClass.getName()));
|
||||||
testClass.getName()));
|
|
||||||
}
|
}
|
||||||
return new MergedContextConfiguration(testClass, null, null, null, null);
|
return new MergedContextConfiguration(testClass, null, null, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AnnotationUtils.findAnnotation(testClass, ContextHierarchy.class) != null) {
|
if (AnnotationUtils.findAnnotation(testClass, ContextHierarchy.class) != null) {
|
||||||
Map<String, List<ContextConfigurationAttributes>> hierarchyMap = ContextLoaderUtils.buildContextHierarchyMap(testClass);
|
Map<String, List<ContextConfigurationAttributes>> hierarchyMap =
|
||||||
|
ContextLoaderUtils.buildContextHierarchyMap(testClass);
|
||||||
MergedContextConfiguration parentConfig = null;
|
MergedContextConfiguration parentConfig = null;
|
||||||
MergedContextConfiguration mergedConfig = null;
|
MergedContextConfiguration mergedConfig = null;
|
||||||
|
|
||||||
@@ -220,8 +228,8 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
|||||||
Assert.notEmpty(reversedList, "ContextConfigurationAttributes list must not be empty");
|
Assert.notEmpty(reversedList, "ContextConfigurationAttributes list must not be empty");
|
||||||
Class<?> declaringClass = reversedList.get(0).getDeclaringClass();
|
Class<?> declaringClass = reversedList.get(0).getDeclaringClass();
|
||||||
|
|
||||||
mergedConfig = buildMergedContextConfiguration(declaringClass, reversedList, parentConfig,
|
mergedConfig = buildMergedContextConfiguration(
|
||||||
cacheAwareContextLoaderDelegate);
|
declaringClass, reversedList, parentConfig, cacheAwareContextLoaderDelegate);
|
||||||
parentConfig = mergedConfig;
|
parentConfig = mergedConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,8 +238,8 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return buildMergedContextConfiguration(testClass,
|
return buildMergedContextConfiguration(testClass,
|
||||||
ContextLoaderUtils.resolveContextConfigurationAttributes(testClass), null,
|
ContextLoaderUtils.resolveContextConfigurationAttributes(testClass), null,
|
||||||
cacheAwareContextLoaderDelegate);
|
cacheAwareContextLoaderDelegate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,20 +265,19 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
|||||||
* @see ApplicationContextInitializerUtils#resolveInitializerClasses
|
* @see ApplicationContextInitializerUtils#resolveInitializerClasses
|
||||||
* @see MergedContextConfiguration
|
* @see MergedContextConfiguration
|
||||||
*/
|
*/
|
||||||
private MergedContextConfiguration buildMergedContextConfiguration(final Class<?> testClass,
|
private MergedContextConfiguration buildMergedContextConfiguration(Class<?> testClass,
|
||||||
final List<ContextConfigurationAttributes> configAttributesList, MergedContextConfiguration parentConfig,
|
List<ContextConfigurationAttributes> configAttributesList, MergedContextConfiguration parentConfig,
|
||||||
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate) {
|
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate) {
|
||||||
|
|
||||||
final ContextLoader contextLoader = resolveContextLoader(testClass, configAttributesList);
|
ContextLoader contextLoader = resolveContextLoader(testClass, configAttributesList);
|
||||||
final List<String> locationsList = new ArrayList<String>();
|
List<String> locationsList = new ArrayList<String>();
|
||||||
final List<Class<?>> classesList = new ArrayList<Class<?>>();
|
List<Class<?>> classesList = new ArrayList<Class<?>>();
|
||||||
|
|
||||||
for (ContextConfigurationAttributes configAttributes : configAttributesList) {
|
for (ContextConfigurationAttributes configAttributes : configAttributesList) {
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace(String.format("Processing locations and classes for context configuration attributes %s",
|
logger.trace(String.format("Processing locations and classes for context configuration attributes %s",
|
||||||
configAttributes));
|
configAttributes));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (contextLoader instanceof SmartContextLoader) {
|
if (contextLoader instanceof SmartContextLoader) {
|
||||||
SmartContextLoader smartContextLoader = (SmartContextLoader) contextLoader;
|
SmartContextLoader smartContextLoader = (SmartContextLoader) contextLoader;
|
||||||
smartContextLoader.processContextConfiguration(configAttributes);
|
smartContextLoader.processContextConfiguration(configAttributes);
|
||||||
@@ -279,11 +286,10 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
String[] processedLocations = contextLoader.processLocations(configAttributes.getDeclaringClass(),
|
String[] processedLocations = contextLoader.processLocations(configAttributes.getDeclaringClass(),
|
||||||
configAttributes.getLocations());
|
configAttributes.getLocations());
|
||||||
locationsList.addAll(0, Arrays.asList(processedLocations));
|
locationsList.addAll(0, Arrays.asList(processedLocations));
|
||||||
// Legacy ContextLoaders don't know how to process classes
|
// Legacy ContextLoaders don't know how to process classes
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!configAttributes.isInheritLocations()) {
|
if (!configAttributes.isInheritLocations()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -296,7 +302,7 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
|||||||
String[] activeProfiles = ActiveProfilesUtils.resolveActiveProfiles(testClass);
|
String[] activeProfiles = ActiveProfilesUtils.resolveActiveProfiles(testClass);
|
||||||
|
|
||||||
return buildMergedContextConfiguration(testClass, locations, classes, initializerClasses, activeProfiles,
|
return buildMergedContextConfiguration(testClass, locations, classes, initializerClasses, activeProfiles,
|
||||||
contextLoader, cacheAwareContextLoaderDelegate, parentConfig);
|
contextLoader, cacheAwareContextLoaderDelegate, parentConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -408,8 +414,8 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
|||||||
* @param initializerClasses the merged context initializer classes
|
* @param initializerClasses the merged context initializer classes
|
||||||
* @param activeProfiles the merged active bean definition profiles
|
* @param activeProfiles the merged active bean definition profiles
|
||||||
* @param contextLoader the resolved {@code ContextLoader}
|
* @param contextLoader the resolved {@code ContextLoader}
|
||||||
* @param cacheAwareContextLoaderDelegate the cache-aware context loader delegate to
|
* @param cacheAwareContextLoaderDelegate the cache-aware context loader delegate
|
||||||
* be provided to the instantiated {@code MergedContextConfiguration}
|
* to be provided to the instantiated {@code MergedContextConfiguration}
|
||||||
* @param parentConfig the merged context configuration for the parent application
|
* @param parentConfig the merged context configuration for the parent application
|
||||||
* context in a context hierarchy, or {@code null} if there is no parent
|
* context in a context hierarchy, or {@code null} if there is no parent
|
||||||
* @return the fully initialized {@code MergedContextConfiguration}
|
* @return the fully initialized {@code MergedContextConfiguration}
|
||||||
|
|||||||
Reference in New Issue
Block a user