Refined ApplicationContextInitializer assignability exception
This commit is contained in:
parent
a68b910b7c
commit
ca19920d74
|
@ -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"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
|
@ -373,8 +373,8 @@ public abstract class Assert {
|
|||
public static void isAssignable(Class<?> superType, Class<?> subType, String message) {
|
||||
notNull(superType, "Type to check against must not be null");
|
||||
if (subType == null || !superType.isAssignableFrom(subType)) {
|
||||
throw new IllegalArgumentException((StringUtils.hasLength(message) ? message + " " : "")
|
||||
+ subType + " is not assignable to " + superType);
|
||||
throw new IllegalArgumentException((StringUtils.hasLength(message) ? message + " " : "") +
|
||||
subType + " is not assignable to " + superType);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -25,6 +25,7 @@ import org.apache.commons.logging.LogFactory;
|
|||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextException;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
|
@ -86,8 +87,8 @@ public abstract class AbstractContextLoader implements SmartContextLoader {
|
|||
*/
|
||||
@Override
|
||||
public void processContextConfiguration(ContextConfigurationAttributes configAttributes) {
|
||||
String[] processedLocations = processLocations(configAttributes.getDeclaringClass(),
|
||||
configAttributes.getLocations());
|
||||
String[] processedLocations =
|
||||
processLocations(configAttributes.getDeclaringClass(), configAttributes.getLocations());
|
||||
configAttributes.setLocations(processedLocations);
|
||||
}
|
||||
|
||||
|
@ -135,7 +136,9 @@ public abstract class AbstractContextLoader implements SmartContextLoader {
|
|||
@SuppressWarnings("unchecked")
|
||||
private void invokeApplicationContextInitializers(ConfigurableApplicationContext context,
|
||||
MergedContextConfiguration mergedConfig) {
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses = mergedConfig.getContextInitializerClasses();
|
||||
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses =
|
||||
mergedConfig.getContextInitializerClasses();
|
||||
if (initializerClasses.isEmpty()) {
|
||||
// no ApplicationContextInitializers have been declared -> nothing to do
|
||||
return;
|
||||
|
@ -145,13 +148,15 @@ public abstract class AbstractContextLoader implements SmartContextLoader {
|
|||
Class<?> contextClass = context.getClass();
|
||||
|
||||
for (Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>> initializerClass : initializerClasses) {
|
||||
Class<?> initializerContextClass = GenericTypeResolver.resolveTypeArgument(initializerClass,
|
||||
ApplicationContextInitializer.class);
|
||||
Assert.isAssignable(initializerContextClass, contextClass, String.format(
|
||||
"Could not add context initializer [%s] since its generic parameter [%s] "
|
||||
+ "is not assignable from the type of application context used by this "
|
||||
+ "context loader [%s]: ", initializerClass.getName(), initializerContextClass.getName(),
|
||||
contextClass.getName()));
|
||||
Class<?> initializerContextClass =
|
||||
GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
|
||||
if (initializerContextClass != null && !initializerContextClass.isInstance(context)) {
|
||||
throw new ApplicationContextException(String.format(
|
||||
"Could not apply context initializer [%s] since its generic parameter [%s] " +
|
||||
"is not assignable from the type of application context used by this " +
|
||||
"context loader: [%s]", initializerClass.getName(), initializerContextClass.getName(),
|
||||
contextClass.getName()));
|
||||
}
|
||||
initializerInstances.add((ApplicationContextInitializer<ConfigurableApplicationContext>) BeanUtils.instantiateClass(initializerClass));
|
||||
}
|
||||
|
||||
|
@ -161,6 +166,7 @@ public abstract class AbstractContextLoader implements SmartContextLoader {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// --- ContextLoader -------------------------------------------------------
|
||||
|
||||
/**
|
||||
|
@ -171,7 +177,6 @@ public abstract class AbstractContextLoader implements SmartContextLoader {
|
|||
* and the configured {@linkplain #getResourceSuffixes() resource suffixes};
|
||||
* otherwise, the supplied {@code locations} will be
|
||||
* {@linkplain #modifyLocations modified} if necessary and returned.
|
||||
*
|
||||
* @param clazz the class with which the locations are associated: to be
|
||||
* used when generating default locations
|
||||
* @param locations the unmodified locations to use for loading the
|
||||
|
@ -186,30 +191,26 @@ public abstract class AbstractContextLoader implements SmartContextLoader {
|
|||
*/
|
||||
@Override
|
||||
public final String[] processLocations(Class<?> clazz, String... locations) {
|
||||
return (ObjectUtils.isEmpty(locations) && isGenerateDefaultLocations()) ? generateDefaultLocations(clazz)
|
||||
: modifyLocations(clazz, locations);
|
||||
return (ObjectUtils.isEmpty(locations) && isGenerateDefaultLocations()) ?
|
||||
generateDefaultLocations(clazz) : modifyLocations(clazz, locations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the default classpath resource locations array based on the
|
||||
* supplied class.
|
||||
*
|
||||
* <p>For example, if the supplied class is {@code com.example.MyTest},
|
||||
* the generated locations will contain a single string with a value of
|
||||
* {@code "classpath:com/example/MyTest<suffix>"}, where {@code <suffix>}
|
||||
* is the value of the first configured
|
||||
* {@linkplain #getResourceSuffixes() resource suffix} for which the
|
||||
* generated location actually exists in the classpath.
|
||||
*
|
||||
* <p>As of Spring 3.1, the implementation of this method adheres to the
|
||||
* contract defined in the {@link SmartContextLoader} SPI. Specifically,
|
||||
* this method will <em>preemptively</em> verify that the generated default
|
||||
* location actually exists. If it does not exist, this method will log a
|
||||
* warning and return an empty array.
|
||||
*
|
||||
* <p>Subclasses can override this method to implement a different
|
||||
* <em>default location generation</em> strategy.
|
||||
*
|
||||
* @param clazz the class for which the default locations are to be generated
|
||||
* @return an array of default application context resource locations
|
||||
* @since 2.5
|
||||
|
@ -224,23 +225,22 @@ public abstract class AbstractContextLoader implements SmartContextLoader {
|
|||
String resourcePath = ClassUtils.convertClassNameToResourcePath(clazz.getName()) + suffix;
|
||||
String prefixedResourcePath = ResourceUtils.CLASSPATH_URL_PREFIX + resourcePath;
|
||||
ClassPathResource classPathResource = new ClassPathResource(resourcePath);
|
||||
|
||||
if (classPathResource.exists()) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info(String.format("Detected default resource location \"%s\" for test class [%s]",
|
||||
prefixedResourcePath, clazz.getName()));
|
||||
prefixedResourcePath, clazz.getName()));
|
||||
}
|
||||
return new String[] { prefixedResourcePath };
|
||||
return new String[] {prefixedResourcePath};
|
||||
}
|
||||
else if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("Did not detect default resource location for test class [%s]: "
|
||||
+ "%s does not exist", clazz.getName(), classPathResource));
|
||||
logger.debug(String.format("Did not detect default resource location for test class [%s]: " +
|
||||
"%s does not exist", clazz.getName(), classPathResource));
|
||||
}
|
||||
}
|
||||
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info(String.format("Could not detect default resource locations for test class [%s]: "
|
||||
+ "no resource found for suffixes %s.", clazz.getName(), ObjectUtils.nullSafeToString(suffixes)));
|
||||
logger.info(String.format("Could not detect default resource locations for test class [%s]: " +
|
||||
"no resource found for suffixes %s.", clazz.getName(), ObjectUtils.nullSafeToString(suffixes)));
|
||||
}
|
||||
|
||||
return EMPTY_STRING_ARRAY;
|
||||
|
@ -282,15 +282,26 @@ public abstract class AbstractContextLoader implements SmartContextLoader {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get the suffix to append to {@link ApplicationContext} resource
|
||||
* locations when detecting default locations.
|
||||
*
|
||||
* <p>Subclasses must provide an implementation of this method that
|
||||
* returns a single suffix. Alternatively subclasses may provide a
|
||||
* <em>no-op</em> implementation of this method and override
|
||||
* {@link #getResourceSuffixes()} in order to provide multiple custom
|
||||
* suffixes.
|
||||
*
|
||||
* Get the suffixes to append to {@link ApplicationContext} resource locations
|
||||
* when detecting default locations.
|
||||
* <p>The default implementation simply wraps the value returned by
|
||||
* {@link #getResourceSuffix()} in a single-element array, but this
|
||||
* can be overridden by subclasses in order to support multiple suffixes.
|
||||
* @return the resource suffixes; never {@code null} or empty
|
||||
* @since 4.1
|
||||
* @see #generateDefaultLocations(Class)
|
||||
*/
|
||||
protected String[] getResourceSuffixes() {
|
||||
return new String[] {getResourceSuffix()};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the suffix to append to {@link ApplicationContext} resource locations
|
||||
* when detecting default locations.
|
||||
* <p>Subclasses must provide an implementation of this method that returns
|
||||
* a single suffix. Alternatively subclasses may provide a <em>no-op</em>
|
||||
* implementation of this method and override {@link #getResourceSuffixes()}
|
||||
* in order to provide multiple custom suffixes.
|
||||
* @return the resource suffix; never {@code null} or empty
|
||||
* @since 2.5
|
||||
* @see #generateDefaultLocations(Class)
|
||||
|
@ -298,20 +309,4 @@ public abstract class AbstractContextLoader implements SmartContextLoader {
|
|||
*/
|
||||
protected abstract String getResourceSuffix();
|
||||
|
||||
/**
|
||||
* Get the suffixes to append to {@link ApplicationContext} resource
|
||||
* locations when detecting default locations.
|
||||
*
|
||||
* <p>The default implementation simply wraps the value returned by
|
||||
* {@link #getResourceSuffix()} in a single-element array, but this
|
||||
* can be overridden by subclasses in order to support multiple suffixes.
|
||||
*
|
||||
* @return the resource suffixes; never {@code null} or empty
|
||||
* @since 4.1
|
||||
* @see #generateDefaultLocations(Class)
|
||||
*/
|
||||
protected String[] getResourceSuffixes() {
|
||||
return new String[] { getResourceSuffix() };
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -49,19 +49,18 @@ import org.springframework.util.StringUtils;
|
|||
* Performs the actual initialization work for the root application context.
|
||||
* Called by {@link ContextLoaderListener}.
|
||||
*
|
||||
* <p>Looks for a {@link #CONTEXT_CLASS_PARAM "contextClass"} parameter
|
||||
* at the {@code web.xml} context-param level to specify the context
|
||||
* class type, falling back to the default of
|
||||
* {@link org.springframework.web.context.support.XmlWebApplicationContext}
|
||||
* <p>Looks for a {@link #CONTEXT_CLASS_PARAM "contextClass"} parameter at the
|
||||
* {@code web.xml} context-param level to specify the context class type, falling
|
||||
* back to {@link org.springframework.web.context.support.XmlWebApplicationContext}
|
||||
* if not found. With the default ContextLoader implementation, any context class
|
||||
* specified needs to implement the ConfigurableWebApplicationContext interface.
|
||||
* specified needs to implement the {@link ConfigurableWebApplicationContext} interface.
|
||||
*
|
||||
* <p>Processes a {@link #CONFIG_LOCATION_PARAM "contextConfigLocation"}
|
||||
* context-param and passes its value to the context instance, parsing it into
|
||||
* potentially multiple file paths which can be separated by any number of
|
||||
* commas and spaces, e.g. "WEB-INF/applicationContext1.xml,
|
||||
* WEB-INF/applicationContext2.xml". Ant-style path patterns are supported as well,
|
||||
* e.g. "WEB-INF/*Context.xml,WEB-INF/spring*.xml" or "WEB-INF/**/*Context.xml".
|
||||
* <p>Processes a {@link #CONFIG_LOCATION_PARAM "contextConfigLocation"} context-param
|
||||
* and passes its value to the context instance, parsing it into potentially multiple
|
||||
* file paths which can be separated by any number of commas and spaces, e.g.
|
||||
* "WEB-INF/applicationContext1.xml, WEB-INF/applicationContext2.xml".
|
||||
* Ant-style path patterns are supported as well, e.g.
|
||||
* "WEB-INF/*Context.xml,WEB-INF/spring*.xml" or "WEB-INF/**/*Context.xml".
|
||||
* If not explicitly specified, the context implementation is supposed to use a
|
||||
* default location (with XmlWebApplicationContext: "/WEB-INF/applicationContext.xml").
|
||||
*
|
||||
|
@ -70,10 +69,9 @@ import org.springframework.util.StringUtils;
|
|||
* Spring's default ApplicationContext implementations. This can be leveraged
|
||||
* to deliberately override certain bean definitions via an extra XML file.
|
||||
*
|
||||
* <p>Above and beyond loading the root application context, this class
|
||||
* can optionally load or obtain and hook up a shared parent context to
|
||||
* the root application context. See the
|
||||
* {@link #loadParentContext(ServletContext)} method for more information.
|
||||
* <p>Above and beyond loading the root application context, this class can optionally
|
||||
* load or obtain and hook up a shared parent context to the root application context.
|
||||
* See the {@link #loadParentContext(ServletContext)} method for more information.
|
||||
*
|
||||
* <p>As of Spring 3.1, {@code ContextLoader} supports injecting the root web
|
||||
* application context via the {@link #ContextLoader(WebApplicationContext)}
|
||||
|
@ -470,11 +468,11 @@ public class ContextLoader {
|
|||
for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) {
|
||||
Class<?> initializerContextClass =
|
||||
GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
|
||||
if (initializerContextClass != null) {
|
||||
Assert.isAssignable(initializerContextClass, wac.getClass(), String.format(
|
||||
"Could not add context initializer [%s] since its generic parameter [%s] " +
|
||||
if (initializerContextClass != null && !initializerContextClass.isInstance(wac)) {
|
||||
throw new ApplicationContextException(String.format(
|
||||
"Could not apply context initializer [%s] since its generic parameter [%s] " +
|
||||
"is not assignable from the type of application context used by this " +
|
||||
"context loader [%s]: ", initializerClass.getName(), initializerContextClass.getName(),
|
||||
"context loader: [%s]", initializerClass.getName(), initializerContextClass.getName(),
|
||||
wac.getClass().getName()));
|
||||
}
|
||||
this.contextInitializers.add(BeanUtils.instantiateClass(initializerClass));
|
||||
|
|
|
@ -43,7 +43,6 @@ import org.springframework.core.GenericTypeResolver;
|
|||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
@ -740,17 +739,17 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic
|
|||
Class<?> initializerClass = ClassUtils.forName(className, wac.getClassLoader());
|
||||
Class<?> initializerContextClass =
|
||||
GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
|
||||
if (initializerContextClass != null) {
|
||||
Assert.isAssignable(initializerContextClass, wac.getClass(), String.format(
|
||||
"Could not add context initializer [%s] since its generic parameter [%s] " +
|
||||
if (initializerContextClass != null && !initializerContextClass.isInstance(wac)) {
|
||||
throw new ApplicationContextException(String.format(
|
||||
"Could not apply context initializer [%s] since its generic parameter [%s] " +
|
||||
"is not assignable from the type of application context used by this " +
|
||||
"framework servlet [%s]: ", initializerClass.getName(), initializerContextClass.getName(),
|
||||
"framework servlet: [%s]", initializerClass.getName(), initializerContextClass.getName(),
|
||||
wac.getClass().getName()));
|
||||
}
|
||||
return BeanUtils.instantiateClass(initializerClass, ApplicationContextInitializer.class);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalArgumentException(String.format("Could not instantiate class [%s] specified " +
|
||||
catch (ClassNotFoundException ex) {
|
||||
throw new ApplicationContextException(String.format("Could not load class [%s] specified " +
|
||||
"via 'contextInitializerClasses' init-param", className), ex);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -225,7 +225,7 @@ public class ContextLoaderTests {
|
|||
listener.contextInitialized(new ServletContextEvent(sc));
|
||||
fail("expected exception");
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
catch (ApplicationContextException ex) {
|
||||
assertTrue(ex.getMessage().contains("not assignable"));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue