From 7271ba818215ce267ab0638661915075dd05e8dd Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Fri, 20 May 2011 03:50:14 +0000 Subject: [PATCH] Introduce AbstractEnvironment#customizePropertySources This new hook in the AbstractEnvironment lifecycle allows for more explicit and predictable customization of property sources by subclasses. See Javadoc and existing implementations for detail. Issue: SPR-8354 --- .../core/env/AbstractEnvironment.java | 88 ++++++++++++++++++- .../core/env/ConfigurableEnvironment.java | 14 ++- .../core/env/DefaultEnvironment.java | 12 ++- .../context/DefaultPortletEnvironment.java | 24 ++--- .../support/DefaultWebEnvironment.java | 25 +++--- 5 files changed, 134 insertions(+), 29 deletions(-) diff --git a/org.springframework.core/src/main/java/org/springframework/core/env/AbstractEnvironment.java b/org.springframework.core/src/main/java/org/springframework/core/env/AbstractEnvironment.java index 7d7ae1b3bb2..9bc6c3a8aeb 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/env/AbstractEnvironment.java +++ b/org.springframework.core/src/main/java/org/springframework/core/env/AbstractEnvironment.java @@ -74,8 +74,92 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment { private Set activeProfiles = new LinkedHashSet(); private Set defaultProfiles = new LinkedHashSet(this.getReservedDefaultProfiles()); - private MutablePropertySources propertySources = new MutablePropertySources(); - private ConfigurablePropertyResolver propertyResolver = new PropertySourcesPropertyResolver(propertySources); + private final MutablePropertySources propertySources = new MutablePropertySources(); + private final ConfigurablePropertyResolver propertyResolver = new PropertySourcesPropertyResolver(propertySources); + + + public AbstractEnvironment() { + this.customizePropertySources(propertySources); + } + + /** + * Customize the set of {@link PropertySource} objects to be searched by this + * {@code Environment} during calls to {@link #getProperty(String)} and related + * methods. + * + *

Subclasses that override this method are encouraged to add property + * sources using {@link MutablePropertySources#addLast(PropertySource)} such that + * further subclasses may call {@code super.customizePropertySources()} with + * predictable results. For example: + *

+	 * public class Level1Environment extends AbstractEnvironment {
+	 *     @Override
+	 *     protected void customizePropertySources(MutablePropertySources propertySources) {
+	 *         super.customizePropertySources(propertySources); // no-op from base class
+	 *         propertySources.addLast(new PropertySourceA(...));
+	 *         propertySources.addLast(new PropertySourceB(...));
+	 *     }
+	 * }
+	 *
+	 * public class Level2Environment extends Level1Environment {
+	 *     @Override
+	 *     protected void customizePropertySources(MutablePropertySources propertySources) {
+	 *         super.customizePropertySources(propertySources); // add all from superclass
+	 *         propertySources.addLast(new PropertySourceC(...));
+	 *         propertySources.addLast(new PropertySourceD(...));
+	 *     }
+	 * }
+	 * 
+ * In this arrangement, properties will be resolved against sources A, B, C, D in that + * order. That is to say that property source "A" has precedence over property source + * "D". If the {@code Level2Environment} subclass wished to give property sources C + * and D higher precedence than A and B, it could simply call + * {@code super.customizePropertySources} after, rather than before adding its own: + *
+	 * public class Level2Environment extends Level1Environment {
+	 *     @Override
+	 *     protected void customizePropertySources(MutablePropertySources propertySources) {
+	 *         propertySources.addLast(new PropertySourceC(...));
+	 *         propertySources.addLast(new PropertySourceD(...));
+	 *         super.customizePropertySources(propertySources); // add all from superclass
+	 *     }
+	 * }
+	 * 
+ * The search order is now C, D, A, B as desired. + * + *

Beyond these recommendations, subclasses may use any of the add*, + * {@code remove}, or {@code replace} methods exposed by {@link MutablePropertySources} + * in order to create the exact arrangement of property sources desired. + * + *

The base implementation in {@link AbstractEnvironment#customizePropertySources} + * registers no property sources. + * + *

Note that clients of any {@link ConfigurableEnvironment} may further customize + * property sources via the {@link #getPropertySources()} accessor, typically within + * an {@link org.springframework.context.ApplicationContextInitializer + * ApplicationContextInitializer}. For example: + *

+	 * ConfigurableEnvironment env = new StandardEnvironment();
+	 * env.getPropertySources().addLast(new PropertySourceX(...));
+	 * 
+ * + * @see MutablePropertySources + * @see PropertySourcesPropertyResolver + * @see org.springframework.context.ApplicationContextInitializer + */ + protected void customizePropertySources(MutablePropertySources propertySources) { + } + + /** + * Return the set of reserved default profile names. This implementation returns + * {@value #RESERVED_DEFAULT_PROFILE_NAME}. Subclasses may override in order to + * customize the set of reserved names. + * @see #RESERVED_DEFAULT_PROFILE_NAME + * @see #doGetDefaultProfiles() + */ + protected Set getReservedDefaultProfiles() { + return Collections.singleton(RESERVED_DEFAULT_PROFILE_NAME); + } //--------------------------------------------------------------------- diff --git a/org.springframework.core/src/main/java/org/springframework/core/env/ConfigurableEnvironment.java b/org.springframework.core/src/main/java/org/springframework/core/env/ConfigurableEnvironment.java index 4574ff062f2..d9b5e6dc243 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/env/ConfigurableEnvironment.java +++ b/org.springframework.core/src/main/java/org/springframework/core/env/ConfigurableEnvironment.java @@ -49,7 +49,19 @@ public interface ConfigurableEnvironment extends Environment, ConfigurableProper void setDefaultProfiles(String... profiles); /** - * Return the {@link PropertySources} for this environment in mutable form + * Return the {@link PropertySources} for this {@code Environment} in mutable form, + * allowing for manipulation of the set of {@link PropertySource} objects that should + * be searched when resolving properties against this {@code Environment} object. + * The various {@link MutablePropertySources} methods such as + * {@link MutablePropertySources#addFirst addFirst}, + * {@link MutablePropertySources#addFirst addLast}, + * {@link MutablePropertySources#addFirst addBefore} and + * {@link MutablePropertySources#addFirst addAfter} allow for fine-grained control + * over property source ordering. This is useful, for example, in ensuring that + * certain user-defined property sources have search precedence over default property + * sources such as the set of system properties or the set of system environment + * variables. + * @see DefaultEnvironment#DefaultEnvironment() */ MutablePropertySources getPropertySources(); diff --git a/org.springframework.core/src/main/java/org/springframework/core/env/DefaultEnvironment.java b/org.springframework.core/src/main/java/org/springframework/core/env/DefaultEnvironment.java index 12e1c701ae7..0d8cf23cdf1 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/env/DefaultEnvironment.java +++ b/org.springframework.core/src/main/java/org/springframework/core/env/DefaultEnvironment.java @@ -83,17 +83,21 @@ public class DefaultEnvironment extends AbstractEnvironment { /** - * Create a new {@code Environment} populated with property sources in the following order: + * Customize the set of property sources with those appropriate for any standard Java + * environment: *
    *
  • {@value #SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME} *
  • {@value #SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME} *
*

Properties present in {@value #SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME} will * take precedence over those in {@value #SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME}. + * @see #getSystemProperties() + * @see #getSystemEnvironment() */ - public DefaultEnvironment() { - getPropertySources().addFirst(new MapPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment())); - getPropertySources().addFirst(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties())); + @Override + protected void customizePropertySources(MutablePropertySources propertySources) { + propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties())); + propertySources.addLast(new MapPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment())); } } diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/context/DefaultPortletEnvironment.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/context/DefaultPortletEnvironment.java index 3491c92ae09..2c3a71e17a3 100644 --- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/context/DefaultPortletEnvironment.java +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/context/DefaultPortletEnvironment.java @@ -22,6 +22,7 @@ import javax.servlet.ServletContext; import org.springframework.core.env.DefaultEnvironment; import org.springframework.core.env.Environment; +import org.springframework.core.env.MutablePropertySources; import org.springframework.core.env.PropertySource; import org.springframework.core.env.PropertySource.StubPropertySource; import org.springframework.web.context.support.DefaultWebEnvironment; @@ -49,8 +50,8 @@ public class DefaultPortletEnvironment extends DefaultEnvironment { public static final String PORTLET_CONFIG_PROPERTY_SOURCE_NAME = "portletConfigInitParams"; /** - * Create a new {@code Environment} populated with the property sources contributed by - * superclasses as well as: + * Customize the set of property sources with those contributed by superclasses as + * well as those appropriate for standard portlet-based environments: *

    *
  • {@value #PORTLET_CONFIG_PROPERTY_SOURCE_NAME} *
  • {@value #PORTLET_CONTEXT_PROPERTY_SOURCE_NAME} @@ -58,22 +59,25 @@ public class DefaultPortletEnvironment extends DefaultEnvironment { *
*

Properties present in {@value #PORTLET_CONFIG_PROPERTY_SOURCE_NAME} will * take precedence over those in {@value #PORTLET_CONTEXT_PROPERTY_SOURCE_NAME}, - * which takes precedence over those in . - * Properties in either will take precedence over system properties and environment - * variables. + * which takes precedence over those in + * {@linkplain DefaultWebEnvironment#SERVLET_CONTEXT_PROPERTY_SOURCE_NAME "servletContextInitParams"}. + *

Properties in any of the above will take precedence over system properties and environment + * variables contributed by the {@link DefaultEnvironment} superclass. *

The property sources are added as stubs for now, and will be * {@linkplain PortletApplicationContextUtils#initPortletPropertySources fully initialized} * once the actual {@link PortletConfig}, {@link PortletContext}, and {@link ServletContext} * objects are available. - * @see DefaultEnvironment#DefaultEnvironment + * @see DefaultEnvironment#customizePropertySources * @see PortletConfigPropertySource * @see PortletContextPropertySource * @see AbstractRefreshablePortletApplicationContext#initPropertySources * @see PortletApplicationContextUtils#initPortletPropertySources */ - public DefaultPortletEnvironment() { - this.getPropertySources().addFirst(new StubPropertySource(DefaultWebEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME)); - this.getPropertySources().addFirst(new StubPropertySource(PORTLET_CONTEXT_PROPERTY_SOURCE_NAME)); - this.getPropertySources().addFirst(new StubPropertySource(PORTLET_CONFIG_PROPERTY_SOURCE_NAME)); + @Override + protected void customizePropertySources(MutablePropertySources propertySources) { + propertySources.addLast(new StubPropertySource(PORTLET_CONFIG_PROPERTY_SOURCE_NAME)); + propertySources.addLast(new StubPropertySource(PORTLET_CONTEXT_PROPERTY_SOURCE_NAME)); + propertySources.addLast(new StubPropertySource(DefaultWebEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME)); + super.customizePropertySources(propertySources); } } diff --git a/org.springframework.web/src/main/java/org/springframework/web/context/support/DefaultWebEnvironment.java b/org.springframework.web/src/main/java/org/springframework/web/context/support/DefaultWebEnvironment.java index a30a56f4aa0..9fd2b6aa67c 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/context/support/DefaultWebEnvironment.java +++ b/org.springframework.web/src/main/java/org/springframework/web/context/support/DefaultWebEnvironment.java @@ -21,6 +21,7 @@ import javax.servlet.ServletContext; import org.springframework.core.env.DefaultEnvironment; import org.springframework.core.env.Environment; +import org.springframework.core.env.MutablePropertySources; import org.springframework.core.env.PropertySource; import org.springframework.core.env.PropertySource.StubPropertySource; import org.springframework.core.env.PropertySources; @@ -53,8 +54,8 @@ public class DefaultWebEnvironment extends DefaultEnvironment { public static final String SERVLET_CONFIG_PROPERTY_SOURCE_NAME = "servletConfigInitParams"; /** - * Create a new {@code Environment} populated with the property sources contributed by - * superclasses as well as: + * Customize the set of property sources with those contributed by superclasses as + * well as those appropriate for standard servlet-based environments: *

    *
  • {@value #SERVLET_CONFIG_PROPERTY_SOURCE_NAME} *
  • {@value #SERVLET_CONTEXT_PROPERTY_SOURCE_NAME} @@ -62,8 +63,8 @@ public class DefaultWebEnvironment extends DefaultEnvironment { *
*

Properties present in {@value #SERVLET_CONFIG_PROPERTY_SOURCE_NAME} will * take precedence over those in {@value #SERVLET_CONTEXT_PROPERTY_SOURCE_NAME}. - * Properties in either will take precedence over system properties and environment - * variables. + *

Properties in any of the above will take precedence over system properties and environment + * variables contributed by the {@link DefaultEnvironment} superclass. *

The {@code Servlet}-related property sources are added as stubs for now, and will be * {@linkplain WebApplicationContextUtils#initServletPropertySources fully initialized} * once the actual {@link ServletConfig} and {@link ServletContext} objects are available. @@ -71,21 +72,21 @@ public class DefaultWebEnvironment extends DefaultEnvironment { * property is present in any of the default property sources, a {@link JndiPropertySource} will * be added as well, with precedence lower than servlet property sources, but higher than system * properties and environment variables. - * @see DefaultEnvironment#DefaultEnvironment + * @see DefaultEnvironment#customizePropertySources * @see ServletConfigPropertySource * @see ServletContextPropertySource * @see org.springframework.jndi.JndiPropertySource * @see org.springframework.context.support.AbstractApplicationContext#initPropertySources * @see WebApplicationContextUtils#initServletPropertySources */ - public DefaultWebEnvironment() { - getPropertySources().addFirst(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME)); - getPropertySources().addFirst(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME)); + @Override + protected void customizePropertySources(MutablePropertySources propertySources) { + propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME)); + propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME)); + super.customizePropertySources(propertySources); - Boolean jndiPropertySourceEnabled = this.getProperty(JndiPropertySource.JNDI_PROPERTY_SOURCE_ENABLED_FLAG, boolean.class); - if (jndiPropertySourceEnabled != null && jndiPropertySourceEnabled) { - getPropertySources().addAfter(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME, new JndiPropertySource()); + if (this.getProperty(JndiPropertySource.JNDI_PROPERTY_SOURCE_ENABLED_FLAG, boolean.class, false)) { + propertySources.addAfter(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME, new JndiPropertySource()); } } - }