From 23e58aa718f7575dad14d2f00be15ff2896ef57a Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Mon, 12 Dec 2011 14:42:24 +0000 Subject: [PATCH] Preserve programmatically set context config locations Prior to this fix, ContextLoader(Listener)'s would overwrite any value set directly against a WebApplicationContext's #setConfigLocation method. This is a likely scenario when using Spring 3.1's new WebApplicationInitializer support. Now a check is performed to ensure that the ContextLoader init-param value is non-null before doing the overwriting. Added tests to ensure that all expected precedence, overwriting and defaulting of context config locations works as expected. Issue: SPR-8510 --- .../web/context/ContextLoaderTests.java | 1 + .../web/context/ContextLoader.java | 5 +- .../web/context/support/Spr8510Tests.java | 187 ++++++++++++++++++ 3 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 org.springframework.web/src/test/java/org/springframework/web/context/support/Spr8510Tests.java diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/context/ContextLoaderTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/context/ContextLoaderTests.java index e68907bf7d..e228f24f5e 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/context/ContextLoaderTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/context/ContextLoaderTests.java @@ -61,6 +61,7 @@ import org.springframework.web.servlet.SimpleWebApplicationContext; * @author Sam Brannen * @author Chris Beams * @since 12.08.2003 + * @see org.springframework.web.context.support.Spr8510Tests */ public final class ContextLoaderTests { diff --git a/org.springframework.web/src/main/java/org/springframework/web/context/ContextLoader.java b/org.springframework.web/src/main/java/org/springframework/web/context/ContextLoader.java index 6176aad650..1f32562deb 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/context/ContextLoader.java +++ b/org.springframework.web/src/main/java/org/springframework/web/context/ContextLoader.java @@ -376,7 +376,10 @@ public class ContextLoader { wac.setParent(parent); wac.setServletContext(sc); - wac.setConfigLocation(sc.getInitParameter(CONFIG_LOCATION_PARAM)); + String initParameter = sc.getInitParameter(CONFIG_LOCATION_PARAM); + if (initParameter != null) { + wac.setConfigLocation(initParameter); + } customizeContext(sc, wac); wac.refresh(); } diff --git a/org.springframework.web/src/test/java/org/springframework/web/context/support/Spr8510Tests.java b/org.springframework.web/src/test/java/org/springframework/web/context/support/Spr8510Tests.java new file mode 100644 index 0000000000..2a3be43c9d --- /dev/null +++ b/org.springframework.web/src/test/java/org/springframework/web/context/support/Spr8510Tests.java @@ -0,0 +1,187 @@ +/* + * Copyright 2002-2011 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 the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.web.context.support; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import javax.servlet.ServletContextEvent; + +import org.junit.Test; +import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; +import org.springframework.mock.web.MockServletContext; +import org.springframework.web.context.ContextLoader; +import org.springframework.web.context.ContextLoaderListener; + +/** + * Tests the interaction between a WebApplicationContext and ContextLoaderListener with + * regard to config location precedence, overriding and defaulting in programmatic + * configuration use cases, e.g. with Spring 3.1's WebApplicationInitializer. + * + * @author Chris Beams + * @since 3.1 + * @see org.springframework.web.context.ContextLoaderTests + */ +public class Spr8510Tests { + + @Test + public void abstractRefreshableWAC_respectsProgrammaticConfigLocations() { + XmlWebApplicationContext ctx = new XmlWebApplicationContext(); + ctx.setConfigLocation("programmatic.xml"); + ContextLoaderListener cll = new ContextLoaderListener(ctx); + + MockServletContext sc = new MockServletContext(); + + try { + cll.contextInitialized(new ServletContextEvent(sc)); + fail("expected exception"); + } catch (Throwable t) { + // assert that an attempt was made to load the correct XML + assertTrue(t.getMessage(), t.getMessage().endsWith( + "Could not open ServletContext resource [/programmatic.xml]")); + } + } + + /** + * If a contextConfigLocation init-param has been specified for the ContextLoaderListener, + * then it should take precedence. This is generally not a recommended practice, but + * when it does happen, the init-param should be considered more specific than the + * programmatic configuration, given that it still quite possibly externalized in + * hybrid web.xml + WebApplicationInitializer cases. + */ + @Test + public void abstractRefreshableWAC_respectsInitParam_overProgrammaticConfigLocations() { + XmlWebApplicationContext ctx = new XmlWebApplicationContext(); + ctx.setConfigLocation("programmatic.xml"); + ContextLoaderListener cll = new ContextLoaderListener(ctx); + + MockServletContext sc = new MockServletContext(); + sc.addInitParameter(ContextLoader.CONFIG_LOCATION_PARAM, "from-init-param.xml"); + + try { + cll.contextInitialized(new ServletContextEvent(sc)); + fail("expected exception"); + } catch (Throwable t) { + // assert that an attempt was made to load the correct XML + assertTrue(t.getMessage(), t.getMessage().endsWith( + "Could not open ServletContext resource [/from-init-param.xml]")); + } + } + + /** + * If setConfigLocation has not been called explicitly against the application context, + * then fall back to the ContextLoaderListener init-param if present. + */ + @Test + public void abstractRefreshableWAC_fallsBackToInitParam() { + XmlWebApplicationContext ctx = new XmlWebApplicationContext(); + //ctx.setConfigLocation("programmatic.xml"); // nothing set programmatically + ContextLoaderListener cll = new ContextLoaderListener(ctx); + + MockServletContext sc = new MockServletContext(); + sc.addInitParameter(ContextLoader.CONFIG_LOCATION_PARAM, "from-init-param.xml"); + + try { + cll.contextInitialized(new ServletContextEvent(sc)); + fail("expected exception"); + } catch (Throwable t) { + // assert that an attempt was made to load the correct XML + assertTrue(t.getMessage().endsWith( + "Could not open ServletContext resource [/from-init-param.xml]")); + } + } + + /** + * Ensure that any custom default locations are still respected. + */ + @Test + public void customAbstractRefreshableWAC_fallsBackToInitParam() { + XmlWebApplicationContext ctx = new XmlWebApplicationContext() { + @Override + protected String[] getDefaultConfigLocations() { + return new String[] { "/WEB-INF/custom.xml" }; + } + }; + //ctx.setConfigLocation("programmatic.xml"); // nothing set programmatically + ContextLoaderListener cll = new ContextLoaderListener(ctx); + + MockServletContext sc = new MockServletContext(); + sc.addInitParameter(ContextLoader.CONFIG_LOCATION_PARAM, "from-init-param.xml"); + + try { + cll.contextInitialized(new ServletContextEvent(sc)); + fail("expected exception"); + } catch (Throwable t) { + // assert that an attempt was made to load the correct XML + System.out.println(t.getMessage()); + assertTrue(t.getMessage().endsWith( + "Could not open ServletContext resource [/from-init-param.xml]")); + } + } + + /** + * If context config locations have been specified neither against the application + * context nor the context loader listener, then fall back to default values. + */ + @Test + public void abstractRefreshableWAC_fallsBackToConventionBasedNaming() { + XmlWebApplicationContext ctx = new XmlWebApplicationContext(); + //ctx.setConfigLocation("programmatic.xml"); // nothing set programmatically + ContextLoaderListener cll = new ContextLoaderListener(ctx); + + MockServletContext sc = new MockServletContext(); + // no init-param set + //sc.addInitParameter(ContextLoader.CONFIG_LOCATION_PARAM, "from-init-param.xml"); + + try { + cll.contextInitialized(new ServletContextEvent(sc)); + fail("expected exception"); + } catch (Throwable t) { + // assert that an attempt was made to load the correct XML + System.out.println(t.getMessage()); + assertTrue(t.getMessage().endsWith( + "Could not open ServletContext resource [/WEB-INF/applicationContext.xml]")); + } + } + + /** + * Ensure that ContextLoaderListener and GenericWebApplicationContext interact nicely. + */ + @Test + public void genericWAC() { + GenericWebApplicationContext ctx = new GenericWebApplicationContext(); + ContextLoaderListener cll = new ContextLoaderListener(ctx); + + ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(ctx); + scanner.scan("bogus.pkg"); + + cll.contextInitialized(new ServletContextEvent(new MockServletContext())); + } + + /** + * Ensure that ContextLoaderListener and AnnotationConfigApplicationContext interact nicely. + */ + @Test + public void annotationConfigWAC() { + AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); + + ctx.scan("does.not.matter"); + + ContextLoaderListener cll = new ContextLoaderListener(ctx); + cll.contextInitialized(new ServletContextEvent(new MockServletContext())); + } +}