21
21
22
22
import java .util .ArrayList ;
23
23
import java .util .Arrays ;
24
- import java .util .Collections ;
25
24
import java .util .HashSet ;
26
25
import java .util .List ;
27
26
import java .util .Set ;
@@ -64,58 +63,70 @@ private ContextLoaderUtils() {
64
63
}
65
64
66
65
/**
67
- * Resolve the {@link ContextLoader} {@link Class class} to use for the
68
- * supplied {@link Class testClass } and then instantiate and return that
69
- * {@code ContextLoader}.
66
+ * Resolve the {@link ContextLoader} {@linkplain Class class} to use for the
67
+ * supplied list of {@link ContextConfigurationAttributes } and then
68
+ * instantiate and return that {@code ContextLoader}.
70
69
*
71
70
* <p>If the supplied <code>defaultContextLoaderClassName</code> is
72
- * <code>null</code> or <em>empty</em>, the <em>standard</em>
73
- * default context loader class name {@value #DEFAULT_CONTEXT_LOADER_CLASS_NAME}
74
- * will be used. For details on the class resolution process, see
75
- * {@link #resolveContextLoaderClass()}.
71
+ * {@code null} or <em>empty</em>, depending on the absence or presence
72
+ * of @{@link WebAppConfiguration} either {@value #DEFAULT_CONTEXT_LOADER_CLASS_NAME}
73
+ * or {@value #DEFAULT_WEB_CONTEXT_LOADER_CLASS_NAME} will be used as the
74
+ * default context loader class name. For details on the class resolution
75
+ * process, see {@link #resolveContextLoaderClass()}.
76
76
*
77
77
* @param testClass the test class for which the {@code ContextLoader}
78
- * should be resolved (must not be <code>null</code>)
78
+ * should be resolved; must not be {@code null}
79
+ * @param configAttributesList the list of configuration attributes to process;
80
+ * must not be {@code null} or <em>empty</em>; must be ordered <em>bottom-up</em>
81
+ * (i.e., as if we were traversing up the class hierarchy)
79
82
* @param defaultContextLoaderClassName the name of the default
80
- * {@code ContextLoader} class to use ( may be < code> null</code>)
83
+ * {@code ContextLoader} class to use; may be {@ code null} or <em>empty</em>
81
84
* @return the resolved {@code ContextLoader} for the supplied
82
- * <code>testClass</code> (never < code> null</code> )
85
+ * <code>testClass</code> (never {@ code null} )
83
86
* @see #resolveContextLoaderClass()
84
87
*/
85
- static ContextLoader resolveContextLoader (Class <?> testClass , String defaultContextLoaderClassName ) {
86
- Assert .notNull (testClass , "Test class must not be null" );
88
+ static ContextLoader resolveContextLoader (Class <?> testClass ,
89
+ List <ContextConfigurationAttributes > configAttributesList , String defaultContextLoaderClassName ) {
90
+ Assert .notNull (testClass , "Class must not be null" );
91
+ Assert .notEmpty (configAttributesList , "ContextConfigurationAttributes list must not be empty" );
87
92
88
93
if (!StringUtils .hasText (defaultContextLoaderClassName )) {
89
94
defaultContextLoaderClassName = testClass .isAnnotationPresent (WebAppConfiguration .class ) ? DEFAULT_WEB_CONTEXT_LOADER_CLASS_NAME
90
95
: DEFAULT_CONTEXT_LOADER_CLASS_NAME ;
91
96
}
92
97
93
- Class <? extends ContextLoader > contextLoaderClass = resolveContextLoaderClass (testClass ,
98
+ Class <? extends ContextLoader > contextLoaderClass = resolveContextLoaderClass (testClass , configAttributesList ,
94
99
defaultContextLoaderClassName );
95
100
96
101
return instantiateClass (contextLoaderClass , ContextLoader .class );
97
102
}
98
103
99
104
/**
100
- * Resolve the {@link ContextLoader} {@link Class} to use for the supplied
101
- * {@link Class testClass}.
105
+ * Resolve the {@link ContextLoader} {@linkplain Class class} to use for the
106
+ * supplied list of {@link ContextConfigurationAttributes}.
107
+ *
108
+ * <p>Beginning with the first level in the context configuration attributes
109
+ * hierarchy:
102
110
*
103
111
* <ol>
104
- * <li>If the {@link ContextConfiguration#loader() loader} attribute of
105
- * {@link ContextConfiguration @ContextConfiguration } is configured
106
- * with an explicit class, that class will be returned.</li>
107
- * <li>If a < code>loader</code> class is not specified, the class hierarchy
108
- * will be traversed to find a parent class annotated with
109
- * {@code @ContextConfiguration}; go to step #1.</li>
110
- * <li>If no explicit < code>loader</code> class is found after traversing
111
- * the class hierarchy, an attempt will be made to load and return the class
112
+ * <li>If the {@link ContextConfigurationAttributes#getContextLoaderClass()
113
+ * contextLoaderClass} property of {@link ContextConfigurationAttributes } is
114
+ * configured with an explicit class, that class will be returned.</li>
115
+ * <li>If an explicit {@ code ContextLoader} class is not specified at the
116
+ * current level in the hierarchy, traverse to the next level in the hierarchy
117
+ * and return to step #1.</li>
118
+ * <li>If no explicit {@ code ContextLoader} class is found after traversing
119
+ * the hierarchy, an attempt will be made to load and return the class
112
120
* with the supplied <code>defaultContextLoaderClassName</code>.</li>
113
121
* </ol>
114
122
*
115
123
* @param testClass the class for which to resolve the {@code ContextLoader}
116
- * class; must not be <code>null</code>
124
+ * class; must not be {@code null}; only used for logging purposes
125
+ * @param configAttributesList the list of configuration attributes to process;
126
+ * must not be {@code null} or <em>empty</em>; must be ordered <em>bottom-up</em>
127
+ * (i.e., as if we were traversing up the class hierarchy)
117
128
* @param defaultContextLoaderClassName the name of the default
118
- * {@code ContextLoader} class to use; must not be < code> null</code> or empty
129
+ * {@code ContextLoader} class to use; must not be {@ code null} or empty
119
130
* @return the {@code ContextLoader} class to use for the supplied test class
120
131
* @throws IllegalArgumentException if {@code @ContextConfiguration} is not
121
132
* <em>present</em> on the supplied test class
@@ -124,46 +135,37 @@ static ContextLoader resolveContextLoader(Class<?> testClass, String defaultCont
124
135
*/
125
136
@ SuppressWarnings ("unchecked" )
126
137
static Class <? extends ContextLoader > resolveContextLoaderClass (Class <?> testClass ,
127
- String defaultContextLoaderClassName ) {
138
+ List < ContextConfigurationAttributes > configAttributesList , String defaultContextLoaderClassName ) {
128
139
Assert .notNull (testClass , "Class must not be null" );
140
+ Assert .notEmpty (configAttributesList , "ContextConfigurationAttributes list must not be empty" );
129
141
Assert .hasText (defaultContextLoaderClassName , "Default ContextLoader class name must not be null or empty" );
130
142
131
- Class <ContextConfiguration > annotationType = ContextConfiguration .class ;
132
- Class <?> declaringClass = findAnnotationDeclaringClass (annotationType , testClass );
133
- Assert .notNull (declaringClass , String .format (
134
- "Could not find an 'annotation declaring class' for annotation type [%s] and test class [%s]" ,
135
- annotationType , testClass ));
136
-
137
- while (declaringClass != null ) {
138
- ContextConfiguration contextConfiguration = declaringClass .getAnnotation (annotationType );
139
-
143
+ for (ContextConfigurationAttributes configAttributes : configAttributesList ) {
140
144
if (logger .isTraceEnabled ()) {
141
- logger .trace (String .format (
142
- "Processing ContextLoader for @ContextConfiguration [%s] and declaring class [%s]" ,
143
- contextConfiguration , declaringClass ));
145
+ logger .trace (String .format ("Processing ContextLoader for context configuration attributes %s" ,
146
+ configAttributes ));
144
147
}
145
148
146
- Class <? extends ContextLoader > contextLoaderClass = contextConfiguration . loader ();
149
+ Class <? extends ContextLoader > contextLoaderClass = configAttributes . getContextLoaderClass ();
147
150
if (!ContextLoader .class .equals (contextLoaderClass )) {
148
151
if (logger .isDebugEnabled ()) {
149
152
logger .debug (String .format (
150
- "Found explicit ContextLoader class [%s] for @ContextConfiguration [%s] and declaring class [%s] " ,
151
- contextLoaderClass , contextConfiguration , declaringClass ));
153
+ "Found explicit ContextLoader class [%s] for context configuration attributes %s " ,
154
+ contextLoaderClass . getName (), configAttributes ));
152
155
}
153
156
return contextLoaderClass ;
154
157
}
155
-
156
- declaringClass = findAnnotationDeclaringClass (annotationType , declaringClass .getSuperclass ());
157
158
}
158
159
159
160
try {
160
161
if (logger .isTraceEnabled ()) {
161
162
logger .trace (String .format ("Using default ContextLoader class [%s] for test class [%s]" ,
162
- defaultContextLoaderClassName , testClass ));
163
+ defaultContextLoaderClassName , testClass . getName () ));
163
164
}
164
165
return (Class <? extends ContextLoader >) ContextLoaderUtils .class .getClassLoader ().loadClass (
165
166
defaultContextLoaderClassName );
166
- } catch (ClassNotFoundException ex ) {
167
+ }
168
+ catch (ClassNotFoundException ex ) {
167
169
throw new IllegalStateException ("Could not load default ContextLoader class ["
168
170
+ defaultContextLoaderClassName + "]. Specify @ContextConfiguration's 'loader' "
169
171
+ "attribute or make the default loader class available." );
@@ -181,30 +183,29 @@ static Class<? extends ContextLoader> resolveContextLoaderClass(Class<?> testCla
181
183
* consideration. If these flags need to be honored, that must be handled
182
184
* manually when traversing the list returned by this method.
183
185
*
184
- * @param clazz the class for which to resolve the configuration attributes (must
185
- * not be < code> null</code> )
186
- * @return the list of configuration attributes for the specified class
187
- * (never < code> null</code>)
188
- * @throws IllegalArgumentException if the supplied class is < code> null</code> or
186
+ * @param testClass the class for which to resolve the configuration attributes (must
187
+ * not be {@ code null} )
188
+ * @return the list of configuration attributes for the specified class, ordered <em>bottom-up</em>
189
+ * (i.e., as if we were traversing up the class hierarchy); never {@ code null}
190
+ * @throws IllegalArgumentException if the supplied class is {@ code null} or
189
191
* if {@code @ContextConfiguration} is not <em>present</em> on the supplied class
190
192
*/
191
- static List <ContextConfigurationAttributes > resolveContextConfigurationAttributes (Class <?> clazz ) {
192
- Assert .notNull (clazz , "Class must not be null" );
193
+ static List <ContextConfigurationAttributes > resolveContextConfigurationAttributes (Class <?> testClass ) {
194
+ Assert .notNull (testClass , "Class must not be null" );
193
195
194
196
final List <ContextConfigurationAttributes > attributesList = new ArrayList <ContextConfigurationAttributes >();
195
197
196
198
Class <ContextConfiguration > annotationType = ContextConfiguration .class ;
197
- Class <?> declaringClass = findAnnotationDeclaringClass (annotationType , clazz );
199
+ Class <?> declaringClass = findAnnotationDeclaringClass (annotationType , testClass );
198
200
Assert .notNull (declaringClass , String .format (
199
- "Could not find an 'annotation declaring class' for annotation type [%s] and class [%s]" , annotationType ,
200
- clazz ));
201
+ "Could not find an 'annotation declaring class' for annotation type [%s] and class [%s]" ,
202
+ annotationType . getName (), testClass . getName () ));
201
203
202
204
while (declaringClass != null ) {
203
205
ContextConfiguration contextConfiguration = declaringClass .getAnnotation (annotationType );
204
-
205
206
if (logger .isTraceEnabled ()) {
206
207
logger .trace (String .format ("Retrieved @ContextConfiguration [%s] for declaring class [%s]." ,
207
- contextConfiguration , declaringClass ));
208
+ contextConfiguration , declaringClass . getName () ));
208
209
}
209
210
210
211
ContextConfigurationAttributes attributes = new ContextConfigurationAttributes (declaringClass ,
@@ -213,28 +214,14 @@ static List<ContextConfigurationAttributes> resolveContextConfigurationAttribute
213
214
logger .trace ("Resolved context configuration attributes: " + attributes );
214
215
}
215
216
216
- attributesList .add (0 , attributes );
217
+ attributesList .add (attributes );
217
218
218
219
declaringClass = findAnnotationDeclaringClass (annotationType , declaringClass .getSuperclass ());
219
220
}
220
221
221
222
return attributesList ;
222
223
}
223
224
224
- /**
225
- * Create a copy of the supplied list of {@code ContextConfigurationAttributes}
226
- * in reverse order.
227
- *
228
- * @since 3.2
229
- */
230
- private static List <ContextConfigurationAttributes > reverseContextConfigurationAttributes (
231
- List <ContextConfigurationAttributes > configAttributesList ) {
232
- List <ContextConfigurationAttributes > configAttributesListReversed = new ArrayList <ContextConfigurationAttributes >(
233
- configAttributesList );
234
- Collections .reverse (configAttributesListReversed );
235
- return configAttributesListReversed ;
236
- }
237
-
238
225
/**
239
226
* Resolve the list of merged {@code ApplicationContextInitializer} classes
240
227
* for the supplied list of {@code ContextConfigurationAttributes}.
@@ -247,22 +234,21 @@ private static List<ContextConfigurationAttributes> reverseContextConfigurationA
247
234
* at the given level will be merged with those defined in higher levels
248
235
* of the class hierarchy.
249
236
*
250
- * @param configAttributesList the list of configuration attributes to process
251
- * (must not be <code>null</code>)
252
- * @return the list of merged context initializer classes, including those
253
- * from superclasses if appropriate (never <code>null</code>)
237
+ * @param configAttributesList the list of configuration attributes to process;
238
+ * must not be {@code null} or <em>empty</em>; must be ordered <em>bottom-up</em>
239
+ * (i.e., as if we were traversing up the class hierarchy)
240
+ * @return the set of merged context initializer classes, including those
241
+ * from superclasses if appropriate (never {@code null})
254
242
* @since 3.2
255
243
*/
256
244
static Set <Class <? extends ApplicationContextInitializer <? extends ConfigurableApplicationContext >>> resolveInitializerClasses (
257
245
List <ContextConfigurationAttributes > configAttributesList ) {
258
- Assert .notNull (configAttributesList , "configAttributesList must not be null " );
246
+ Assert .notEmpty (configAttributesList , "ContextConfigurationAttributes list must not be empty " );
259
247
260
248
final Set <Class <? extends ApplicationContextInitializer <? extends ConfigurableApplicationContext >>> initializerClasses = //
261
249
new HashSet <Class <? extends ApplicationContextInitializer <? extends ConfigurableApplicationContext >>>();
262
250
263
- // Traverse config attributes in reverse order (i.e., as if we were traversing up
264
- // the class hierarchy).
265
- for (ContextConfigurationAttributes configAttributes : reverseContextConfigurationAttributes (configAttributesList )) {
251
+ for (ContextConfigurationAttributes configAttributes : configAttributesList ) {
266
252
if (logger .isTraceEnabled ()) {
267
253
logger .trace (String .format ("Processing context initializers for context configuration attributes %s" ,
268
254
configAttributes ));
@@ -287,23 +273,23 @@ static Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableA
287
273
* set to <code>true</code>, profiles defined in the test class will be
288
274
* merged with those defined in superclasses.
289
275
*
290
- * @param clazz the class for which to resolve the active profiles (must
291
- * not be < code> null</code> )
276
+ * @param testClass the class for which to resolve the active profiles (must
277
+ * not be {@ code null} )
292
278
* @return the set of active profiles for the specified class, including
293
- * active profiles from superclasses if appropriate (never < code> null</code> )
279
+ * active profiles from superclasses if appropriate (never {@ code null} )
294
280
* @see org.springframework.test.context.ActiveProfiles
295
281
* @see org.springframework.context.annotation.Profile
296
282
*/
297
- static String [] resolveActiveProfiles (Class <?> clazz ) {
298
- Assert .notNull (clazz , "Class must not be null" );
283
+ static String [] resolveActiveProfiles (Class <?> testClass ) {
284
+ Assert .notNull (testClass , "Class must not be null" );
299
285
300
286
Class <ActiveProfiles > annotationType = ActiveProfiles .class ;
301
- Class <?> declaringClass = findAnnotationDeclaringClass (annotationType , clazz );
287
+ Class <?> declaringClass = findAnnotationDeclaringClass (annotationType , testClass );
302
288
303
289
if (declaringClass == null && logger .isDebugEnabled ()) {
304
290
logger .debug (String .format (
305
291
"Could not find an 'annotation declaring class' for annotation type [%s] and class [%s]" ,
306
- annotationType , clazz ));
292
+ annotationType . getName (), testClass . getName () ));
307
293
}
308
294
309
295
final Set <String > activeProfiles = new HashSet <String >();
@@ -313,7 +299,7 @@ static String[] resolveActiveProfiles(Class<?> clazz) {
313
299
314
300
if (logger .isTraceEnabled ()) {
315
301
logger .trace (String .format ("Retrieved @ActiveProfiles [%s] for declaring class [%s]." , annotation ,
316
- declaringClass ));
302
+ declaringClass . getName () ));
317
303
}
318
304
319
305
String [] profiles = annotation .profiles ();
@@ -322,11 +308,12 @@ static String[] resolveActiveProfiles(Class<?> clazz) {
322
308
if (!ObjectUtils .isEmpty (valueProfiles ) && !ObjectUtils .isEmpty (profiles )) {
323
309
String msg = String .format ("Test class [%s] has been configured with @ActiveProfiles' 'value' [%s] "
324
310
+ "and 'profiles' [%s] attributes. Only one declaration of active bean "
325
- + "definition profiles is permitted per @ActiveProfiles annotation." , declaringClass ,
311
+ + "definition profiles is permitted per @ActiveProfiles annotation." , declaringClass . getName () ,
326
312
ObjectUtils .nullSafeToString (valueProfiles ), ObjectUtils .nullSafeToString (profiles ));
327
313
logger .error (msg );
328
314
throw new IllegalStateException (msg );
329
- } else if (!ObjectUtils .isEmpty (valueProfiles )) {
315
+ }
316
+ else if (!ObjectUtils .isEmpty (valueProfiles )) {
330
317
profiles = valueProfiles ;
331
318
}
332
319
@@ -349,9 +336,9 @@ static String[] resolveActiveProfiles(Class<?> clazz) {
349
336
* <code>defaultContextLoaderClassName</code>.
350
337
*
351
338
* @param testClass the test class for which the {@code MergedContextConfiguration}
352
- * should be built (must not be < code> null</code> )
339
+ * should be built (must not be {@ code null} )
353
340
* @param defaultContextLoaderClassName the name of the default
354
- * {@code ContextLoader} class to use (may be < code> null</code> )
341
+ * {@code ContextLoader} class to use (may be {@ code null} )
355
342
* @return the merged context configuration
356
343
* @see #resolveContextLoader()
357
344
* @see #resolveContextConfigurationAttributes()
@@ -363,14 +350,13 @@ static String[] resolveActiveProfiles(Class<?> clazz) {
363
350
static MergedContextConfiguration buildMergedContextConfiguration (Class <?> testClass ,
364
351
String defaultContextLoaderClassName ) {
365
352
366
- final ContextLoader contextLoader = resolveContextLoader (testClass , defaultContextLoaderClassName );
367
353
final List <ContextConfigurationAttributes > configAttributesList = resolveContextConfigurationAttributes (testClass );
354
+ final ContextLoader contextLoader = resolveContextLoader (testClass , configAttributesList ,
355
+ defaultContextLoaderClassName );
368
356
final List <String > locationsList = new ArrayList <String >();
369
357
final List <Class <?>> classesList = new ArrayList <Class <?>>();
370
358
371
- // Traverse config attributes in reverse order (i.e., as if we were traversing up
372
- // the class hierarchy).
373
- for (ContextConfigurationAttributes configAttributes : reverseContextConfigurationAttributes (configAttributesList )) {
359
+ for (ContextConfigurationAttributes configAttributes : configAttributesList ) {
374
360
if (logger .isTraceEnabled ()) {
375
361
logger .trace (String .format ("Processing locations and classes for context configuration attributes %s" ,
376
362
configAttributes ));
@@ -381,7 +367,8 @@ static MergedContextConfiguration buildMergedContextConfiguration(Class<?> testC
381
367
smartContextLoader .processContextConfiguration (configAttributes );
382
368
locationsList .addAll (0 , Arrays .asList (configAttributes .getLocations ()));
383
369
classesList .addAll (0 , Arrays .asList (configAttributes .getClasses ()));
384
- } else {
370
+ }
371
+ else {
385
372
String [] processedLocations = contextLoader .processLocations (configAttributes .getDeclaringClass (),
386
373
configAttributes .getLocations ());
387
374
locationsList .addAll (0 , Arrays .asList (processedLocations ));
0 commit comments