From 5874383ef081bb52a872dd49d63e5b542fbf20ca Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Sat, 26 May 2012 14:04:36 +0300 Subject: [PATCH 1/3] Polish Issue: SPR-9439 --- .../core/env/ConfigurableEnvironment.java | 22 ++++++--------- .../java/org/springframework/util/Assert.java | 4 +-- .../core/env/StandardEnvironmentTests.java | 28 ++++++++----------- .../AbstractApplicationContextTests.java | 18 ++++++++++++ .../XmlWebApplicationContextTests.java | 8 ++++-- 5 files changed, 45 insertions(+), 35 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/env/ConfigurableEnvironment.java b/spring-core/src/main/java/org/springframework/core/env/ConfigurableEnvironment.java index 8e8aad80ea8..38f4c36275c 100644 --- a/spring-core/src/main/java/org/springframework/core/env/ConfigurableEnvironment.java +++ b/spring-core/src/main/java/org/springframework/core/env/ConfigurableEnvironment.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -55,13 +55,14 @@ import java.util.Map; * propertySources.replace(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, mockEnvVars); * * - * When an {@link Environment} is being used by an ApplicationContext, it is important - * that any such PropertySource manipulations be performed before the context's - * {@link org.springframework.context.support.AbstractApplicationContext#refresh() - * refresh()} method is called. This ensures that all property sources are available - * during the container bootstrap process, including use by - * {@linkplain org.springframework.context.support.PropertySourcesPlaceholderConfigurer - * property placeholder configurers}. + * When an {@link Environment} is being used by an {@code ApplicationContext}, it is + * important that any such {@code PropertySource} manipulations be performed + * before the context's {@link + * org.springframework.context.support.AbstractApplicationContext#refresh() refresh()} + * method is called. This ensures that all property sources are available during the + * container bootstrap process, including use by {@linkplain + * org.springframework.context.support.PropertySourcesPlaceholderConfigurer property + * placeholder configurers}. * * * @author Chris Beams @@ -78,7 +79,6 @@ public interface ConfigurableEnvironment extends Environment, ConfigurableProper *

Any existing active profiles will be replaced with the given arguments; call * with zero arguments to clear the current set of active profiles. Use * {@link #addActiveProfile} to add a profile while preserving the existing set. - * * @see #addActiveProfile * @see #setDefaultProfiles * @see org.springframework.context.annotation.Profile @@ -123,12 +123,10 @@ public interface ConfigurableEnvironment extends Environment, ConfigurableProper * Return the value of {@link System#getenv()} if allowed by the current * {@link SecurityManager}, otherwise return a map implementation that will attempt * to access individual keys using calls to {@link System#getenv(String)}. - * *

Note that most {@link Environment} implementations will include this system * environment map as a default {@link PropertySource} to be searched. Therefore, it * is recommended that this method not be used directly unless bypassing other * property sources is expressly intended. - * *

Calls to {@link Map#get(Object)} on the Map returned will never throw * {@link IllegalAccessException}; in cases where the SecurityManager forbids access * to a property, {@code null} will be returned and an INFO-level log message will be @@ -140,12 +138,10 @@ public interface ConfigurableEnvironment extends Environment, ConfigurableProper * Return the value of {@link System#getProperties()} if allowed by the current * {@link SecurityManager}, otherwise return a map implementation that will attempt * to access individual keys using calls to {@link System#getProperty(String)}. - * *

Note that most {@code Environment} implementations will include this system * properties map as a default {@link PropertySource} to be searched. Therefore, it is * recommended that this method not be used directly unless bypassing other property * sources is expressly intended. - * *

Calls to {@link Map#get(Object)} on the Map returned will never throw * {@link IllegalAccessException}; in cases where the SecurityManager forbids access * to a property, {@code null} will be returned and an INFO-level log message will be diff --git a/spring-core/src/main/java/org/springframework/util/Assert.java b/spring-core/src/main/java/org/springframework/util/Assert.java index b78364a9f60..d1ecbe54c1f 100644 --- a/spring-core/src/main/java/org/springframework/util/Assert.java +++ b/spring-core/src/main/java/org/springframework/util/Assert.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2007 the original author or authors. + * Copyright 2002-2012 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 @@ -335,7 +335,7 @@ public abstract class Assert { notNull(type, "Type to check against must not be null"); if (!type.isInstance(obj)) { throw new IllegalArgumentException(message + - "Object of class [" + (obj != null ? obj.getClass().getName() : "null") + + ". Object of class [" + (obj != null ? obj.getClass().getName() : "null") + "] must be an instance of " + type); } } diff --git a/spring-core/src/test/java/org/springframework/core/env/StandardEnvironmentTests.java b/spring-core/src/test/java/org/springframework/core/env/StandardEnvironmentTests.java index 4a5881dcb71..f793eefe61c 100644 --- a/spring-core/src/test/java/org/springframework/core/env/StandardEnvironmentTests.java +++ b/spring-core/src/test/java/org/springframework/core/env/StandardEnvironmentTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -16,22 +16,6 @@ package org.springframework.core.env; -import static java.lang.String.format; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.not; -import static org.hamcrest.CoreMatchers.notNullValue; -import static org.hamcrest.CoreMatchers.nullValue; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.fail; -import static org.junit.matchers.JUnitMatchers.hasItem; -import static org.junit.matchers.JUnitMatchers.hasItems; -import static org.springframework.core.env.AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME; -import static org.springframework.core.env.AbstractEnvironment.DEFAULT_PROFILES_PROPERTY_NAME; -import static org.springframework.core.env.AbstractEnvironment.RESERVED_DEFAULT_PROFILE_NAME; - import java.lang.reflect.Field; import java.security.AccessControlException; import java.security.Permission; @@ -40,8 +24,18 @@ import java.util.Collections; import java.util.Map; import org.junit.Test; + import org.springframework.mock.env.MockPropertySource; +import static java.lang.String.*; + +import static org.hamcrest.CoreMatchers.*; + +import static org.junit.Assert.*; +import static org.junit.matchers.JUnitMatchers.*; + +import static org.springframework.core.env.AbstractEnvironment.*; + /** * Unit tests for {@link StandardEnvironment}. * diff --git a/spring-webmvc/src/test/java/org/springframework/web/context/AbstractApplicationContextTests.java b/spring-webmvc/src/test/java/org/springframework/web/context/AbstractApplicationContextTests.java index eeafcb71055..2541dfe0520 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/context/AbstractApplicationContextTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/context/AbstractApplicationContextTests.java @@ -1,4 +1,21 @@ +/* + * Copyright 2002-2012 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; + import java.util.Locale; import org.springframework.beans.TestBean; @@ -136,6 +153,7 @@ public abstract class AbstractApplicationContextTests extends AbstractListableBe } + @SuppressWarnings("serial") public static class MyEvent extends ApplicationEvent { public MyEvent(Object source) { diff --git a/spring-webmvc/src/test/java/org/springframework/web/context/XmlWebApplicationContextTests.java b/spring-webmvc/src/test/java/org/springframework/web/context/XmlWebApplicationContextTests.java index b20b13220bd..8d6660baf36 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/context/XmlWebApplicationContextTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/context/XmlWebApplicationContextTests.java @@ -16,9 +16,6 @@ package org.springframework.web.context; -import static org.hamcrest.CoreMatchers.sameInstance; -import static org.junit.Assert.assertThat; - import java.util.Locale; import javax.servlet.ServletException; @@ -36,6 +33,10 @@ import org.springframework.context.TestListener; import org.springframework.mock.web.MockServletContext; import org.springframework.web.context.support.XmlWebApplicationContext; +import static org.hamcrest.CoreMatchers.*; + +import static org.junit.Assert.*; + /** * @author Rod Johnson * @author Juergen Hoeller @@ -53,6 +54,7 @@ public class XmlWebApplicationContextTests extends AbstractApplicationContextTes root.addBeanFactoryPostProcessor(new BeanFactoryPostProcessor() { public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { beanFactory.addBeanPostProcessor(new BeanPostProcessor() { + @SuppressWarnings("unchecked") public Object postProcessBeforeInitialization(Object bean, String name) throws BeansException { if (bean instanceof TestBean) { ((TestBean) bean).getFriends().add("myFriend"); From 9fcfd7e827323a0a47161779ff7fcad224b473d4 Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Sat, 26 May 2012 14:09:21 +0300 Subject: [PATCH 2/3] Introduce ConfigurableEnvironment#merge Prior to this change, AbstractApplicationContext#setParent replaced the child context's Environment with the parent's Environment if available. This has the negative effect of potentially changing the type of the child context's Environment, and in any case causes property sources added directly against the child environment to be ignored. This situation could easily occur if a WebApplicationContext child had a non-web ApplicationContext set as its parent. In this case the parent Environment type would (likely) be StandardEnvironment, while the child Environment type would (likely) be StandardServletEnvironment. By directly inheriting the parent environment, critical property sources such as ServletContextPropertySource are lost entirely. This commit introduces the concept of merging an environment through the new ConfigurableEnvironment#merge method. Instead of replacing the child's environment with the parent's, AbstractApplicationContext#setParent now merges property sources as well as active and default profile names from the parent into the child. In this way, distinct environment objects are maintained with specific types and property sources preserved. See #merge Javadoc for additional details. Issue: SPR-9444, SPR-9439 --- .../support/AbstractApplicationContext.java | 15 ++++--- .../core/env/AbstractEnvironment.java | 14 +++++++ .../core/env/ConfigurableEnvironment.java | 20 +++++++++ .../core/env/StandardEnvironmentTests.java | 41 +++++++++++++++++++ .../XmlWebApplicationContextTests.java | 9 +++- 5 files changed, 92 insertions(+), 7 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java b/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java index 23b157f1d8a..8f3be1847d1 100644 --- a/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java +++ b/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java @@ -378,14 +378,19 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader /** * {@inheritDoc} - *

The parent {@linkplain #getEnvironment() environment} is - * delegated to this (child) context if the parent is a - * {@link ConfigurableApplicationContext} implementation. + *

The parent {@linkplain ApplicationContext#getEnvironment() environment} is + * {@linkplain ConfigurableEnvironment#merge(ConfigurableEnvironment) merged} with + * this (child) application context environment if the parent is non-{@code null} and + * its environment is an instance of {@link ConfigurableEnvironment}. + * @see ConfigurableEnvironment#merge(ConfigurableEnvironment) */ public void setParent(ApplicationContext parent) { this.parent = parent; - if (parent instanceof ConfigurableApplicationContext) { - this.setEnvironment(((ConfigurableApplicationContext)parent).getEnvironment()); + if (parent != null) { + Object parentEnvironment = parent.getEnvironment(); + if (parentEnvironment instanceof ConfigurableEnvironment) { + this.environment.merge((ConfigurableEnvironment)parentEnvironment); + } } } diff --git a/spring-core/src/main/java/org/springframework/core/env/AbstractEnvironment.java b/spring-core/src/main/java/org/springframework/core/env/AbstractEnvironment.java index b9798525db4..65574834a9a 100644 --- a/spring-core/src/main/java/org/springframework/core/env/AbstractEnvironment.java +++ b/spring-core/src/main/java/org/springframework/core/env/AbstractEnvironment.java @@ -387,6 +387,20 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment { return systemProperties; } + public void merge(ConfigurableEnvironment parent) { + for (PropertySource ps : parent.getPropertySources()) { + if (!this.propertySources.contains(ps.getName())) { + this.propertySources.addLast(ps); + } + } + for (String profile : parent.getActiveProfiles()) { + this.activeProfiles.add(profile); + } + for (String profile : parent.getDefaultProfiles()) { + this.defaultProfiles.add(profile); + } + } + //--------------------------------------------------------------------- // Implementation of ConfigurablePropertyResolver interface diff --git a/spring-core/src/main/java/org/springframework/core/env/ConfigurableEnvironment.java b/spring-core/src/main/java/org/springframework/core/env/ConfigurableEnvironment.java index 38f4c36275c..0972d962e70 100644 --- a/spring-core/src/main/java/org/springframework/core/env/ConfigurableEnvironment.java +++ b/spring-core/src/main/java/org/springframework/core/env/ConfigurableEnvironment.java @@ -149,4 +149,24 @@ public interface ConfigurableEnvironment extends Environment, ConfigurableProper */ Map getSystemProperties(); + /** + * Append the given parent environment's active profiles, default profiles and + * property sources to this (child) environment's respective collections of each. + *

For any identically-named {@code PropertySource} instance existing in both + * parent and child, the child instance is to be preserved and the parent instance + * discarded. This has the effect of allowing overriding of property sources by the + * child as well as avoiding redundant searches through common property source types, + * e.g. system environment and system properties. + *

Active and default profile names are also filtered for duplicates, to avoid + * confusion and redundant storage. + *

The parent environment remains unmodified in any case. Note that any changes to + * the parent environment occurring after the call to {@code merge} will not be + * reflected in the child. Therefore, care should be taken to configure parent + * property sources and profile information prior to calling {@code merge}. + * @param parent the environment to merge with + * @since 3.2 + * @see org.springframework.context.support.AbstractApplicationContext#setParent + */ + void merge(ConfigurableEnvironment parent); + } diff --git a/spring-core/src/test/java/org/springframework/core/env/StandardEnvironmentTests.java b/spring-core/src/test/java/org/springframework/core/env/StandardEnvironmentTests.java index f793eefe61c..8fca6ec6d83 100644 --- a/spring-core/src/test/java/org/springframework/core/env/StandardEnvironmentTests.java +++ b/spring-core/src/test/java/org/springframework/core/env/StandardEnvironmentTests.java @@ -56,6 +56,47 @@ public class StandardEnvironmentTests { private ConfigurableEnvironment environment = new StandardEnvironment(); + @Test + public void merge() { + ConfigurableEnvironment child = new StandardEnvironment(); + child.setActiveProfiles("c1", "c2"); + child.getPropertySources().addLast( + new MockPropertySource("childMock") + .withProperty("childKey", "childVal") + .withProperty("bothKey", "childBothVal")); + + ConfigurableEnvironment parent = new StandardEnvironment(); + parent.setActiveProfiles("p1", "p2"); + parent.getPropertySources().addLast( + new MockPropertySource("parentMock") + .withProperty("parentKey", "parentVal") + .withProperty("bothKey", "parentBothVal")); + + assertThat(child.getProperty("childKey"), is("childVal")); + assertThat(child.getProperty("parentKey"), nullValue()); + assertThat(child.getProperty("bothKey"), is("childBothVal")); + + assertThat(parent.getProperty("childKey"), nullValue()); + assertThat(parent.getProperty("parentKey"), is("parentVal")); + assertThat(parent.getProperty("bothKey"), is("parentBothVal")); + + assertThat(child.getActiveProfiles(), equalTo(new String[]{"c1","c2"})); + assertThat(parent.getActiveProfiles(), equalTo(new String[]{"p1","p2"})); + + child.merge(parent); + + assertThat(child.getProperty("childKey"), is("childVal")); + assertThat(child.getProperty("parentKey"), is("parentVal")); + assertThat(child.getProperty("bothKey"), is("childBothVal")); + + assertThat(parent.getProperty("childKey"), nullValue()); + assertThat(parent.getProperty("parentKey"), is("parentVal")); + assertThat(parent.getProperty("bothKey"), is("parentBothVal")); + + assertThat(child.getActiveProfiles(), equalTo(new String[]{"c1","c2","p1","p2"})); + assertThat(parent.getActiveProfiles(), equalTo(new String[]{"p1","p2"})); + } + @Test public void propertySourceOrder() { ConfigurableEnvironment env = new StandardEnvironment(); diff --git a/spring-webmvc/src/test/java/org/springframework/web/context/XmlWebApplicationContextTests.java b/spring-webmvc/src/test/java/org/springframework/web/context/XmlWebApplicationContextTests.java index 8d6660baf36..c2fb218aad1 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/context/XmlWebApplicationContextTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/context/XmlWebApplicationContextTests.java @@ -48,6 +48,7 @@ public class XmlWebApplicationContextTests extends AbstractApplicationContextTes protected ConfigurableApplicationContext createContext() throws Exception { InitAndIB.constructed = false; root = new XmlWebApplicationContext(); + root.getEnvironment().addActiveProfile("rootProfile1"); MockServletContext sc = new MockServletContext(""); root.setServletContext(sc); root.setConfigLocations(new String[] {"/org/springframework/web/context/WEB-INF/applicationContext.xml"}); @@ -69,6 +70,7 @@ public class XmlWebApplicationContextTests extends AbstractApplicationContextTes }); root.refresh(); XmlWebApplicationContext wac = new XmlWebApplicationContext(); + wac.getEnvironment().addActiveProfile("wacProfile1"); wac.setParent(root); wac.setServletContext(sc); wac.setNamespace("test-servlet"); @@ -77,8 +79,11 @@ public class XmlWebApplicationContextTests extends AbstractApplicationContextTes return wac; } - public void testEnvironmentInheritance() { - assertThat(this.applicationContext.getEnvironment(), sameInstance(this.root.getEnvironment())); + public void testEnvironmentMerge() { + assertThat(this.root.getEnvironment().acceptsProfiles("rootProfile1"), is(true)); + assertThat(this.root.getEnvironment().acceptsProfiles("wacProfile1"), is(false)); + assertThat(this.applicationContext.getEnvironment().acceptsProfiles("rootProfile1"), is(true)); + assertThat(this.applicationContext.getEnvironment().acceptsProfiles("wacProfile1"), is(true)); } /** From 2a2b6eef9107a877492e965b5e063d6f1712e7f9 Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Fri, 25 May 2012 23:05:08 +0300 Subject: [PATCH 3/3] Introduce ConfigurableWebEnvironment Changes introduced in Spring 3.1 for Environment support inadvertently established a cyclic dependency between the org.springframework.web.context and org.springframework.web.context.support packages, specifically through web.context.ContextLoader's invocation of web.context.support.WebApplicationContextUtils#initServletPropertySources. This commit introduces ConfigurableWebEnvironment to break this cyclic dependency. All web.context.ConfigurableWebApplicationContext types now return web.context.ConfigurableWebEnvironment from their #getEnvironment methods; web.context.support.StandardServletEnvironment now implements ConfigurableWebEnvironment and makes the call to web.context.support.WebApplicationContextUtils#initServletPropertySources within its implementation of #initPropertySources. This means that web.context.ContextLoader now invokes web.context.ConfigurableWebEnvironment#initPropertySources instead of web.context.support.WebApplicationContextUtils#initServletPropertySources and thus the cycle is broken. Issue: SPR-9439 --- .../ConfigurableWebApplicationContext.java | 7 ++- .../context/ConfigurableWebEnvironment.java | 46 +++++++++++++++++++ .../web/context/ContextLoader.java | 11 +---- ...tractRefreshableWebApplicationContext.java | 16 +++++-- .../support/GenericWebApplicationContext.java | 16 +++++-- .../support/StandardServletEnvironment.java | 17 +++++-- .../support/StaticWebApplicationContext.java | 13 +++++- .../XmlWebApplicationContextTests.java | 2 +- .../core/env/EnvironmentIntegrationTests.java | 28 ++++++----- 9 files changed, 120 insertions(+), 36 deletions(-) create mode 100644 spring-web/src/main/java/org/springframework/web/context/ConfigurableWebEnvironment.java diff --git a/spring-web/src/main/java/org/springframework/web/context/ConfigurableWebApplicationContext.java b/spring-web/src/main/java/org/springframework/web/context/ConfigurableWebApplicationContext.java index 6727659a315..1ce36b4a290 100644 --- a/spring-web/src/main/java/org/springframework/web/context/ConfigurableWebApplicationContext.java +++ b/spring-web/src/main/java/org/springframework/web/context/ConfigurableWebApplicationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2012 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. @@ -71,6 +71,11 @@ public interface ConfigurableWebApplicationContext extends WebApplicationContext */ ServletConfig getServletConfig(); + /** + * Return the {@link ConfigurableWebEnvironment} used by this web application context. + */ + ConfigurableWebEnvironment getEnvironment(); + /** * Set the namespace for this web application context, * to be used for building a default context config location. diff --git a/spring-web/src/main/java/org/springframework/web/context/ConfigurableWebEnvironment.java b/spring-web/src/main/java/org/springframework/web/context/ConfigurableWebEnvironment.java new file mode 100644 index 00000000000..350d9813303 --- /dev/null +++ b/spring-web/src/main/java/org/springframework/web/context/ConfigurableWebEnvironment.java @@ -0,0 +1,46 @@ +/* + * Copyright 2002-2012 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; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletContext; + +import org.springframework.core.env.ConfigurableEnvironment; + +/** + * Specialization of {@link ConfigurableEnvironment} allowing initialization of + * servlet-related {@link org.springframework.core.env.PropertySource} objects at the + * earliest moment the {@link ServletContext} and (optionally) {@link ServletConfig} + * become available. + * + * @author Chris Beams + * @since 3.1.2 + * @see ConfigurableWebApplicationContext#getEnvironment() + */ +public interface ConfigurableWebEnvironment extends ConfigurableEnvironment { + + /** + * Replace any {@linkplain + * org.springframework.core.env.PropertySource.StubPropertySource stub property source} + * instances acting as placeholders with real servlet context/config property sources + * using the given parameters. + * @param servletContext the {@link ServletContext} (may not be {@code null}) + * @param servletConfig the {@link ServletContext} ({@code null} if not available) + */ + void initPropertySources(ServletContext servletContext, ServletConfig servletConfig); + +} 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 9abcece00ba..79702474ad2 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 @@ -44,7 +44,6 @@ 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. @@ -485,15 +484,9 @@ public class ContextLoader { initializerInstances.add(BeanUtils.instantiateClass(initializerClass)); } - 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); + applicationContext.getEnvironment().initPropertySources(servletContext, null); + Collections.sort(initializerInstances, new AnnotationAwareOrderComparator()); for (ApplicationContextInitializer initializer : initializerInstances) { initializer.initialize(applicationContext); } diff --git a/spring-web/src/main/java/org/springframework/web/context/support/AbstractRefreshableWebApplicationContext.java b/spring-web/src/main/java/org/springframework/web/context/support/AbstractRefreshableWebApplicationContext.java index 285b4040cc1..c5e30e9f955 100644 --- a/spring-web/src/main/java/org/springframework/web/context/support/AbstractRefreshableWebApplicationContext.java +++ b/spring-web/src/main/java/org/springframework/web/context/support/AbstractRefreshableWebApplicationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2012 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. @@ -27,7 +27,9 @@ import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.ui.context.Theme; import org.springframework.ui.context.ThemeSource; import org.springframework.ui.context.support.UiApplicationContextUtils; +import org.springframework.util.Assert; import org.springframework.web.context.ConfigurableWebApplicationContext; +import org.springframework.web.context.ConfigurableWebEnvironment; import org.springframework.web.context.ServletConfigAware; import org.springframework.web.context.ServletContextAware; @@ -139,6 +141,14 @@ public abstract class AbstractRefreshableWebApplicationContext extends AbstractR return new StandardServletEnvironment(); } + @Override + public ConfigurableWebEnvironment getEnvironment() { + ConfigurableEnvironment env = super.getEnvironment(); + Assert.isInstanceOf(ConfigurableWebEnvironment.class, env, + "ConfigurableWebApplicationContext environment must be of type " + + "ConfigurableWebEnvironment"); + return (ConfigurableWebEnvironment) env; + } /** * Register request/session scopes, a {@link ServletContextAwareProcessor}, etc. @@ -186,9 +196,7 @@ public abstract class AbstractRefreshableWebApplicationContext extends AbstractR @Override protected void initPropertySources() { super.initPropertySources(); - WebApplicationContextUtils.initServletPropertySources( - this.getEnvironment().getPropertySources(), this.servletContext, - this.servletConfig); + this.getEnvironment().initPropertySources(this.servletContext, this.servletConfig); } public Theme getTheme(String themeName) { diff --git a/spring-web/src/main/java/org/springframework/web/context/support/GenericWebApplicationContext.java b/spring-web/src/main/java/org/springframework/web/context/support/GenericWebApplicationContext.java index a70f955d077..99f83dcc5d7 100644 --- a/spring-web/src/main/java/org/springframework/web/context/support/GenericWebApplicationContext.java +++ b/spring-web/src/main/java/org/springframework/web/context/support/GenericWebApplicationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -28,9 +28,11 @@ import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.ui.context.Theme; import org.springframework.ui.context.ThemeSource; import org.springframework.ui.context.support.UiApplicationContextUtils; +import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import org.springframework.web.context.ConfigurableWebApplicationContext; +import org.springframework.web.context.ConfigurableWebEnvironment; import org.springframework.web.context.ServletContextAware; /** @@ -130,6 +132,15 @@ public class GenericWebApplicationContext extends GenericApplicationContext return new StandardServletEnvironment(); } + @Override + public ConfigurableWebEnvironment getEnvironment() { + ConfigurableEnvironment env = super.getEnvironment(); + Assert.isInstanceOf(ConfigurableWebEnvironment.class, env, + "ConfigurableWebApplicationContext environment must be of type " + + "ConfigurableWebEnvironment"); + return (ConfigurableWebEnvironment) env; + } + /** * Register ServletContextAwareProcessor. * @see ServletContextAwareProcessor @@ -176,8 +187,7 @@ public class GenericWebApplicationContext extends GenericApplicationContext @Override protected void initPropertySources() { super.initPropertySources(); - WebApplicationContextUtils.initServletPropertySources( - this.getEnvironment().getPropertySources(), this.servletContext); + this.getEnvironment().initPropertySources(this.servletContext, null); } public Theme getTheme(String themeName) { diff --git a/spring-web/src/main/java/org/springframework/web/context/support/StandardServletEnvironment.java b/spring-web/src/main/java/org/springframework/web/context/support/StandardServletEnvironment.java index 0885fadd40d..c5dcfe9da8f 100644 --- a/spring-web/src/main/java/org/springframework/web/context/support/StandardServletEnvironment.java +++ b/spring-web/src/main/java/org/springframework/web/context/support/StandardServletEnvironment.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -26,6 +26,7 @@ import org.springframework.core.env.PropertySource.StubPropertySource; import org.springframework.core.env.StandardEnvironment; import org.springframework.jndi.JndiLocatorDelegate; import org.springframework.jndi.JndiPropertySource; +import org.springframework.web.context.ConfigurableWebEnvironment; /** * {@link Environment} implementation to be used by {@code Servlet}-based web @@ -40,7 +41,8 @@ import org.springframework.jndi.JndiPropertySource; * @since 3.1 * @see StandardEnvironment */ -public class StandardServletEnvironment extends StandardEnvironment { +public class StandardServletEnvironment extends StandardEnvironment + implements ConfigurableWebEnvironment { /** Servlet context init parameters property source name: {@value} */ public static final String SERVLET_CONTEXT_PROPERTY_SOURCE_NAME = "servletContextInitParams"; @@ -68,15 +70,15 @@ public class StandardServletEnvironment extends StandardEnvironment { * environment variables contributed by the {@link StandardEnvironment} superclass. *

The {@code Servlet}-related property sources are added as {@link * StubPropertySource stubs} at this stage, and will be {@linkplain - * WebApplicationContextUtils#initServletPropertySources fully initialized} once the - * actual {@link ServletConfig} and {@link ServletContext} objects become available. + * #initPropertySources(ServletContext) fully initialized} once the actual + * {@link ServletContext} object becomes available. * @see StandardEnvironment#customizePropertySources * @see org.springframework.core.env.AbstractEnvironment#customizePropertySources * @see ServletConfigPropertySource * @see ServletContextPropertySource * @see org.springframework.jndi.JndiPropertySource * @see org.springframework.context.support.AbstractApplicationContext#initPropertySources - * @see WebApplicationContextUtils#initServletPropertySources + * @see #initPropertySources(ServletContext) */ @Override protected void customizePropertySources(MutablePropertySources propertySources) { @@ -88,4 +90,9 @@ public class StandardServletEnvironment extends StandardEnvironment { super.customizePropertySources(propertySources); } + public void initPropertySources(ServletContext servletContext, ServletConfig servletConfig) { + WebApplicationContextUtils.initServletPropertySources( + this.getPropertySources(), servletContext, servletConfig); + } + } diff --git a/spring-web/src/main/java/org/springframework/web/context/support/StaticWebApplicationContext.java b/spring-web/src/main/java/org/springframework/web/context/support/StaticWebApplicationContext.java index 67cc216ade5..5de71bda608 100644 --- a/spring-web/src/main/java/org/springframework/web/context/support/StaticWebApplicationContext.java +++ b/spring-web/src/main/java/org/springframework/web/context/support/StaticWebApplicationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -27,7 +27,9 @@ import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.ui.context.Theme; import org.springframework.ui.context.ThemeSource; import org.springframework.ui.context.support.UiApplicationContextUtils; +import org.springframework.util.Assert; import org.springframework.web.context.ConfigurableWebApplicationContext; +import org.springframework.web.context.ConfigurableWebEnvironment; import org.springframework.web.context.ServletConfigAware; import org.springframework.web.context.ServletContextAware; @@ -167,6 +169,15 @@ public class StaticWebApplicationContext extends StaticApplicationContext return new StandardServletEnvironment(); } + @Override + public ConfigurableWebEnvironment getEnvironment() { + ConfigurableEnvironment env = super.getEnvironment(); + Assert.isInstanceOf(ConfigurableWebEnvironment.class, env, + "ConfigurableWebApplication environment must be of type " + + "ConfigurableWebEnvironment"); + return (ConfigurableWebEnvironment) env; + } + /** * Initialize the theme capability. */ diff --git a/spring-webmvc/src/test/java/org/springframework/web/context/XmlWebApplicationContextTests.java b/spring-webmvc/src/test/java/org/springframework/web/context/XmlWebApplicationContextTests.java index c2fb218aad1..271c07c494e 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/context/XmlWebApplicationContextTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/context/XmlWebApplicationContextTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2005 the original author or authors. + * Copyright 2002-2012 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. diff --git a/src/test/java/org/springframework/core/env/EnvironmentIntegrationTests.java b/src/test/java/org/springframework/core/env/EnvironmentIntegrationTests.java index 4272719b621..fc60b191ee6 100644 --- a/src/test/java/org/springframework/core/env/EnvironmentIntegrationTests.java +++ b/src/test/java/org/springframework/core/env/EnvironmentIntegrationTests.java @@ -99,6 +99,7 @@ public class EnvironmentIntegrationTests { private ConfigurableEnvironment prodEnv; private ConfigurableEnvironment devEnv; + private ConfigurableEnvironment prodWebEnv; /** * Constants used both locally and in scan* sub-packages @@ -125,6 +126,9 @@ public class EnvironmentIntegrationTests { devEnv = new StandardEnvironment(); devEnv.setActiveProfiles(DEV_ENV_NAME); + + prodWebEnv = new StandardServletEnvironment(); + prodWebEnv.setActiveProfiles(PROD_ENV_NAME); } @Test @@ -348,24 +352,24 @@ public class EnvironmentIntegrationTests { assertHasStandardServletEnvironment(ctx); - ctx.setEnvironment(prodEnv); + ctx.setEnvironment(prodWebEnv); ctx.refresh(); - assertHasEnvironment(ctx, prodEnv); + assertHasEnvironment(ctx, prodWebEnv); assertEnvironmentBeanRegistered(ctx); - assertEnvironmentAwareInvoked(ctx, prodEnv); + assertEnvironmentAwareInvoked(ctx, prodWebEnv); } @Test public void xmlWebApplicationContext() { AbstractRefreshableWebApplicationContext ctx = new XmlWebApplicationContext(); ctx.setConfigLocation("classpath:" + XML_PATH); - ctx.setEnvironment(prodEnv); + ctx.setEnvironment(prodWebEnv); ctx.refresh(); - assertHasEnvironment(ctx, prodEnv); + assertHasEnvironment(ctx, prodWebEnv); assertEnvironmentBeanRegistered(ctx); - assertEnvironmentAwareInvoked(ctx, prodEnv); + assertEnvironmentAwareInvoked(ctx, prodWebEnv); assertThat(ctx.containsBean(DEV_BEAN_NAME), is(false)); assertThat(ctx.containsBean(PROD_BEAN_NAME), is(true)); } @@ -394,24 +398,24 @@ public class EnvironmentIntegrationTests { registerEnvironmentBeanDefinition(ctx); - ctx.setEnvironment(prodEnv); + ctx.setEnvironment(prodWebEnv); ctx.refresh(); - assertHasEnvironment(ctx, prodEnv); + assertHasEnvironment(ctx, prodWebEnv); assertEnvironmentBeanRegistered(ctx); - assertEnvironmentAwareInvoked(ctx, prodEnv); + assertEnvironmentAwareInvoked(ctx, prodWebEnv); } @Test public void annotationConfigWebApplicationContext() { AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); - ctx.setEnvironment(prodEnv); + ctx.setEnvironment(prodWebEnv); ctx.setConfigLocation(EnvironmentAwareBean.class.getName()); ctx.refresh(); - assertHasEnvironment(ctx, prodEnv); + assertHasEnvironment(ctx, prodWebEnv); assertEnvironmentBeanRegistered(ctx); - assertEnvironmentAwareInvoked(ctx, prodEnv); + assertEnvironmentAwareInvoked(ctx, prodWebEnv); } @Test