Skip to content

Commit

Permalink
Merge branch cbeams/SPR-9439
Browse files Browse the repository at this point in the history
* SPR-9439:
  Introduce ConfigurableWebEnvironment
  Introduce ConfigurableEnvironment#merge
  Polish
  • Loading branch information
cbeams committed May 26, 2012
2 parents 5327a7a + 2a2b6ee commit e72c49f
Show file tree
Hide file tree
Showing 15 changed files with 257 additions and 78 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -378,14 +378,19 @@ protected ResourcePatternResolver getResourcePatternResolver() {

/**
* {@inheritDoc}
* <p>The parent {@linkplain #getEnvironment() environment} is
* delegated to this (child) context if the parent is a
* {@link ConfigurableApplicationContext} implementation.
* <p>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);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,20 @@ protected String getSystemAttribute(String propertyName) {
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
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -55,13 +55,14 @@
* propertySources.replace(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, mockEnvVars);
* </pre>
*
* When an {@link Environment} is being used by an ApplicationContext, it is important
* that any such PropertySource manipulations be performed <em>before</em> 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
* <em>before</em> 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
Expand All @@ -78,7 +79,6 @@ public interface ConfigurableEnvironment extends Environment, ConfigurableProper
* <p>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
Expand Down Expand Up @@ -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)}.
*
* <p>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.
*
* <p>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
Expand All @@ -140,17 +138,35 @@ 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)}.
*
* <p>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.
*
* <p>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
* issued noting the exception.
*/
Map<String, Object> getSystemProperties();

/**
* Append the given parent environment's active profiles, default profiles and
* property sources to this (child) environment's respective collections of each.
* <p>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.
* <p>Active and default profile names are also filtered for duplicates, to avoid
* confusion and redundant storage.
* <p>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);

}
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -335,7 +335,7 @@ public static void isInstanceOf(Class type, Object obj, String message) {
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);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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;
Expand All @@ -40,8 +24,18 @@
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}.
*
Expand All @@ -62,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();
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
@@ -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);

}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@
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.
Expand Down Expand Up @@ -485,15 +484,9 @@ protected void customizeContext(ServletContext servletContext, ConfigurableWebAp
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<ConfigurableApplicationContext> initializer : initializerInstances) {
initializer.initialize(applicationContext);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -27,7 +27,9 @@
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;

Expand Down Expand Up @@ -139,6 +141,14 @@ protected ConfigurableEnvironment createEnvironment() {
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.
Expand Down Expand Up @@ -186,9 +196,7 @@ protected void onRefresh() {
@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) {
Expand Down
Loading

0 comments on commit e72c49f

Please sign in to comment.