From 91881ff036aec38bf97d2a6159269bf2db4d0ac3 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 15 Jan 2014 22:21:27 +0100 Subject: [PATCH] Introduced "globalInitializerClasses" next to the existing "contextInitializerClasses", applying to FrameworkServlets as well Issue: SPR-11314 --- .../web/context/ContextLoader.java | 196 ++++++++++-------- .../web/servlet/FrameworkServlet.java | 165 ++++++++------- .../web/context/ContextLoaderTests.java | 95 ++++++--- 3 files changed, 266 insertions(+), 190 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/context/ContextLoader.java b/spring-web/src/main/java/org/springframework/web/context/ContextLoader.java index 9f45686fefd..16da9161d32 100644 --- a/spring-web/src/main/java/org/springframework/web/context/ContextLoader.java +++ b/spring-web/src/main/java/org/springframework/web/context/ContextLoader.java @@ -77,8 +77,8 @@ import org.springframework.util.StringUtils; * *

As of Spring 3.1, {@code ContextLoader} supports injecting the root web * application context via the {@link #ContextLoader(WebApplicationContext)} - * constructor, allowing for programmatic configuration in Servlet 3.0+ environments. See - * {@link org.springframework.web.WebApplicationInitializer} for usage examples. + * constructor, allowing for programmatic configuration in Servlet 3.0+ environments. + * See {@link org.springframework.web.WebApplicationInitializer} for usage examples. * * @author Juergen Hoeller * @author Colin Sampaleanu @@ -90,26 +90,12 @@ import org.springframework.util.StringUtils; */ public class ContextLoader { - /** - * Config param for the root WebApplicationContext implementation class to use: {@value} - * @see #determineContextClass(ServletContext) - * @see #createWebApplicationContext(ServletContext, ApplicationContext) - */ - public static final String CONTEXT_CLASS_PARAM = "contextClass"; - /** * Config param for the root WebApplicationContext id, * to be used as serialization id for the underlying BeanFactory: {@value} */ public static final String CONTEXT_ID_PARAM = "contextId"; - /** - * Config param for which {@link ApplicationContextInitializer} classes to use - * for initializing the web application context: {@value} - * @see #customizeContext(ServletContext, ConfigurableWebApplicationContext) - */ - public static final String CONTEXT_INITIALIZER_CLASSES_PARAM = "contextInitializerClasses"; - /** * Name of servlet context parameter (i.e., {@value}) that can specify the * config location for the root context, falling back to the implementation's @@ -118,6 +104,27 @@ public class ContextLoader { */ public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation"; + /** + * Config param for the root WebApplicationContext implementation class to use: {@value} + * @see #determineContextClass(ServletContext) + * @see #createWebApplicationContext(ServletContext, ApplicationContext) + */ + public static final String CONTEXT_CLASS_PARAM = "contextClass"; + + /** + * Config param for {@link ApplicationContextInitializer} classes to use + * for initializing the root web application context: {@value} + * @see #customizeContext(ServletContext, ConfigurableWebApplicationContext) + */ + public static final String CONTEXT_INITIALIZER_CLASSES_PARAM = "contextInitializerClasses"; + + /** + * Config param for global {@link ApplicationContextInitializer} classes to use + * for initializing all web application contexts in the current application: {@value} + * @see #customizeContext(ServletContext, ConfigurableWebApplicationContext) + */ + public static final String GLOBAL_INITIALIZER_CLASSES_PARAM = "globalInitializerClasses"; + /** * Optional servlet context parameter (i.e., "{@code locatorFactorySelector}") * used only when obtaining a parent context using the default implementation @@ -146,6 +153,12 @@ public class ContextLoader { */ public static final String LOCATOR_FACTORY_KEY_PARAM = "parentContextKey"; + /** + * Any number of these characters are considered delimiters between + * multiple values in a single init-param String value. + */ + private static final String INIT_PARAM_DELIMITERS = ",; \t\n"; + /** * Name of the class path resource (relative to the ContextLoader class) * that defines ContextLoader's default strategy names. @@ -373,14 +386,71 @@ public class ContextLoader { } wac.setServletContext(sc); - String initParameter = sc.getInitParameter(CONFIG_LOCATION_PARAM); - if (initParameter != null) { - wac.setConfigLocation(initParameter); + String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM); + if (configLocationParam != null) { + wac.setConfigLocation(configLocationParam); } + + // The wac environment's #initPropertySources will be called in any case when the context + // is refreshed; do it eagerly here to ensure servlet property sources are in place for + // use in any post-processing or initialization that occurs below prior to #refresh + ConfigurableEnvironment env = wac.getEnvironment(); + if (env instanceof ConfigurableWebEnvironment) { + ((ConfigurableWebEnvironment) env).initPropertySources(sc, null); + } + customizeContext(sc, wac); wac.refresh(); } + /** + * Customize the {@link ConfigurableWebApplicationContext} created by this + * ContextLoader after config locations have been supplied to the context + * but before the context is refreshed. + *

The default implementation {@linkplain #determineContextInitializerClasses(ServletContext) + * determines} what (if any) context initializer classes have been specified through + * {@linkplain #CONTEXT_INITIALIZER_CLASSES_PARAM context init parameters} and + * {@linkplain ApplicationContextInitializer#initialize invokes each} with the + * given web application context. + *

Any {@code ApplicationContextInitializers} implementing + * {@link org.springframework.core.Ordered Ordered} or marked with @{@link + * org.springframework.core.annotation.Order Order} will be sorted appropriately. + * @param sc the current servlet context + * @param wac the newly created application context + * @see #createWebApplicationContext(ServletContext, ApplicationContext) + * @see #CONTEXT_INITIALIZER_CLASSES_PARAM + * @see ApplicationContextInitializer#initialize(ConfigurableApplicationContext) + */ + protected void customizeContext(ServletContext sc, ConfigurableWebApplicationContext wac) { + List>> initializerClasses = + determineContextInitializerClasses(sc); + if (initializerClasses.isEmpty()) { + // no ApplicationContextInitializers have been declared -> nothing to do + return; + } + + ArrayList> initializerInstances = + new ArrayList>(); + + for (Class> 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] " + + "is not assignable from the type of application context used by this " + + "context loader [%s]: ", initializerClass.getName(), initializerContextClass.getName(), + wac.getClass().getName())); + } + initializerInstances.add(BeanUtils.instantiateClass(initializerClass)); + } + + AnnotationAwareOrderComparator.sort(initializerInstances); + for (ApplicationContextInitializer initializer : initializerInstances) { + initializer.initialize(wac); + } + } + /** * Return the WebApplicationContext implementation class to use, either the * default XmlWebApplicationContext or a custom context class if specified. @@ -418,80 +488,38 @@ public class ContextLoader { * @param servletContext current servlet context * @see #CONTEXT_INITIALIZER_CLASSES_PARAM */ - @SuppressWarnings("unchecked") protected List>> determineContextInitializerClasses(ServletContext servletContext) { - String classNames = servletContext.getInitParameter(CONTEXT_INITIALIZER_CLASSES_PARAM); + List>> classes = - new ArrayList>>(); - if (classNames != null) { - for (String className : StringUtils.tokenizeToStringArray(classNames, ",")) { - try { - Class clazz = ClassUtils.forName(className, ClassUtils.getDefaultClassLoader()); - Assert.isAssignable(ApplicationContextInitializer.class, clazz, - "class [" + className + "] must implement ApplicationContextInitializer"); - classes.add((Class>)clazz); - } - catch (ClassNotFoundException ex) { - throw new ApplicationContextException( - "Failed to load context initializer class [" + className + "]", ex); - } + new ArrayList>>(); + + String globalClassNames = servletContext.getInitParameter(GLOBAL_INITIALIZER_CLASSES_PARAM); + if (globalClassNames != null) { + for (String className : StringUtils.tokenizeToStringArray(globalClassNames, INIT_PARAM_DELIMITERS)) { + classes.add(loadInitializerClass(className)); } } + + String localClassNames = servletContext.getInitParameter(CONTEXT_INITIALIZER_CLASSES_PARAM); + if (localClassNames != null) { + for (String className : StringUtils.tokenizeToStringArray(localClassNames, INIT_PARAM_DELIMITERS)) { + classes.add(loadInitializerClass(className)); + } + } + return classes; } - /** - * Customize the {@link ConfigurableWebApplicationContext} created by this - * ContextLoader after config locations have been supplied to the context - * but before the context is refreshed. - *

The default implementation {@linkplain #determineContextInitializerClasses(ServletContext) - * determines} what (if any) context initializer classes have been specified through - * {@linkplain #CONTEXT_INITIALIZER_CLASSES_PARAM context init parameters} and - * {@linkplain ApplicationContextInitializer#initialize invokes each} with the - * given web application context. - *

Any {@code ApplicationContextInitializers} implementing - * {@link org.springframework.core.Ordered Ordered} or marked with @{@link - * org.springframework.core.annotation.Order Order} will be sorted appropriately. - * @param servletContext the current servlet context - * @param applicationContext the newly created application context - * @see #createWebApplicationContext(ServletContext, ApplicationContext) - * @see #CONTEXT_INITIALIZER_CLASSES_PARAM - * @see ApplicationContextInitializer#initialize(ConfigurableApplicationContext) - */ - protected void customizeContext(ServletContext servletContext, ConfigurableWebApplicationContext applicationContext) { - List>> initializerClasses = - determineContextInitializerClasses(servletContext); - if (initializerClasses.isEmpty()) { - // no ApplicationContextInitializers have been declared -> nothing to do - return; + @SuppressWarnings("unchecked") + private Class> loadInitializerClass(String className) { + try { + Class clazz = ClassUtils.forName(className, ClassUtils.getDefaultClassLoader()); + Assert.isAssignable(ApplicationContextInitializer.class, clazz); + return (Class>) clazz; } - - Class contextClass = applicationContext.getClass(); - ArrayList> initializerInstances = - new ArrayList>(); - - for (Class> initializerClass : initializerClasses) { - Class initializerContextClass = - GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class); - if (initializerContextClass != null) { - Assert.isAssignable(initializerContextClass, contextClass, String.format( - "Could not add context initializer [%s] as 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(BeanUtils.instantiateClass(initializerClass)); - } - - ConfigurableEnvironment env = applicationContext.getEnvironment(); - if (env instanceof ConfigurableWebEnvironment) { - ((ConfigurableWebEnvironment) env).initPropertySources(servletContext, null); - } - - AnnotationAwareOrderComparator.sort(initializerInstances); - for (ApplicationContextInitializer initializer : initializerInstances) { - initializer.initialize(applicationContext); + catch (ClassNotFoundException ex) { + throw new ApplicationContextException("Failed to load context initializer class [" + className + "]", ex); } } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java index 6dde6d8dc1d..6b2ffa4f079 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java @@ -20,7 +20,6 @@ import java.io.IOException; import java.security.Principal; import java.util.ArrayList; import java.util.concurrent.Callable; - import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; @@ -40,14 +39,17 @@ import org.springframework.context.event.SourceFilteringListener; import org.springframework.context.i18n.LocaleContext; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.context.i18n.SimpleLocaleContext; +import org.springframework.core.GenericTypeResolver; import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.context.ConfigurableWebApplicationContext; import org.springframework.web.context.ConfigurableWebEnvironment; +import org.springframework.web.context.ContextLoader; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.RequestAttributes; @@ -175,6 +177,13 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic /** Explicit context config location */ private String contextConfigLocation; + /** Actual ApplicationContextInitializer instances to apply to the context */ + private final ArrayList> contextInitializers = + new ArrayList>(); + + /** Comma-delimited ApplicationContextInitializer class names set through init param */ + private String contextInitializerClasses; + /** Should we publish the context as a ServletContext attribute? */ private boolean publishContext = true; @@ -199,13 +208,6 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic /** Flag used to detect whether onRefresh has already been called */ private boolean refreshEventReceived = false; - /** Comma-delimited ApplicationContextInitializer classnames set through init param */ - private String contextInitializerClasses; - - /** Actual ApplicationContextInitializer instances to apply to the context */ - private ArrayList> contextInitializers = - new ArrayList>(); - /** * Create a new {@code FrameworkServlet} that will create its own internal web @@ -339,29 +341,6 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic return (this.namespace != null ? this.namespace : getServletName() + DEFAULT_NAMESPACE_SUFFIX); } - /** - * Specify the set of fully-qualified {@link ApplicationContextInitializer} class - * names, per the optional "contextInitializerClasses" servlet init-param. - * @see #configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext) - * @see #applyInitializers(ConfigurableApplicationContext) - */ - public void setContextInitializerClasses(String contextInitializerClasses) { - this.contextInitializerClasses = contextInitializerClasses; - } - - /** - * Specify which {@link ApplicationContextInitializer} instances should be used - * to initialize the application context used by this {@code FrameworkServlet}. - * @see #configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext) - * @see #applyInitializers(ConfigurableApplicationContext) - */ - @SuppressWarnings("unchecked") - public void setContextInitializers(ApplicationContextInitializer... contextInitializers) { - for (ApplicationContextInitializer initializer : contextInitializers) { - this.contextInitializers.add(initializer); - } - } - /** * Set the context config location explicitly, instead of relying on the default * location built from the namespace. This location string can consist of @@ -378,6 +357,28 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic return this.contextConfigLocation; } + /** + * Specify which {@link ApplicationContextInitializer} instances should be used + * to initialize the application context used by this {@code FrameworkServlet}. + * @see #configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext) + * @see #applyInitializers(ConfigurableApplicationContext) + */ + public void setContextInitializers(ApplicationContextInitializer... contextInitializers) { + for (ApplicationContextInitializer initializer : contextInitializers) { + this.contextInitializers.add(initializer); + } + } + + /** + * Specify the set of fully-qualified {@link ApplicationContextInitializer} class + * names, per the optional "contextInitializerClasses" servlet init-param. + * @see #configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext) + * @see #applyInitializers(ConfigurableApplicationContext) + */ + public void setContextInitializerClasses(String contextInitializerClasses) { + this.contextInitializerClasses = contextInitializerClasses; + } + /** * Set whether to publish this servlet's context as a ServletContext attribute, * available to all objects in the web container. Default is "true". @@ -627,19 +628,16 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic wac.setNamespace(getNamespace()); wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener())); - // the wac environment's #initPropertySources will be called in any case when - // the context is refreshed; do it eagerly here to ensure servlet property sources - // are in place for use in any post-processing or initialization that occurs - // below prior to #refresh + // The wac environment's #initPropertySources will be called in any case when the context + // is refreshed; do it eagerly here to ensure servlet property sources are in place for + // use in any post-processing or initialization that occurs below prior to #refresh ConfigurableEnvironment env = wac.getEnvironment(); if (env instanceof ConfigurableWebEnvironment) { - ((ConfigurableWebEnvironment)env).initPropertySources(getServletContext(), getServletConfig()); + ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig()); } postProcessWebApplicationContext(wac); - applyInitializers(wac); - wac.refresh(); } @@ -657,43 +655,6 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic return createWebApplicationContext((ApplicationContext) parent); } - /** - * Delegate the WebApplicationContext before it is refreshed to any - * {@link ApplicationContextInitializer} instances specified by the - * "contextInitializerClasses" servlet init-param. - *

See also {@link #postProcessWebApplicationContext}, which is designed to allow - * subclasses (as opposed to end-users) to modify the application context, and is - * called immediately after this method. - * @param wac the configured WebApplicationContext (not refreshed yet) - * @see #createWebApplicationContext - * @see #postProcessWebApplicationContext - * @see ConfigurableApplicationContext#refresh() - */ - @SuppressWarnings("unchecked") - protected void applyInitializers(ConfigurableApplicationContext wac) { - if (this.contextInitializerClasses != null) { - String[] initializerClassNames = - StringUtils.tokenizeToStringArray(this.contextInitializerClasses, INIT_PARAM_DELIMITERS); - for (String initializerClassName : initializerClassNames) { - ApplicationContextInitializer initializer; - try { - Class initializerClass = ClassUtils.forName(initializerClassName, wac.getClassLoader()); - initializer = BeanUtils.instantiateClass(initializerClass, ApplicationContextInitializer.class); - } - catch (Exception ex) { - throw new IllegalArgumentException( - String.format("Could not instantiate class [%s] specified via " + - "'contextInitializerClasses' init-param", initializerClassName), ex); - } - this.contextInitializers.add(initializer); - } - } - AnnotationAwareOrderComparator.sort(this.contextInitializers); - for (ApplicationContextInitializer initializer : this.contextInitializers) { - initializer.initialize(wac); - } - } - /** * Post-process the given WebApplicationContext before it is refreshed * and activated as context for this servlet. @@ -711,6 +672,60 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic protected void postProcessWebApplicationContext(ConfigurableWebApplicationContext wac) { } + /** + * Delegate the WebApplicationContext before it is refreshed to any + * {@link ApplicationContextInitializer} instances specified by the + * "contextInitializerClasses" servlet init-param. + *

See also {@link #postProcessWebApplicationContext}, which is designed to allow + * subclasses (as opposed to end-users) to modify the application context, and is + * called immediately before this method. + * @param wac the configured WebApplicationContext (not refreshed yet) + * @see #createWebApplicationContext + * @see #postProcessWebApplicationContext + * @see ConfigurableApplicationContext#refresh() + */ + protected void applyInitializers(ConfigurableApplicationContext wac) { + String globalClassNames = getServletContext().getInitParameter(ContextLoader.GLOBAL_INITIALIZER_CLASSES_PARAM); + if (globalClassNames != null) { + for (String className : StringUtils.tokenizeToStringArray(globalClassNames, INIT_PARAM_DELIMITERS)) { + this.contextInitializers.add(loadInitializer(className, wac)); + } + } + + if (this.contextInitializerClasses != null) { + for (String className : StringUtils.tokenizeToStringArray(this.contextInitializerClasses, INIT_PARAM_DELIMITERS)) { + this.contextInitializers.add(loadInitializer(className, wac)); + } + } + + AnnotationAwareOrderComparator.sort(this.contextInitializers); + for (ApplicationContextInitializer initializer : this.contextInitializers) { + initializer.initialize(wac); + } + } + + @SuppressWarnings("unchecked") + private ApplicationContextInitializer loadInitializer( + String className, ConfigurableApplicationContext wac) { + try { + 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] " + + "is not assignable from the type of application context used by this " + + "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 " + + "via 'contextInitializerClasses' init-param", className), ex); + } + } + /** * Return the ServletContext attribute name for this servlet's WebApplicationContext. *

The default implementation returns diff --git a/spring-webmvc/src/test/java/org/springframework/web/context/ContextLoaderTests.java b/spring-webmvc/src/test/java/org/springframework/web/context/ContextLoaderTests.java index 15a7e36fea4..b8e8630a5df 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/context/ContextLoaderTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/context/ContextLoaderTests.java @@ -16,30 +16,17 @@ package org.springframework.web.context; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.notNullValue; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - import java.io.FileNotFoundException; import java.io.IOException; - import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import org.junit.Test; + import org.springframework.beans.BeansException; -import org.springframework.tests.sample.beans.TestBean; import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.BeanDefinitionStoreException; -import org.springframework.tests.sample.beans.LifecycleBean; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextException; @@ -50,11 +37,17 @@ import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.PropertySource; import org.springframework.mock.web.test.MockServletConfig; import org.springframework.mock.web.test.MockServletContext; +import org.springframework.tests.sample.beans.LifecycleBean; +import org.springframework.tests.sample.beans.TestBean; import org.springframework.util.StringUtils; +import org.springframework.web.context.support.WebApplicationContextUtils; import org.springframework.web.context.support.XmlWebApplicationContext; import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.SimpleWebApplicationContext; +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + /** * Tests for {@link ContextLoader} and {@link ContextLoaderListener}. * @@ -70,14 +63,14 @@ public final class ContextLoaderTests { public void testContextLoaderListenerWithDefaultContext() { MockServletContext sc = new MockServletContext(""); sc.addInitParameter(ContextLoader.CONFIG_LOCATION_PARAM, - "/org/springframework/web/context/WEB-INF/applicationContext.xml " - + "/org/springframework/web/context/WEB-INF/context-addition.xml"); + "/org/springframework/web/context/WEB-INF/applicationContext.xml " + + "/org/springframework/web/context/WEB-INF/context-addition.xml"); ServletContextListener listener = new ContextLoaderListener(); ServletContextEvent event = new ServletContextEvent(sc); listener.contextInitialized(event); WebApplicationContext context = (WebApplicationContext) sc.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE); assertTrue("Correct WebApplicationContext exposed in ServletContext", context instanceof XmlWebApplicationContext); - assertTrue(ContextLoader.getCurrentWebApplicationContext() instanceof XmlWebApplicationContext); + assertTrue(WebApplicationContextUtils.getRequiredWebApplicationContext(sc) instanceof XmlWebApplicationContext); LifecycleBean lb = (LifecycleBean) context.getBean("lifecycle"); assertTrue("Has father", context.containsBean("father")); assertTrue("Has rod", context.containsBean("rod")); @@ -88,7 +81,7 @@ public final class ContextLoaderTests { listener.contextDestroyed(event); assertTrue("Destroyed", lb.isDestroyed()); assertNull(sc.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE)); - assertNull(ContextLoader.getCurrentWebApplicationContext()); + assertNull(WebApplicationContextUtils.getWebApplicationContext(sc)); } /** @@ -104,12 +97,12 @@ public final class ContextLoaderTests { final MockServletContext sc = new MockServletContext(""); sc.addInitParameter(ContextLoader.CONFIG_LOCATION_PARAM, "/org/springframework/web/context/WEB-INF/applicationContext.xml"); - final ServletContextListener listener = new ContextLoaderListener() { + ServletContextListener listener = new ContextLoaderListener() { @Override - protected void customizeContext(ServletContext servletContext, ConfigurableWebApplicationContext applicationContext) { - assertNotNull("The ServletContext should not be null.", servletContext); - assertEquals("Verifying that we received the expected ServletContext.", sc, servletContext); - assertFalse("The ApplicationContext should not yet have been refreshed.", applicationContext.isActive()); + protected void customizeContext(ServletContext sc, ConfigurableWebApplicationContext wac) { + assertNotNull("The ServletContext should not be null.", sc); + assertEquals("Verifying that we received the expected ServletContext.", sc, sc); + assertFalse("The ApplicationContext should not yet have been refreshed.", wac.isActive()); buffer.append(expectedContents); } }; @@ -118,16 +111,45 @@ public final class ContextLoaderTests { } @Test - public void testContextLoaderListenerWithRegisteredContextInitializer() { + public void testContextLoaderListenerWithLocalContextInitializers() { MockServletContext sc = new MockServletContext(""); sc.addInitParameter(ContextLoader.CONFIG_LOCATION_PARAM, "org/springframework/web/context/WEB-INF/ContextLoaderTests-acc-context.xml"); - sc.addInitParameter(ContextLoader.CONTEXT_INITIALIZER_CLASSES_PARAM, - StringUtils.arrayToCommaDelimitedString( - new Object[]{TestContextInitializer.class.getName(), TestWebContextInitializer.class.getName()})); + sc.addInitParameter(ContextLoader.CONTEXT_INITIALIZER_CLASSES_PARAM, StringUtils.arrayToCommaDelimitedString( + new Object[] {TestContextInitializer.class.getName(), TestWebContextInitializer.class.getName()})); ContextLoaderListener listener = new ContextLoaderListener(); listener.contextInitialized(new ServletContextEvent(sc)); - WebApplicationContext wac = ContextLoaderListener.getCurrentWebApplicationContext(); + WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(sc); + TestBean testBean = wac.getBean(TestBean.class); + assertThat(testBean.getName(), equalTo("testName")); + assertThat(wac.getServletContext().getAttribute("initialized"), notNullValue()); + } + + @Test + public void testContextLoaderListenerWithGlobalContextInitializers() { + MockServletContext sc = new MockServletContext(""); + sc.addInitParameter(ContextLoader.CONFIG_LOCATION_PARAM, + "org/springframework/web/context/WEB-INF/ContextLoaderTests-acc-context.xml"); + sc.addInitParameter(ContextLoader.GLOBAL_INITIALIZER_CLASSES_PARAM, StringUtils.arrayToCommaDelimitedString( + new Object[] {TestContextInitializer.class.getName(), TestWebContextInitializer.class.getName()})); + ContextLoaderListener listener = new ContextLoaderListener(); + listener.contextInitialized(new ServletContextEvent(sc)); + WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(sc); + TestBean testBean = wac.getBean(TestBean.class); + assertThat(testBean.getName(), equalTo("testName")); + assertThat(wac.getServletContext().getAttribute("initialized"), notNullValue()); + } + + @Test + public void testContextLoaderListenerWithMixedContextInitializers() { + MockServletContext sc = new MockServletContext(""); + sc.addInitParameter(ContextLoader.CONFIG_LOCATION_PARAM, + "org/springframework/web/context/WEB-INF/ContextLoaderTests-acc-context.xml"); + sc.addInitParameter(ContextLoader.CONTEXT_INITIALIZER_CLASSES_PARAM, TestContextInitializer.class.getName()); + sc.addInitParameter(ContextLoader.GLOBAL_INITIALIZER_CLASSES_PARAM, TestWebContextInitializer.class.getName()); + ContextLoaderListener listener = new ContextLoaderListener(); + listener.contextInitialized(new ServletContextEvent(sc)); + WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(sc); TestBean testBean = wac.getBean(TestBean.class); assertThat(testBean.getName(), equalTo("testName")); assertThat(wac.getServletContext().getAttribute("initialized"), notNullValue()); @@ -136,7 +158,7 @@ public final class ContextLoaderTests { @Test public void testRegisteredContextInitializerCanAccessServletContextParamsViaEnvironment() { MockServletContext sc = new MockServletContext(""); - // config file doesn't matter. just a placeholder + // config file doesn't matter - just a placeholder sc.addInitParameter(ContextLoader.CONFIG_LOCATION_PARAM, "/org/springframework/web/context/WEB-INF/empty-context.xml"); @@ -153,12 +175,13 @@ public final class ContextLoaderTests { sc.addInitParameter(ContextLoader.CONFIG_LOCATION_PARAM, "/org/springframework/web/context/WEB-INF/empty-context.xml"); sc.addInitParameter(ContextLoader.CONTEXT_INITIALIZER_CLASSES_PARAM, - StringUtils.arrayToCommaDelimitedString(new Object[]{UnknownContextInitializer.class.getName()})); + StringUtils.arrayToCommaDelimitedString(new Object[] {UnknownContextInitializer.class.getName()})); ContextLoaderListener listener = new ContextLoaderListener(); try { listener.contextInitialized(new ServletContextEvent(sc)); fail("expected exception"); - } catch (IllegalArgumentException ex) { + } + catch (IllegalArgumentException ex) { assertTrue(ex.getMessage().contains("not assignable")); } } @@ -321,7 +344,9 @@ public final class ContextLoaderTests { } } + private static class TestContextInitializer implements ApplicationContextInitializer { + @Override public void initialize(ConfigurableApplicationContext applicationContext) { ConfigurableEnvironment environment = applicationContext.getEnvironment(); @@ -334,7 +359,9 @@ public final class ContextLoaderTests { } } + private static class TestWebContextInitializer implements ApplicationContextInitializer { + @Override public void initialize(ConfigurableWebApplicationContext applicationContext) { ServletContext ctx = applicationContext.getServletContext(); // type-safe access to servlet-specific methods @@ -342,7 +369,9 @@ public final class ContextLoaderTests { } } + private static class EnvApplicationContextInitializer implements ApplicationContextInitializer { + @Override public void initialize(ConfigurableWebApplicationContext applicationContext) { // test that ApplicationContextInitializers can access ServletContext properties @@ -352,11 +381,15 @@ public final class ContextLoaderTests { } } + private static interface UnknownApplicationContext extends ConfigurableApplicationContext { + void unheardOf(); } + private static class UnknownContextInitializer implements ApplicationContextInitializer { + @Override public void initialize(UnknownApplicationContext applicationContext) { applicationContext.unheardOf();