From 41ed228450f82abb544858d3d22743423e281fca Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 19 May 2014 16:31:23 +0200 Subject: [PATCH] Refined check for NoClassDefFoundError in getTestExecutionListeners() Issue: SPR-11804 --- .../AbstractTestContextBootstrapper.java | 86 ++++++++++--------- 1 file changed, 46 insertions(+), 40 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/test/context/support/AbstractTestContextBootstrapper.java b/spring-test/src/main/java/org/springframework/test/context/support/AbstractTestContextBootstrapper.java index 68a302c3e42..2728e9bf51a 100644 --- a/spring-test/src/main/java/org/springframework/test/context/support/AbstractTestContextBootstrapper.java +++ b/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.LogFactory; +import org.springframework.beans.BeanInstantiationException; import org.springframework.beans.BeanUtils; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; @@ -57,9 +58,10 @@ import org.springframework.util.StringUtils; *

Concrete subclasses typically will only need to provide implementations for * the following {@code abstract} methods: *

* * @author Sam Brannen * @since 4.1 @@ -111,24 +113,22 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot // Traverse the class hierarchy... while (descriptor != null) { Class declaringClass = descriptor.getDeclaringClass(); - AnnotationAttributes annAttrs = descriptor.getAnnotationAttributes(); if (logger.isTraceEnabled()) { - logger.trace(String.format( - "Retrieved @TestExecutionListeners attributes [%s] for declaring class [%s].", annAttrs, - declaringClass)); + logger.trace(String.format("Retrieved @TestExecutionListeners attributes [%s] for declaring class [%s].", + annAttrs, declaringClass)); } - Class[] valueListenerClasses = (Class[]) annAttrs.getClassArray("value"); - Class[] listenerClasses = (Class[]) annAttrs.getClassArray("listeners"); + Class[] valueListenerClasses = + (Class[]) annAttrs.getClassArray("value"); + Class[] listenerClasses = + (Class[]) 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.", - declaringClass, ObjectUtils.nullSafeToString(valueListenerClasses), - ObjectUtils.nullSafeToString(listenerClasses)); - logger.error(msg); - throw new IllegalStateException(msg); + throw new IllegalStateException( + String.format("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))); } else if (!ObjectUtils.isEmpty(valueListenerClasses)) { listenerClasses = valueListenerClasses; @@ -137,22 +137,31 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot if (listenerClasses != null) { classesList.addAll(0, Arrays.> asList(listenerClasses)); } - descriptor = (annAttrs.getBoolean("inheritListeners") ? MetaAnnotationUtils.findAnnotationDescriptor( - descriptor.getRootDeclaringClass().getSuperclass(), annotationType) : null); + descriptor.getRootDeclaringClass().getSuperclass(), annotationType) : null); } } List listeners = new ArrayList(classesList.size()); for (Class listenerClass : classesList) { + NoClassDefFoundError ncdfe = null; try { listeners.add(BeanUtils.instantiateClass(listenerClass)); } catch (NoClassDefFoundError err) { + ncdfe = err; + } + catch (BeanInstantiationException ex) { + if (ex.getCause() instanceof NoClassDefFoundError) { + ncdfe = (NoClassDefFoundError) ex.getCause(); + } + } + if (ncdfe != null) { 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())); + "(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, ContextHierarchy.class) == null) { if (logger.isInfoEnabled()) { - logger.info(String.format( - "Neither @ContextConfiguration nor @ContextHierarchy found for test class [%s]", - testClass.getName())); + logger.info(String.format("Neither @ContextConfiguration nor @ContextHierarchy found for test class [%s]", + testClass.getName())); } return new MergedContextConfiguration(testClass, null, null, null, null); } if (AnnotationUtils.findAnnotation(testClass, ContextHierarchy.class) != null) { - Map> hierarchyMap = ContextLoaderUtils.buildContextHierarchyMap(testClass); - + Map> hierarchyMap = + ContextLoaderUtils.buildContextHierarchyMap(testClass); MergedContextConfiguration parentConfig = null; MergedContextConfiguration mergedConfig = null; @@ -220,8 +228,8 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot Assert.notEmpty(reversedList, "ContextConfigurationAttributes list must not be empty"); Class declaringClass = reversedList.get(0).getDeclaringClass(); - mergedConfig = buildMergedContextConfiguration(declaringClass, reversedList, parentConfig, - cacheAwareContextLoaderDelegate); + mergedConfig = buildMergedContextConfiguration( + declaringClass, reversedList, parentConfig, cacheAwareContextLoaderDelegate); parentConfig = mergedConfig; } @@ -230,8 +238,8 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot } else { return buildMergedContextConfiguration(testClass, - ContextLoaderUtils.resolveContextConfigurationAttributes(testClass), null, - cacheAwareContextLoaderDelegate); + ContextLoaderUtils.resolveContextConfigurationAttributes(testClass), null, + cacheAwareContextLoaderDelegate); } } @@ -257,20 +265,19 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot * @see ApplicationContextInitializerUtils#resolveInitializerClasses * @see MergedContextConfiguration */ - private MergedContextConfiguration buildMergedContextConfiguration(final Class testClass, - final List configAttributesList, MergedContextConfiguration parentConfig, + private MergedContextConfiguration buildMergedContextConfiguration(Class testClass, + List configAttributesList, MergedContextConfiguration parentConfig, CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate) { - final ContextLoader contextLoader = resolveContextLoader(testClass, configAttributesList); - final List locationsList = new ArrayList(); - final List> classesList = new ArrayList>(); + ContextLoader contextLoader = resolveContextLoader(testClass, configAttributesList); + List locationsList = new ArrayList(); + List> classesList = new ArrayList>(); for (ContextConfigurationAttributes configAttributes : configAttributesList) { if (logger.isTraceEnabled()) { logger.trace(String.format("Processing locations and classes for context configuration attributes %s", - configAttributes)); + configAttributes)); } - if (contextLoader instanceof SmartContextLoader) { SmartContextLoader smartContextLoader = (SmartContextLoader) contextLoader; smartContextLoader.processContextConfiguration(configAttributes); @@ -279,11 +286,10 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot } else { String[] processedLocations = contextLoader.processLocations(configAttributes.getDeclaringClass(), - configAttributes.getLocations()); + configAttributes.getLocations()); locationsList.addAll(0, Arrays.asList(processedLocations)); // Legacy ContextLoaders don't know how to process classes } - if (!configAttributes.isInheritLocations()) { break; } @@ -296,7 +302,7 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot String[] activeProfiles = ActiveProfilesUtils.resolveActiveProfiles(testClass); 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 activeProfiles the merged active bean definition profiles * @param contextLoader the resolved {@code ContextLoader} - * @param cacheAwareContextLoaderDelegate the cache-aware context loader delegate to - * be provided to the instantiated {@code MergedContextConfiguration} + * @param cacheAwareContextLoaderDelegate the cache-aware context loader delegate + * to be provided to the instantiated {@code MergedContextConfiguration} * @param parentConfig the merged context configuration for the parent application * context in a context hierarchy, or {@code null} if there is no parent * @return the fully initialized {@code MergedContextConfiguration}