|
|
|
@ -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} |
|
|
|
|