Browse Source

Refined check for NoClassDefFoundError in getTestExecutionListeners()

Issue: SPR-11804
pull/493/merge
Juergen Hoeller 12 years ago
parent
commit
41ed228450
  1. 68
      spring-test/src/main/java/org/springframework/test/context/support/AbstractTestContextBootstrapper.java

68
spring-test/src/main/java/org/springframework/test/context/support/AbstractTestContextBootstrapper.java

@ -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,7 +137,6 @@ 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);
} }
@ -145,14 +144,24 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
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;
} }
@ -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);
@ -283,7 +290,6 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
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;
} }
@ -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}

Loading…
Cancel
Save