Skip to content

Commit 9c5cabf

Browse files
committed
Refined ApplicationContextInitializer assignability exception
(cherry picked from commit ca19920)
1 parent 97b0177 commit 9c5cabf

File tree

5 files changed

+72
-80
lines changed

5 files changed

+72
-80
lines changed

spring-core/src/main/java/org/springframework/util/Assert.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
55
* use this file except in compliance with the License. You may obtain a copy of
@@ -373,8 +373,8 @@ public static void isAssignable(Class<?> superType, Class<?> subType) {
373373
public static void isAssignable(Class<?> superType, Class<?> subType, String message) {
374374
notNull(superType, "Type to check against must not be null");
375375
if (subType == null || !superType.isAssignableFrom(subType)) {
376-
throw new IllegalArgumentException((StringUtils.hasLength(message) ? message + " " : "")
377-
+ subType + " is not assignable to " + superType);
376+
throw new IllegalArgumentException((StringUtils.hasLength(message) ? message + " " : "") +
377+
subType + " is not assignable to " + superType);
378378
}
379379
}
380380

spring-test/src/main/java/org/springframework/test/context/support/AbstractContextLoader.java

Lines changed: 42 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@
2525

2626
import org.springframework.beans.BeanUtils;
2727
import org.springframework.context.ApplicationContext;
28+
import org.springframework.context.ApplicationContextException;
2829
import org.springframework.context.ApplicationContextInitializer;
2930
import org.springframework.context.ConfigurableApplicationContext;
3031
import org.springframework.core.GenericTypeResolver;
@@ -86,8 +87,8 @@ public abstract class AbstractContextLoader implements SmartContextLoader {
8687
*/
8788
@Override
8889
public void processContextConfiguration(ContextConfigurationAttributes configAttributes) {
89-
String[] processedLocations = processLocations(configAttributes.getDeclaringClass(),
90-
configAttributes.getLocations());
90+
String[] processedLocations =
91+
processLocations(configAttributes.getDeclaringClass(), configAttributes.getLocations());
9192
configAttributes.setLocations(processedLocations);
9293
}
9394

@@ -135,7 +136,9 @@ protected void prepareContext(ConfigurableApplicationContext context, MergedCont
135136
@SuppressWarnings("unchecked")
136137
private void invokeApplicationContextInitializers(ConfigurableApplicationContext context,
137138
MergedContextConfiguration mergedConfig) {
138-
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses = mergedConfig.getContextInitializerClasses();
139+
140+
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses =
141+
mergedConfig.getContextInitializerClasses();
139142
if (initializerClasses.isEmpty()) {
140143
// no ApplicationContextInitializers have been declared -> nothing to do
141144
return;
@@ -145,13 +148,15 @@ private void invokeApplicationContextInitializers(ConfigurableApplicationContext
145148
Class<?> contextClass = context.getClass();
146149

147150
for (Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>> initializerClass : initializerClasses) {
148-
Class<?> initializerContextClass = GenericTypeResolver.resolveTypeArgument(initializerClass,
149-
ApplicationContextInitializer.class);
150-
Assert.isAssignable(initializerContextClass, contextClass, String.format(
151-
"Could not add context initializer [%s] since its generic parameter [%s] "
152-
+ "is not assignable from the type of application context used by this "
153-
+ "context loader [%s]: ", initializerClass.getName(), initializerContextClass.getName(),
154-
contextClass.getName()));
151+
Class<?> initializerContextClass =
152+
GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
153+
if (initializerContextClass != null && !initializerContextClass.isInstance(context)) {
154+
throw new ApplicationContextException(String.format(
155+
"Could not apply context initializer [%s] since its generic parameter [%s] " +
156+
"is not assignable from the type of application context used by this " +
157+
"context loader: [%s]", initializerClass.getName(), initializerContextClass.getName(),
158+
contextClass.getName()));
159+
}
155160
initializerInstances.add((ApplicationContextInitializer<ConfigurableApplicationContext>) BeanUtils.instantiateClass(initializerClass));
156161
}
157162

@@ -161,6 +166,7 @@ private void invokeApplicationContextInitializers(ConfigurableApplicationContext
161166
}
162167
}
163168

169+
164170
// --- ContextLoader -------------------------------------------------------
165171

166172
/**
@@ -171,7 +177,6 @@ private void invokeApplicationContextInitializers(ConfigurableApplicationContext
171177
* and the configured {@linkplain #getResourceSuffixes() resource suffixes};
172178
* otherwise, the supplied {@code locations} will be
173179
* {@linkplain #modifyLocations modified} if necessary and returned.
174-
*
175180
* @param clazz the class with which the locations are associated: to be
176181
* used when generating default locations
177182
* @param locations the unmodified locations to use for loading the
@@ -186,30 +191,26 @@ private void invokeApplicationContextInitializers(ConfigurableApplicationContext
186191
*/
187192
@Override
188193
public final String[] processLocations(Class<?> clazz, String... locations) {
189-
return (ObjectUtils.isEmpty(locations) && isGenerateDefaultLocations()) ? generateDefaultLocations(clazz)
190-
: modifyLocations(clazz, locations);
194+
return (ObjectUtils.isEmpty(locations) && isGenerateDefaultLocations()) ?
195+
generateDefaultLocations(clazz) : modifyLocations(clazz, locations);
191196
}
192197

193198
/**
194199
* Generate the default classpath resource locations array based on the
195200
* supplied class.
196-
*
197201
* <p>For example, if the supplied class is {@code com.example.MyTest},
198202
* the generated locations will contain a single string with a value of
199203
* {@code "classpath:com/example/MyTest<suffix>"}, where {@code <suffix>}
200204
* is the value of the first configured
201205
* {@linkplain #getResourceSuffixes() resource suffix} for which the
202206
* generated location actually exists in the classpath.
203-
*
204207
* <p>As of Spring 3.1, the implementation of this method adheres to the
205208
* contract defined in the {@link SmartContextLoader} SPI. Specifically,
206209
* this method will <em>preemptively</em> verify that the generated default
207210
* location actually exists. If it does not exist, this method will log a
208211
* warning and return an empty array.
209-
*
210212
* <p>Subclasses can override this method to implement a different
211213
* <em>default location generation</em> strategy.
212-
*
213214
* @param clazz the class for which the default locations are to be generated
214215
* @return an array of default application context resource locations
215216
* @since 2.5
@@ -224,23 +225,22 @@ protected String[] generateDefaultLocations(Class<?> clazz) {
224225
String resourcePath = ClassUtils.convertClassNameToResourcePath(clazz.getName()) + suffix;
225226
String prefixedResourcePath = ResourceUtils.CLASSPATH_URL_PREFIX + resourcePath;
226227
ClassPathResource classPathResource = new ClassPathResource(resourcePath);
227-
228228
if (classPathResource.exists()) {
229229
if (logger.isInfoEnabled()) {
230230
logger.info(String.format("Detected default resource location \"%s\" for test class [%s]",
231-
prefixedResourcePath, clazz.getName()));
231+
prefixedResourcePath, clazz.getName()));
232232
}
233-
return new String[] { prefixedResourcePath };
233+
return new String[] {prefixedResourcePath};
234234
}
235235
else if (logger.isDebugEnabled()) {
236-
logger.debug(String.format("Did not detect default resource location for test class [%s]: "
237-
+ "%s does not exist", clazz.getName(), classPathResource));
236+
logger.debug(String.format("Did not detect default resource location for test class [%s]: " +
237+
"%s does not exist", clazz.getName(), classPathResource));
238238
}
239239
}
240240

241241
if (logger.isInfoEnabled()) {
242-
logger.info(String.format("Could not detect default resource locations for test class [%s]: "
243-
+ "no resource found for suffixes %s.", clazz.getName(), ObjectUtils.nullSafeToString(suffixes)));
242+
logger.info(String.format("Could not detect default resource locations for test class [%s]: " +
243+
"no resource found for suffixes %s.", clazz.getName(), ObjectUtils.nullSafeToString(suffixes)));
244244
}
245245

246246
return EMPTY_STRING_ARRAY;
@@ -282,36 +282,31 @@ protected boolean isGenerateDefaultLocations() {
282282
}
283283

284284
/**
285-
* Get the suffix to append to {@link ApplicationContext} resource
286-
* locations when detecting default locations.
287-
*
288-
* <p>Subclasses must provide an implementation of this method that
289-
* returns a single suffix. Alternatively subclasses may provide a
290-
* <em>no-op</em> implementation of this method and override
291-
* {@link #getResourceSuffixes()} in order to provide multiple custom
292-
* suffixes.
293-
*
294-
* @return the resource suffix; never {@code null} or empty
295-
* @since 2.5
296-
* @see #generateDefaultLocations(Class)
297-
* @see #getResourceSuffixes()
298-
*/
299-
protected abstract String getResourceSuffix();
300-
301-
/**
302-
* Get the suffixes to append to {@link ApplicationContext} resource
303-
* locations when detecting default locations.
304-
*
285+
* Get the suffixes to append to {@link ApplicationContext} resource locations
286+
* when detecting default locations.
305287
* <p>The default implementation simply wraps the value returned by
306288
* {@link #getResourceSuffix()} in a single-element array, but this
307289
* can be overridden by subclasses in order to support multiple suffixes.
308-
*
309290
* @return the resource suffixes; never {@code null} or empty
310291
* @since 4.1
311292
* @see #generateDefaultLocations(Class)
312293
*/
313294
protected String[] getResourceSuffixes() {
314-
return new String[] { getResourceSuffix() };
295+
return new String[] {getResourceSuffix()};
315296
}
316297

298+
/**
299+
* Get the suffix to append to {@link ApplicationContext} resource locations
300+
* when detecting default locations.
301+
* <p>Subclasses must provide an implementation of this method that returns
302+
* a single suffix. Alternatively subclasses may provide a <em>no-op</em>
303+
* implementation of this method and override {@link #getResourceSuffixes()}
304+
* in order to provide multiple custom suffixes.
305+
* @return the resource suffix; never {@code null} or empty
306+
* @since 2.5
307+
* @see #generateDefaultLocations(Class)
308+
* @see #getResourceSuffixes()
309+
*/
310+
protected abstract String getResourceSuffix();
311+
317312
}

spring-web/src/main/java/org/springframework/web/context/ContextLoader.java

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -49,19 +49,18 @@
4949
* Performs the actual initialization work for the root application context.
5050
* Called by {@link ContextLoaderListener}.
5151
*
52-
* <p>Looks for a {@link #CONTEXT_CLASS_PARAM "contextClass"} parameter
53-
* at the {@code web.xml} context-param level to specify the context
54-
* class type, falling back to the default of
55-
* {@link org.springframework.web.context.support.XmlWebApplicationContext}
52+
* <p>Looks for a {@link #CONTEXT_CLASS_PARAM "contextClass"} parameter at the
53+
* {@code web.xml} context-param level to specify the context class type, falling
54+
* back to {@link org.springframework.web.context.support.XmlWebApplicationContext}
5655
* if not found. With the default ContextLoader implementation, any context class
57-
* specified needs to implement the ConfigurableWebApplicationContext interface.
56+
* specified needs to implement the {@link ConfigurableWebApplicationContext} interface.
5857
*
59-
* <p>Processes a {@link #CONFIG_LOCATION_PARAM "contextConfigLocation"}
60-
* context-param and passes its value to the context instance, parsing it into
61-
* potentially multiple file paths which can be separated by any number of
62-
* commas and spaces, e.g. "WEB-INF/applicationContext1.xml,
63-
* WEB-INF/applicationContext2.xml". Ant-style path patterns are supported as well,
64-
* e.g. "WEB-INF/*Context.xml,WEB-INF/spring*.xml" or "WEB-INF/&#42;&#42;/*Context.xml".
58+
* <p>Processes a {@link #CONFIG_LOCATION_PARAM "contextConfigLocation"} context-param
59+
* and passes its value to the context instance, parsing it into potentially multiple
60+
* file paths which can be separated by any number of commas and spaces, e.g.
61+
* "WEB-INF/applicationContext1.xml, WEB-INF/applicationContext2.xml".
62+
* Ant-style path patterns are supported as well, e.g.
63+
* "WEB-INF/*Context.xml,WEB-INF/spring*.xml" or "WEB-INF/&#42;&#42;/*Context.xml".
6564
* If not explicitly specified, the context implementation is supposed to use a
6665
* default location (with XmlWebApplicationContext: "/WEB-INF/applicationContext.xml").
6766
*
@@ -70,10 +69,9 @@
7069
* Spring's default ApplicationContext implementations. This can be leveraged
7170
* to deliberately override certain bean definitions via an extra XML file.
7271
*
73-
* <p>Above and beyond loading the root application context, this class
74-
* can optionally load or obtain and hook up a shared parent context to
75-
* the root application context. See the
76-
* {@link #loadParentContext(ServletContext)} method for more information.
72+
* <p>Above and beyond loading the root application context, this class can optionally
73+
* load or obtain and hook up a shared parent context to the root application context.
74+
* See the {@link #loadParentContext(ServletContext)} method for more information.
7775
*
7876
* <p>As of Spring 3.1, {@code ContextLoader} supports injecting the root web
7977
* application context via the {@link #ContextLoader(WebApplicationContext)}
@@ -470,11 +468,11 @@ protected void customizeContext(ServletContext sc, ConfigurableWebApplicationCon
470468
for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) {
471469
Class<?> initializerContextClass =
472470
GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
473-
if (initializerContextClass != null) {
474-
Assert.isAssignable(initializerContextClass, wac.getClass(), String.format(
475-
"Could not add context initializer [%s] since its generic parameter [%s] " +
471+
if (initializerContextClass != null && !initializerContextClass.isInstance(wac)) {
472+
throw new ApplicationContextException(String.format(
473+
"Could not apply context initializer [%s] since its generic parameter [%s] " +
476474
"is not assignable from the type of application context used by this " +
477-
"context loader [%s]: ", initializerClass.getName(), initializerContextClass.getName(),
475+
"context loader: [%s]", initializerClass.getName(), initializerContextClass.getName(),
478476
wac.getClass().getName()));
479477
}
480478
this.contextInitializers.add(BeanUtils.instantiateClass(initializerClass));

spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -43,7 +43,6 @@
4343
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
4444
import org.springframework.core.env.ConfigurableEnvironment;
4545
import org.springframework.http.HttpMethod;
46-
import org.springframework.util.Assert;
4746
import org.springframework.util.ClassUtils;
4847
import org.springframework.util.ObjectUtils;
4948
import org.springframework.util.StringUtils;
@@ -738,17 +737,17 @@ private ApplicationContextInitializer<ConfigurableApplicationContext> loadInitia
738737
Class<?> initializerClass = ClassUtils.forName(className, wac.getClassLoader());
739738
Class<?> initializerContextClass =
740739
GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
741-
if (initializerContextClass != null) {
742-
Assert.isAssignable(initializerContextClass, wac.getClass(), String.format(
743-
"Could not add context initializer [%s] since its generic parameter [%s] " +
740+
if (initializerContextClass != null && !initializerContextClass.isInstance(wac)) {
741+
throw new ApplicationContextException(String.format(
742+
"Could not apply context initializer [%s] since its generic parameter [%s] " +
744743
"is not assignable from the type of application context used by this " +
745-
"framework servlet [%s]: ", initializerClass.getName(), initializerContextClass.getName(),
744+
"framework servlet: [%s]", initializerClass.getName(), initializerContextClass.getName(),
746745
wac.getClass().getName()));
747746
}
748747
return BeanUtils.instantiateClass(initializerClass, ApplicationContextInitializer.class);
749748
}
750-
catch (Exception ex) {
751-
throw new IllegalArgumentException(String.format("Could not instantiate class [%s] specified " +
749+
catch (ClassNotFoundException ex) {
750+
throw new ApplicationContextException(String.format("Could not load class [%s] specified " +
752751
"via 'contextInitializerClasses' init-param", className), ex);
753752
}
754753
}

spring-webmvc/src/test/java/org/springframework/web/context/ContextLoaderTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -225,7 +225,7 @@ public void testContextLoaderListenerWithUnknownContextInitializer() {
225225
listener.contextInitialized(new ServletContextEvent(sc));
226226
fail("expected exception");
227227
}
228-
catch (IllegalArgumentException ex) {
228+
catch (ApplicationContextException ex) {
229229
assertTrue(ex.getMessage().contains("not assignable"));
230230
}
231231
}

0 commit comments

Comments
 (0)