@ -1,5 +1,5 @@
/ *
/ *
* Copyright 2002 - 2015 the original author or authors .
* Copyright 2002 - 2016 the original author or authors .
*
*
* Licensed under the Apache License , Version 2 . 0 ( the "License" ) ;
* Licensed under the Apache License , Version 2 . 0 ( the "License" ) ;
* you may not use this file except in compliance with the License .
* you may not use this file except in compliance with the License .
@ -18,6 +18,7 @@ package org.springframework.test.context.support;
import java.util.ArrayList ;
import java.util.ArrayList ;
import java.util.Arrays ;
import java.util.Arrays ;
import java.util.Collection ;
import java.util.Collections ;
import java.util.Collections ;
import java.util.HashSet ;
import java.util.HashSet ;
import java.util.LinkedHashSet ;
import java.util.LinkedHashSet ;
@ -30,8 +31,6 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanInstantiationException ;
import org.springframework.beans.BeanInstantiationException ;
import org.springframework.beans.BeanUtils ;
import org.springframework.beans.BeanUtils ;
import org.springframework.context.ApplicationContextInitializer ;
import org.springframework.context.ConfigurableApplicationContext ;
import org.springframework.core.annotation.AnnotationAwareOrderComparator ;
import org.springframework.core.annotation.AnnotationAwareOrderComparator ;
import org.springframework.core.annotation.AnnotationUtils ;
import org.springframework.core.annotation.AnnotationUtils ;
import org.springframework.core.io.support.SpringFactoriesLoader ;
import org.springframework.core.io.support.SpringFactoriesLoader ;
@ -71,6 +70,7 @@ import org.springframework.util.StringUtils;
*
*
* @author Sam Brannen
* @author Sam Brannen
* @author Juergen Hoeller
* @author Juergen Hoeller
* @author Phillip Webb
* @since 4 . 1
* @since 4 . 1
* /
* /
public abstract class AbstractTestContextBootstrapper implements TestContextBootstrapper {
public abstract class AbstractTestContextBootstrapper implements TestContextBootstrapper {
@ -272,13 +272,8 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate = getCacheAwareContextLoaderDelegate ( ) ;
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate = getCacheAwareContextLoaderDelegate ( ) ;
if ( MetaAnnotationUtils . findAnnotationDescriptorForTypes ( testClass , ContextConfiguration . class ,
if ( MetaAnnotationUtils . findAnnotationDescriptorForTypes ( testClass , ContextConfiguration . class ,
ContextHierarchy . class ) = = null ) {
ContextHierarchy . class ) = = null ) {
if ( logger . isInfoEnabled ( ) ) {
return buildDefaultMergedContextConfiguration ( testClass , cacheAwareContextLoaderDelegate ) ;
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 ) {
if ( AnnotationUtils . findAnnotation ( testClass , ContextHierarchy . class ) ! = null ) {
@ -297,7 +292,7 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
Class < ? > declaringClass = reversedList . get ( 0 ) . getDeclaringClass ( ) ;
Class < ? > declaringClass = reversedList . get ( 0 ) . getDeclaringClass ( ) ;
mergedConfig = buildMergedContextConfiguration ( declaringClass , reversedList , parentConfig ,
mergedConfig = buildMergedContextConfiguration ( declaringClass , reversedList , parentConfig ,
cacheAwareContextLoaderDelegate ) ;
cacheAwareContextLoaderDelegate , true ) ;
parentConfig = mergedConfig ;
parentConfig = mergedConfig ;
}
}
@ -307,10 +302,29 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
else {
else {
return buildMergedContextConfiguration ( testClass ,
return buildMergedContextConfiguration ( testClass ,
ContextLoaderUtils . resolveContextConfigurationAttributes ( testClass ) , null ,
ContextLoaderUtils . resolveContextConfigurationAttributes ( testClass ) , null ,
cacheAwareContextLoaderDelegate ) ;
cacheAwareContextLoaderDelegate , true ) ;
}
}
}
}
/ * *
* @since 4 . 3
* /
private MergedContextConfiguration buildDefaultMergedContextConfiguration ( Class < ? > testClass ,
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate ) {
List < ContextConfigurationAttributes > defaultConfigAttributesList
= Collections . singletonList ( new ContextConfigurationAttributes ( testClass ) ) ;
ContextLoader contextLoader = resolveContextLoader ( testClass , defaultConfigAttributesList ) ;
if ( logger . isInfoEnabled ( ) ) {
logger . info ( String . format (
"Neither @ContextConfiguration nor @ContextHierarchy found for test class [%s], using %s" ,
testClass . getName ( ) , contextLoader . getClass ( ) . getSimpleName ( ) ) ) ;
}
return buildMergedContextConfiguration ( testClass , defaultConfigAttributesList , null ,
cacheAwareContextLoaderDelegate , false ) ;
}
/ * *
/ * *
* Build the { @link MergedContextConfiguration merged context configuration }
* Build the { @link MergedContextConfiguration merged context configuration }
* for the supplied { @link Class testClass } , context configuration attributes ,
* for the supplied { @link Class testClass } , context configuration attributes ,
@ -324,6 +338,9 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
* 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
* @param cacheAwareContextLoaderDelegate the cache - aware context loader delegate to
* @param cacheAwareContextLoaderDelegate the cache - aware context loader delegate to
* be passed to the { @code MergedContextConfiguration } constructor
* be passed to the { @code MergedContextConfiguration } constructor
* @param requireLocationsClassesOrInitializers whether locations , classes , or
* initializers are required ; typically { @code true } but may be set to { @code false }
* if the configured loader supports empty configuration
* @return the merged context configuration
* @return the merged context configuration
* @see # resolveContextLoader
* @see # resolveContextLoader
* @see ContextLoaderUtils # resolveContextConfigurationAttributes
* @see ContextLoaderUtils # resolveContextConfigurationAttributes
@ -335,11 +352,15 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
* /
* /
private MergedContextConfiguration buildMergedContextConfiguration ( Class < ? > testClass ,
private MergedContextConfiguration buildMergedContextConfiguration ( Class < ? > testClass ,
List < ContextConfigurationAttributes > configAttributesList , MergedContextConfiguration parentConfig ,
List < ContextConfigurationAttributes > configAttributesList , MergedContextConfiguration parentConfig ,
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate ) {
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate ,
boolean requireLocationsClassesOrInitializers ) {
Assert . notEmpty ( configAttributesList , "ContextConfigurationAttributes list must not be null or empty" ) ;
ContextLoader contextLoader = resolveContextLoader ( testClass , configAttributesList ) ;
ContextLoader contextLoader = resolveContextLoader ( testClass , configAttributesList ) ;
List < String > locationsList = new ArrayList < String > ( ) ;
List < String > locations = new ArrayList < String > ( ) ;
List < Class < ? > > classesList = new ArrayList < Class < ? > > ( ) ;
List < Class < ? > > classes = new ArrayList < Class < ? > > ( ) ;
List < Class < ? > > initializers = new ArrayList < Class < ? > > ( ) ;
for ( ContextConfigurationAttributes configAttributes : configAttributesList ) {
for ( ContextConfigurationAttributes configAttributes : configAttributesList ) {
if ( logger . isTraceEnabled ( ) ) {
if ( logger . isTraceEnabled ( ) ) {
@ -349,34 +370,53 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
if ( contextLoader instanceof SmartContextLoader ) {
if ( contextLoader instanceof SmartContextLoader ) {
SmartContextLoader smartContextLoader = ( SmartContextLoader ) contextLoader ;
SmartContextLoader smartContextLoader = ( SmartContextLoader ) contextLoader ;
smartContextLoader . processContextConfiguration ( configAttributes ) ;
smartContextLoader . processContextConfiguration ( configAttributes ) ;
locationsList . addAll ( 0 , Arrays . asList ( configAttributes . getLocations ( ) ) ) ;
locations . addAll ( 0 , Arrays . asList ( configAttributes . getLocations ( ) ) ) ;
classesList . addAll ( 0 , Arrays . asList ( configAttributes . getClasses ( ) ) ) ;
classes . addAll ( 0 , Arrays . asList ( configAttributes . getClasses ( ) ) ) ;
}
}
else {
else {
String [ ] processedLocations = contextLoader . processLocations ( configAttributes . getDeclaringClass ( ) ,
String [ ] processedLocations = contextLoader . processLocations (
configAttributes . getLocations ( ) ) ;
configAttributes . getDeclaringClass ( ) , configAttributes . getLocations ( ) ) ;
locationsList . addAll ( 0 , Arrays . asList ( processedLocations ) ) ;
locations . addAll ( 0 , Arrays . asList ( processedLocations ) ) ;
// Legacy ContextLoaders don't know how to process classes
// Legacy ContextLoaders don't know how to process classes
}
}
initializers . addAll ( 0 , Arrays . asList ( configAttributes . getInitializers ( ) ) ) ;
if ( ! configAttributes . isInheritLocations ( ) ) {
if ( ! configAttributes . isInheritLocations ( ) ) {
break ;
break ;
}
}
}
}
String [ ] locations = StringUtils . toStringArray ( locationsList ) ;
if ( requireLocationsClassesOrInitializers & & areAllEmpty ( locations , classes , initializers ) ) {
Class < ? > [ ] classes = ClassUtils . toClassArray ( classesList ) ;
throw new IllegalStateException ( String . format (
Set < Class < ? extends ApplicationContextInitializer < ? extends ConfigurableApplicationContext > > > initializerClasses = //
"%s was unable to detect defaults, and no ApplicationContextInitializers "
ApplicationContextInitializerUtils . resolveInitializerClasses ( configAttributesList ) ;
+ "were declared for context configuration attributes %s" ,
String [ ] activeProfiles = ActiveProfilesUtils . resolveActiveProfiles ( testClass ) ;
contextLoader . getClass ( ) . getSimpleName ( ) , configAttributesList ) ) ;
MergedTestPropertySources mergedTestPropertySources = TestPropertySourceUtils . buildMergedTestPropertySources ( testClass ) ;
}
MergedContextConfiguration mergedConfig = new MergedContextConfiguration ( testClass , locations , classes ,
MergedTestPropertySources mergedTestPropertySources = TestPropertySourceUtils . buildMergedTestPropertySources ( testClass ) ;
initializerClasses , activeProfiles , mergedTestPropertySources . getLocations ( ) ,
MergedContextConfiguration mergedConfig = new MergedContextConfiguration ( testClass ,
mergedTestPropertySources . getProperties ( ) , contextLoader , cacheAwareContextLoaderDelegate , parentConfig ) ;
StringUtils . toStringArray ( locations ) ,
ClassUtils . toClassArray ( classes ) ,
ApplicationContextInitializerUtils . resolveInitializerClasses ( configAttributesList ) ,
ActiveProfilesUtils . resolveActiveProfiles ( testClass ) ,
mergedTestPropertySources . getLocations ( ) ,
mergedTestPropertySources . getProperties ( ) ,
contextLoader , cacheAwareContextLoaderDelegate , parentConfig ) ;
return processMergedContextConfiguration ( mergedConfig ) ;
return processMergedContextConfiguration ( mergedConfig ) ;
}
}
/ * *
* @since 4 . 3
* /
private boolean areAllEmpty ( Collection < ? > . . . collections ) {
for ( Collection < ? > collection : collections ) {
if ( ! collection . isEmpty ( ) ) {
return false ;
}
}
return true ;
}
/ * *
/ * *
* Resolve the { @link ContextLoader } { @linkplain Class class } to use for the
* Resolve the { @link ContextLoader } { @linkplain Class class } to use for the
* supplied list of { @link ContextConfigurationAttributes } and then instantiate
* supplied list of { @link ContextConfigurationAttributes } and then instantiate
@ -389,7 +429,7 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
* @param testClass the test class for which the { @code ContextLoader } should be
* @param testClass the test class for which the { @code ContextLoader } should be
* resolved ; must not be { @code null }
* resolved ; must not be { @code null }
* @param configAttributesList the list of configuration attributes to process ; must
* @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 >
* not be { @code null } ; must be ordered < em > bottom - up < / em >
* ( i . e . , as if we were traversing up the class hierarchy )
* ( i . e . , as if we were traversing up the class hierarchy )
* @return the resolved { @code ContextLoader } for the supplied { @code testClass }
* @return the resolved { @code ContextLoader } for the supplied { @code testClass }
* ( never { @code null } )
* ( never { @code null } )
@ -400,7 +440,7 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
List < ContextConfigurationAttributes > configAttributesList ) {
List < ContextConfigurationAttributes > configAttributesList ) {
Assert . notNull ( testClass , "Class must not be null" ) ;
Assert . notNull ( testClass , "Class must not be null" ) ;
Assert . notEmpty ( configAttributesList , "ContextConfigurationAttributes list must not be empty " ) ;
Assert . notNull ( configAttributesList , "ContextConfigurationAttributes list must not be null " ) ;
Class < ? extends ContextLoader > contextLoaderClass = resolveExplicitContextLoaderClass ( configAttributesList ) ;
Class < ? extends ContextLoader > contextLoaderClass = resolveExplicitContextLoaderClass ( configAttributesList ) ;
if ( contextLoaderClass = = null ) {
if ( contextLoaderClass = = null ) {
@ -429,7 +469,7 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
* step # 1 . < / li >
* step # 1 . < / li >
* < / ol >
* < / ol >
* @param configAttributesList the list of configuration attributes to process ;
* @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 >
* must not be { @code null } ; must be ordered < em > bottom - up < / em >
* ( i . e . , as if we were traversing up the class hierarchy )
* ( i . e . , as if we were traversing up the class hierarchy )
* @return the { @code ContextLoader } class to use for the supplied configuration
* @return the { @code ContextLoader } class to use for the supplied configuration
* attributes , or { @code null } if no explicit loader is found
* attributes , or { @code null } if no explicit loader is found
@ -439,7 +479,8 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
protected Class < ? extends ContextLoader > resolveExplicitContextLoaderClass (
protected Class < ? extends ContextLoader > resolveExplicitContextLoaderClass (
List < ContextConfigurationAttributes > configAttributesList ) {
List < ContextConfigurationAttributes > configAttributesList ) {
Assert . notEmpty ( configAttributesList , "ContextConfigurationAttributes list must not be empty" ) ;
Assert . notNull ( configAttributesList , "ContextConfigurationAttributes list must not be null" ) ;
for ( ContextConfigurationAttributes configAttributes : configAttributesList ) {
for ( ContextConfigurationAttributes configAttributes : configAttributesList ) {
if ( logger . isTraceEnabled ( ) ) {
if ( logger . isTraceEnabled ( ) ) {
logger . trace ( String . format ( "Resolving ContextLoader for context configuration attributes %s" ,
logger . trace ( String . format ( "Resolving ContextLoader for context configuration attributes %s" ,