1919import  static  org .springframework .beans .BeanUtils .*;
2020import  static  org .springframework .core .annotation .AnnotationUtils .*;
2121
22+ import  java .lang .annotation .Annotation ;
23+ import  java .lang .reflect .Constructor ;
2224import  java .util .ArrayList ;
2325import  java .util .Arrays ;
2426import  java .util .HashSet ;
2729
2830import  org .apache .commons .logging .Log ;
2931import  org .apache .commons .logging .LogFactory ;
32+ 
3033import  org .springframework .context .ApplicationContextInitializer ;
3134import  org .springframework .context .ConfigurableApplicationContext ;
32- import  org .springframework .test .context .web .WebAppConfiguration ;
33- import  org .springframework .test .context .web .WebMergedContextConfiguration ;
35+ import  org .springframework .core .annotation .AnnotationUtils ;
3436import  org .springframework .util .Assert ;
3537import  org .springframework .util .ClassUtils ;
3638import  org .springframework .util .ObjectUtils ;
@@ -57,6 +59,9 @@ abstract class ContextLoaderUtils {
5759	private  static  final  String  DEFAULT_CONTEXT_LOADER_CLASS_NAME  = "org.springframework.test.context.support.DelegatingSmartContextLoader" ;
5860	private  static  final  String  DEFAULT_WEB_CONTEXT_LOADER_CLASS_NAME  = "org.springframework.test.context.support.WebDelegatingSmartContextLoader" ;
5961
62+ 	private  static  final  String  WEB_APP_CONFIGURATION_CLASS_NAME  = "org.springframework.test.context.web.WebAppConfiguration" ;
63+ 	private  static  final  String  WEB_MERGED_CONTEXT_CONFIGURATION_CLASS_NAME  = "org.springframework.test.context.web.WebMergedContextConfiguration" ;
64+ 
6065
6166	private  ContextLoaderUtils () {
6267		/* no-op */ 
@@ -69,7 +74,8 @@ private ContextLoaderUtils() {
6974	 * 
7075	 * <p>If the supplied <code>defaultContextLoaderClassName</code> is 
7176	 * {@code null} or <em>empty</em>, depending on the absence or presence 
72- 	 * of @{@link WebAppConfiguration} either {@value #DEFAULT_CONTEXT_LOADER_CLASS_NAME} 
77+ 	 * of {@link org.springframework.test.context.web.WebAppConfiguration @WebAppConfiguration} 
78+ 	 * either {@value #DEFAULT_CONTEXT_LOADER_CLASS_NAME} 
7379	 * or {@value #DEFAULT_WEB_CONTEXT_LOADER_CLASS_NAME} will be used as the 
7480	 * default context loader class name. For details on the class resolution 
7581	 * process, see {@link #resolveContextLoaderClass()}. 
@@ -91,7 +97,9 @@ static ContextLoader resolveContextLoader(Class<?> testClass,
9197		Assert .notEmpty (configAttributesList , "ContextConfigurationAttributes list must not be empty" );
9298
9399		if  (!StringUtils .hasText (defaultContextLoaderClassName )) {
94- 			defaultContextLoaderClassName  = testClass .isAnnotationPresent (WebAppConfiguration .class ) ? DEFAULT_WEB_CONTEXT_LOADER_CLASS_NAME 
100+ 			Class <? extends  Annotation > webAppConfigClass  = loadWebAppConfigurationClass ();
101+ 			defaultContextLoaderClassName  = webAppConfigClass  != null 
102+ 					&& testClass .isAnnotationPresent (webAppConfigClass ) ? DEFAULT_WEB_CONTEXT_LOADER_CLASS_NAME 
95103					: DEFAULT_CONTEXT_LOADER_CLASS_NAME ;
96104		}
97105
@@ -385,16 +393,82 @@ static MergedContextConfiguration buildMergedContextConfiguration(Class<?> testC
385393		Set <Class <? extends  ApplicationContextInitializer <? extends  ConfigurableApplicationContext >>> initializerClasses  = resolveInitializerClasses (configAttributesList );
386394		String [] activeProfiles  = resolveActiveProfiles (testClass );
387395
388- 		if  (testClass .isAnnotationPresent (WebAppConfiguration .class )) {
389- 			WebAppConfiguration  webAppConfig  = testClass .getAnnotation (WebAppConfiguration .class );
390- 			String  resourceBasePath  = webAppConfig .value ();
391- 			return  new  WebMergedContextConfiguration (testClass , locations , classes , initializerClasses , activeProfiles ,
392- 				resourceBasePath , contextLoader );
396+ 		MergedContextConfiguration  mergedConfig  = buildWebMergedContextConfiguration (testClass , locations , classes ,
397+ 			initializerClasses , activeProfiles , contextLoader );
398+ 
399+ 		if  (mergedConfig  == null ) {
400+ 			mergedConfig  = new  MergedContextConfiguration (testClass , locations , classes , initializerClasses ,
401+ 				activeProfiles , contextLoader );
402+ 		}
403+ 
404+ 		return  mergedConfig ;
405+ 	}
406+ 
407+ 	/** 
408+ 	 * Load the {@link org.springframework.test.context.web.WebAppConfiguration @WebAppConfiguration} 
409+ 	 * class using reflection in order to avoid package cycles. 
410+ 	 *  
411+ 	 * @return the {@code @WebAppConfiguration} class or <code>null</code> if it 
412+ 	 * cannot be loaded 
413+ 	 */ 
414+ 	@ SuppressWarnings ("unchecked" )
415+ 	private  static  Class <? extends  Annotation > loadWebAppConfigurationClass () {
416+ 		Class <? extends  Annotation > webAppConfigClass  = null ;
417+ 		try  {
418+ 			webAppConfigClass  = (Class <? extends  Annotation >) ClassUtils .forName (WEB_APP_CONFIGURATION_CLASS_NAME ,
419+ 				ContextLoaderUtils .class .getClassLoader ());
420+ 		}
421+ 		catch  (Throwable  t ) {
422+ 			if  (logger .isDebugEnabled ()) {
423+ 				logger .debug ("Could not load @WebAppConfiguration class ["  + WEB_APP_CONFIGURATION_CLASS_NAME  + "]." , t );
424+ 			}
425+ 		}
426+ 		return  webAppConfigClass ;
427+ 	}
428+ 
429+ 	/** 
430+ 	 * Attempt to build a {@link org.springframework.test.context.web.WebMergedContextConfiguration 
431+ 	 * WebMergedContextConfiguration} from the supplied arguments using reflection 
432+ 	 * in order to avoid package cycles. 
433+ 	 * 
434+ 	 * @return the {@code WebMergedContextConfiguration} or <code>null</code> if 
435+ 	 * it could not be built 
436+ 	 */ 
437+ 	@ SuppressWarnings ("unchecked" )
438+ 	private  static  MergedContextConfiguration  buildWebMergedContextConfiguration (
439+ 			Class <?> testClass ,
440+ 			String [] locations ,
441+ 			Class <?>[] classes ,
442+ 			Set <Class <? extends  ApplicationContextInitializer <? extends  ConfigurableApplicationContext >>> initializerClasses ,
443+ 			String [] activeProfiles , ContextLoader  contextLoader ) {
444+ 
445+ 		Class <? extends  Annotation > webAppConfigClass  = loadWebAppConfigurationClass ();
446+ 
447+ 		if  (webAppConfigClass  != null  && testClass .isAnnotationPresent (webAppConfigClass )) {
448+ 			Annotation  annotation  = testClass .getAnnotation (webAppConfigClass );
449+ 			String  resourceBasePath  = (String ) AnnotationUtils .getValue (annotation );
450+ 
451+ 			try  {
452+ 				Class <? extends  MergedContextConfiguration > webMergedConfigClass  = (Class <? extends  MergedContextConfiguration >) ClassUtils .forName (
453+ 					WEB_MERGED_CONTEXT_CONFIGURATION_CLASS_NAME , ContextLoaderUtils .class .getClassLoader ());
454+ 
455+ 				Constructor <? extends  MergedContextConfiguration > constructor  = ClassUtils .getConstructorIfAvailable (
456+ 					webMergedConfigClass , Class .class , String [].class , Class [].class , Set .class , String [].class ,
457+ 					String .class , ContextLoader .class );
458+ 
459+ 				if  (constructor  != null ) {
460+ 					return  instantiateClass (constructor , testClass , locations , classes , initializerClasses ,
461+ 						activeProfiles , resourceBasePath , contextLoader );
462+ 				}
463+ 			}
464+ 			catch  (Throwable  t ) {
465+ 				if  (logger .isDebugEnabled ()) {
466+ 					logger .debug ("Could not instantiate ["  + WEB_MERGED_CONTEXT_CONFIGURATION_CLASS_NAME  + "]." , t );
467+ 				}
468+ 			}
393469		}
394470
395- 		// else 
396- 		return  new  MergedContextConfiguration (testClass , locations , classes , initializerClasses , activeProfiles ,
397- 			contextLoader );
471+ 		return  null ;
398472	}
399473
400474}
0 commit comments