Allow ACI classes access to servlet context params

Prior to this commit, StandardServletEnvironment's servlet context
PropertySource remained stubbed out until it the ServletContext became
available and could be replaced during the refresh() of its enclosing
WebApplicationContext. This behavior is acceptable in most cases.
However, if the user has declared an ApplicationContextInitializer that
attempts to access servlet context-params via the Environment API, this
result in a kind of 'false negative', i.e. the context-param key and
value are actually present in the ServletContext, but the PropertySource
representing servlet context params is still a stub at this point,
meaning that it returns an empty result in all cases.

With this change, WebApplicationContextUtils#initServletPropertySources
is invoked eagerly by the ContextLoader if any ACI classes have been
declared. This swaps out the servlet context property source stub for
the real thing just in time for ACIs to use it if necessary.

Extra guard logic has been added to #initServletPropertySources to
ensure idempotency -- once the stub has been replaced, the method
never attempts the replacement again, e.g. during the normal context
refresh() when this method will be called again.

Issue: SPR-8991
This commit is contained in:
Chris Beams 2012-02-04 21:53:20 +01:00
parent 856b77bc31
commit 025b8abfaf
3 changed files with 43 additions and 6 deletions

View File

@ -17,6 +17,7 @@
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;
@ -131,6 +132,19 @@ public final class ContextLoaderTests {
assertThat(wac.getServletContext().getAttribute("initialized"), notNullValue());
}
@Test
public void testRegisteredContextInitializerCanAccessServletContextParamsViaEnvironment() {
MockServletContext sc = new MockServletContext("");
// config file doesn't matter. just a placeholder
sc.addInitParameter(ContextLoader.CONFIG_LOCATION_PARAM,
"/org/springframework/web/context/WEB-INF/empty-context.xml");
sc.addInitParameter("someProperty", "someValue");
sc.addInitParameter(ContextLoader.CONTEXT_INITIALIZER_CLASSES_PARAM, EnvApplicationContextInitializer.class.getName());
ContextLoaderListener listener = new ContextLoaderListener();
listener.contextInitialized(new ServletContextEvent(sc));
}
@Test
public void testContextLoaderListenerWithUnkownContextInitializer() {
MockServletContext sc = new MockServletContext("");
@ -324,6 +338,15 @@ public final class ContextLoaderTests {
}
}
private static class EnvApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableWebApplicationContext> {
public void initialize(ConfigurableWebApplicationContext applicationContext) {
// test that ApplicationContextInitializers can access ServletContext properties
// via the environment (SPR-8991)
String value = applicationContext.getEnvironment().getRequiredProperty("someProperty");
assertThat(value, is("someValue"));
}
}
private static interface UnknownApplicationContext extends ConfigurableApplicationContext {
void unheardOf();
}

View File

@ -44,6 +44,7 @@ import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.support.WebApplicationContextUtils;
/**
* Performs the actual initialization work for the root application context.
@ -487,6 +488,13 @@ public class ContextLoader {
Collections.sort(initializerInstances, new AnnotationAwareOrderComparator());
// eagerly attempt to initialize servlet property sources in case initializers
// below depend on accessing context-params via the Environment API. Note that
// depending on application context implementation, this initialization will be
// attempted again during context refresh.
WebApplicationContextUtils.initServletPropertySources(
applicationContext.getEnvironment().getPropertySources(), servletContext);
for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : initializerInstances) {
initializer.initialize(applicationContext);
}

View File

@ -16,6 +16,9 @@
package org.springframework.web.context.support;
import static org.springframework.web.context.support.StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME;
import static org.springframework.web.context.support.StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME;
import java.io.Serializable;
import java.util.Collections;
import java.util.Enumeration;
@ -32,6 +35,7 @@ import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource.StubPropertySource;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.web.context.ConfigurableWebApplicationContext;
@ -247,13 +251,15 @@ public abstract class WebApplicationContextUtils {
public static void initServletPropertySources(
MutablePropertySources propertySources, ServletContext servletContext, ServletConfig servletConfig) {
Assert.notNull(propertySources, "propertySources must not be null");
if(servletContext != null && propertySources.contains(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME)) {
propertySources.replace(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME,
new ServletContextPropertySource(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME, servletContext));
if(servletContext != null &&
propertySources.contains(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME) &&
propertySources.get(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME) instanceof StubPropertySource) {
propertySources.replace(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME, new ServletContextPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME, servletContext));
}
if(servletConfig != null && propertySources.contains(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME)) {
propertySources.replace(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME,
new ServletConfigPropertySource(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME, servletConfig));
if(servletConfig != null &&
propertySources.contains(SERVLET_CONFIG_PROPERTY_SOURCE_NAME) &&
propertySources.get(SERVLET_CONFIG_PROPERTY_SOURCE_NAME) instanceof StubPropertySource) {
propertySources.replace(SERVLET_CONFIG_PROPERTY_SOURCE_NAME, new ServletConfigPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME, servletConfig));
}
}