diff --git a/.gitignore b/.gitignore
index 55517f74172..6b6a4200bec 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,15 +1,17 @@
-target
+*.java.hsp
+*.sonarj
+*.sw*
+.DS_Store
+.settings
+.springBeans
bin
+build.sh
integration-repo
ivy-cache
jmx.log
-.springBeans
-.settings
-.DS_Store
-*.sw*
-org.springframework.test/test-output/
+org.springframework.jdbc/derby.log
org.springframework.spring-parent/.classpath
org.springframework.spring-parent/.project
-build.sh
-org.springframework.jdbc/derby.log
+org.springframework.test/test-output/
spring-build
+target
diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/BeanFactory.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/BeanFactory.java
index c49b9065056..4e00489b1c9 100644
--- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/BeanFactory.java
+++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/BeanFactory.java
@@ -152,7 +152,7 @@ public interface BeanFactory {
/**
* Return the bean instance that uniquely matches the given object type, if any.
* @param requiredType type the bean must match; can be an interface or superclass.
- * {@literal null} is disallowed.
+ * {@code null} is disallowed.
*
This method goes into {@link ListableBeanFactory} by-type lookup territory
* but may also be translated into a conventional by-name lookup based on the name
* of the given type. For more extensive retrieval operations across sets of beans,
diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/access/SingletonBeanFactoryLocator.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/access/SingletonBeanFactoryLocator.java
index 7f1f73d1cbd..2bdc1e5e5b0 100644
--- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/access/SingletonBeanFactoryLocator.java
+++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/access/SingletonBeanFactoryLocator.java
@@ -432,7 +432,6 @@ public class SingletonBeanFactoryLocator implements BeanFactoryLocator {
protected BeanFactory createDefinition(String resourceLocation, String factoryKey) {
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
- // TODO SPR-7508: consider whether to allow for setEnvironment() here (where would it come from?)
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
try {
diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/config/AbstractPropertyPlaceholderConfigurer.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/config/AbstractPropertyPlaceholderConfigurer.java
index c3fd69f3f6d..b03e529d4c9 100644
--- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/config/AbstractPropertyPlaceholderConfigurer.java
+++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/config/AbstractPropertyPlaceholderConfigurer.java
@@ -29,26 +29,87 @@ import org.springframework.util.StringValueResolver;
/**
- * TODO SPR-7508: document.
+ * Abstract base class for property resource configurers that resolve placeholders
+ * in bean definition property values. Implementations pull values from a
+ * properties file or other {@linkplain org.springframework.core.env.PropertySource
+ * property source} into bean definitions.
+ *
+ *
The default placeholder syntax follows the Ant / Log4J / JSP EL style:
+ *
+ *
${...}
+ *
+ * Example XML bean definition:
+ *
+ *{@code
+ *
+ *
+ *
+ *
+ *}
+ *
+ * Example properties file:
+ *
+ * driver=com.mysql.jdbc.Driver
+ * dbname=mysql:mydb
+ *
+ * Annotated bean definitions may take advantage of property replacement using
+ * the {@link org.springframework.beans.factory.annotation.Value @Value} annotation:
+ *
+ *@Value("${person.age}")
+ *
+ * Implementations check simple property values, lists, maps, props, and bean names
+ * in bean references. Furthermore, placeholder values can also cross-reference
+ * other placeholders, like:
+ *
+ *rootPath=myrootdir
+ *subPath=${rootPath}/subdir
+ *
+ * In contrast to {@link PropertyOverrideConfigurer}, subclasses of this type allow
+ * filling in of explicit placeholders in bean definitions.
+ *
+ * If a configurer cannot resolve a placeholder, a {@link BeanDefinitionStoreException}
+ * will be thrown. If you want to check against multiple properties files, specify multiple
+ * resources via the {@link #setLocations locations} property. You can also define multiple
+ * configurers, each with its own placeholder syntax. Use {@link
+ * #ignoreUnresolvablePlaceholders} to intentionally suppress throwing an exception if a
+ * placeholder cannot be resolved.
+ *
+ *
Default property values can be defined globally for each configurer instance
+ * via the {@link #setProperties properties} property, or on a property-by-property basis
+ * using the default value separator which is {@code ":"} by default and
+ * customizable via {@link #setValueSeparator(String)}.
+ *
+ *
Example XML property with default value:
+ *
+ *
{@code
+ *
+ *}
*
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.1
+ * @see PropertyPlaceholderConfigurer
+ * @see org.springframework.context.support.PropertySourcesPlaceholderConfigurer
*/
public abstract class AbstractPropertyPlaceholderConfigurer extends PropertyResourceConfigurer
implements BeanNameAware, BeanFactoryAware {
- /** Default placeholder prefix: "${" */
+ /** Default placeholder prefix: {@value} */
public static final String DEFAULT_PLACEHOLDER_PREFIX = "${";
- /** Default placeholder suffix: "}" */
+
+ /** Default placeholder suffix: {@value} */
public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}";
- /** Default value separator: ":" */
+
+ /** Default value separator: {@value} */
public static final String DEFAULT_VALUE_SEPARATOR = ":";
+ /** Defaults to {@value #DEFAULT_PLACEHOLDER_PREFIX} */
protected String placeholderPrefix = DEFAULT_PLACEHOLDER_PREFIX;
+ /** Defaults to {@value #DEFAULT_PLACEHOLDER_SUFFIX} */
protected String placeholderSuffix = DEFAULT_PLACEHOLDER_SUFFIX;
+ /** Defaults to {@value #DEFAULT_VALUE_SEPARATOR} */
protected String valueSeparator = DEFAULT_VALUE_SEPARATOR;
protected boolean ignoreUnresolvablePlaceholders = false;
@@ -60,12 +121,14 @@ public abstract class AbstractPropertyPlaceholderConfigurer extends PropertyReso
private BeanFactory beanFactory;
+ /**
+ * Return the {@code PlaceholderResolver} for this configurer.
+ */
protected abstract PlaceholderResolver getPlaceholderResolver(Properties props);
/**
* Set the prefix that a placeholder string starts with.
- * The default is "${".
- * @see #DEFAULT_PLACEHOLDER_PREFIX
+ * The default is {@value #DEFAULT_PLACEHOLDER_PREFIX}.
*/
public void setPlaceholderPrefix(String placeholderPrefix) {
this.placeholderPrefix = placeholderPrefix;
@@ -73,8 +136,7 @@ public abstract class AbstractPropertyPlaceholderConfigurer extends PropertyReso
/**
* Set the suffix that a placeholder string ends with.
- * The default is "}".
- * @see #DEFAULT_PLACEHOLDER_SUFFIX
+ * The default is {@value #DEFAULT_PLACEHOLDER_SUFFIX}.
*/
public void setPlaceholderSuffix(String placeholderSuffix) {
this.placeholderSuffix = placeholderSuffix;
@@ -82,22 +144,22 @@ public abstract class AbstractPropertyPlaceholderConfigurer extends PropertyReso
/**
* Specify the separating character between the placeholder variable
- * and the associated default value, or null if no such
+ * and the associated default value, or {@code null} if no such
* special character should be processed as a value separator.
- * The default is ":".
+ * The default is {@value #DEFAULT_VALUE_SEPARATOR}.
*/
public void setValueSeparator(String valueSeparator) {
this.valueSeparator = valueSeparator;
}
/**
- * Set a value that should be treated as null when
+ * Set a value that should be treated as {@code null} when
* resolved as a placeholder value: e.g. "" (empty String) or "null".
* Note that this will only apply to full property values,
* not to parts of concatenated values.
*
By default, no such null value is defined. This means that
- * there is no way to express null as a property
- * value unless you explictly map a corresponding value here.
+ * there is no way to express {@code null} as a property
+ * value unless you explicitly map a corresponding value here.
*/
public void setNullValue(String nullValue) {
this.nullValue = nullValue;
@@ -139,6 +201,10 @@ public abstract class AbstractPropertyPlaceholderConfigurer extends PropertyReso
}
+ /**
+ * Visit each bean definition in the given bean factory and attempt to replace ${...} property
+ * placeholders with values from the given properties.
+ */
@Override
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props)
throws BeansException {
diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/config/PropertyPlaceholderConfigurer.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/config/PropertyPlaceholderConfigurer.java
index 5274e9a08b5..b823d0f917a 100644
--- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/config/PropertyPlaceholderConfigurer.java
+++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/config/PropertyPlaceholderConfigurer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2010 the original author or authors.
+ * 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.
@@ -25,69 +25,40 @@ import org.springframework.core.Constants;
import org.springframework.util.PropertyPlaceholderHelper;
import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver;
-
/**
- * A property resource configurer that resolves placeholders in bean property values of
- * context definitions. It pulls values from a properties file into bean definitions.
+ * {@link AbstractPropertyPlaceholderConfigurer} subclass that resolves ${...} placeholders
+ * against {@link #setLocation local} {@link #setProperties properties} and/or system properties
+ * and environment variables.
*
- *
The default placeholder syntax follows the Ant / Log4J / JSP EL style:
+ *
As of Spring 3.1, {@link org.springframework.context.support.PropertySourcesPlaceholderConfigurer
+ * PropertySourcesPlaceholderConfigurer} should be used preferentially over this implementation; it is
+ * more flexible through taking advantage of the {@link org.springframework.core.env.Environment Environment} and
+ * {@link org.springframework.core.env.PropertySource PropertySource} mechanisms also made available in Spring 3.1.
*
- *
${...}
+ * {@link PropertyPlaceholderConfigurer} is still appropriate for use when:
+ *
+ * the {@link org.springframework.context spring-context} module is not available (i.e., one is using
+ * Spring's {@code BeanFactory} API as opposed to {@code ApplicationContext}).
+ * existing configuration makes use of the {@link #setSystemPropertiesMode(int) "systemPropertiesMode"} and/or
+ * {@link #setSystemPropertiesModeName(String) "systemPropertiesModeName"} properties. Users are encouraged to move
+ * away from using these settings, and rather configure property source search order through the container's
+ * {@code Environment}; however, exact preservation of functionality may be maintained by continuing to
+ * use {@code PropertyPlaceholderConfigurer}.
+ *
*
- * Example XML context definition:
- *
- * <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
- * <property name="driverClassName"><value>${driver}</value></property>
- * <property name="url"><value>jdbc:${dbname}</value></property>
- * </bean>
- *
- * Example properties file:
- *
- * driver=com.mysql.jdbc.Driver
- * dbname=mysql:mydb
- *
- * PropertyPlaceholderConfigurer checks simple property values, lists, maps,
- * props, and bean names in bean references. Furthermore, placeholder values can
- * also cross-reference other placeholders, like:
- *
- * rootPath=myrootdir
- * subPath=${rootPath}/subdir
- *
- * In contrast to PropertyOverrideConfigurer, this configurer allows to fill in
- * explicit placeholders in context definitions. Therefore, the original definition
- * cannot specify any default values for such bean properties, and the placeholder
- * properties file is supposed to contain an entry for each defined placeholder.
- *
- * If a configurer cannot resolve a placeholder, a BeanDefinitionStoreException
- * will be thrown. If you want to check against multiple properties files, specify
- * multiple resources via the "locations" setting. You can also define multiple
- * PropertyPlaceholderConfigurers, each with its own placeholder syntax.
- *
- *
Default property values can be defined via "properties", to make overriding
- * definitions in properties files optional. A configurer will also check against
- * system properties (e.g. "user.dir") if it cannot resolve a placeholder with any
- * of the specified properties. This can be customized via "systemPropertiesMode".
- *
- *
Note that the context definition is aware of being incomplete;
- * this is immediately obvious to users when looking at the XML definition file.
- * Hence, placeholders have to be resolved; any desired defaults have to be
- * defined as placeholder values as well (for example in a default properties file).
- *
- *
Property values can be converted after reading them in, through overriding
- * the {@link #convertPropertyValue} method. For example, encrypted values can
- * be detected and decrypted accordingly before processing them.
+ *
Prior to Spring 3.1, the {@code } namespace element
+ * registered an instance of {@code PropertyPlaceholderConfigurer}. It will still do so if
+ * using the {@code spring-beans-3.0.xsd} definition of the namespace. That is, you can preserve
+ * registration of {@code PropertyPlaceholderConfigurer} through the namespace, even if using Spring 3.1;
+ * simply do not update your {@code xsi:schemaLocation} and continue using the 3.0 XSD.
*
* @author Juergen Hoeller
* @author Chris Beams
* @since 02.10.2003
- * @see #setLocations
- * @see #setProperties
- * @see #setPlaceholderPrefix
- * @see #setPlaceholderSuffix
* @see #setSystemPropertiesModeName
- * @see System#getProperty(String)
- * @see #convertPropertyValue
+ * @see AbstractPropertyPlaceholderConfigurer
* @see PropertyOverrideConfigurer
+ * @see org.springframework.context.support.PropertySourcesPlaceholderConfigurer
*/
public class PropertyPlaceholderConfigurer extends AbstractPropertyPlaceholderConfigurer
implements BeanNameAware, BeanFactoryAware {
diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/config/PropertyResourceConfigurer.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/config/PropertyResourceConfigurer.java
index ffcd35d9bc0..8da5a7772b1 100644
--- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/config/PropertyResourceConfigurer.java
+++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/config/PropertyResourceConfigurer.java
@@ -55,15 +55,24 @@ public abstract class PropertyResourceConfigurer extends PropertiesLoaderSupport
private int order = Ordered.LOWEST_PRECEDENCE; // default: same as non-Ordered
+ /**
+ * Set the order value of this object for sorting purposes.
+ * @see PriorityOrdered
+ */
public void setOrder(int order) {
- this.order = order;
+ this.order = order;
}
public int getOrder() {
- return this.order;
+ return this.order;
}
+ /**
+ * {@linkplain #mergeProperties Merge}, {@linkplain #convertProperties convert} and
+ * {@linkplain #processProperties process} properties against the given bean factory.
+ * @throws BeanInitializationException if any properties cannot be loaded
+ */
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
try {
Properties mergedProps = mergeProperties();
@@ -88,7 +97,7 @@ public abstract class PropertyResourceConfigurer extends PropertiesLoaderSupport
* @see #processProperties
*/
protected void convertProperties(Properties props) {
- Enumeration propertyNames = props.propertyNames();
+ Enumeration> propertyNames = props.propertyNames();
while (propertyNames.hasMoreElements()) {
String propertyName = (String) propertyNames.nextElement();
String propertyValue = props.getProperty(propertyName);
diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinitionReader.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinitionReader.java
index 25fd17c84df..333757b75f9 100644
--- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinitionReader.java
+++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinitionReader.java
@@ -39,6 +39,7 @@ import org.springframework.util.Assert;
* and the class loader to use for loading bean classes.
*
* @author Juergen Hoeller
+ * @author Chris Beams
* @since 11.12.2003
* @see BeanDefinitionReaderUtils
*/
@@ -140,16 +141,14 @@ public abstract class AbstractBeanDefinitionReader implements EnvironmentCapable
}
/**
- * TODO SPR-7508: document
- * @param environment
+ * Set the Environment to use when reading bean definitions. Most often used
+ * for evaluating profile information to determine which bean definitions
+ * should be read and which should be omitted.
*/
public void setEnvironment(Environment environment) {
this.environment = environment;
}
- /**
- * TODO SPR-7508: document
- */
public Environment getEnvironment() {
return this.environment;
}
diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java
index 69701005050..6b540075340 100644
--- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java
+++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java
@@ -70,7 +70,6 @@ import org.springframework.core.NamedThreadLocal;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.DefaultEnvironment;
-import org.springframework.core.env.Environment;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
@@ -170,8 +169,6 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
private final ThreadLocal prototypesCurrentlyInCreation =
new NamedThreadLocal("Prototype beans currently in creation");
- private ConfigurableEnvironment environment = new DefaultEnvironment();
-
/**
* Create a new AbstractBeanFactory.
diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java
index b9b8b8caa4f..3e8784b1817 100644
--- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java
+++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java
@@ -33,8 +33,6 @@ import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.SmartFactoryBean;
import org.springframework.core.annotation.AnnotationUtils;
-import org.springframework.core.env.ConfigurableEnvironment;
-import org.springframework.core.env.DefaultEnvironment;
import org.springframework.util.StringUtils;
/**
@@ -61,9 +59,6 @@ public class StaticListableBeanFactory implements ListableBeanFactory {
/** Map from bean name to bean instance */
private final Map beans = new HashMap();
- /** TODO SPR-7508: document */
- private ConfigurableEnvironment environment = new DefaultEnvironment();
-
/**
* Add a new singleton bean.
diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionDocumentReader.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionDocumentReader.java
index decc6cd7e5a..ec161a06843 100644
--- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionDocumentReader.java
+++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionDocumentReader.java
@@ -16,9 +16,10 @@
package org.springframework.beans.factory.xml;
-import org.w3c.dom.Document;
-
import org.springframework.beans.factory.BeanDefinitionStoreException;
+import org.springframework.core.env.Environment;
+
+import org.w3c.dom.Document;
/**
* SPI for parsing an XML document that contains Spring bean definitions.
@@ -46,4 +47,11 @@ public interface BeanDefinitionDocumentReader {
void registerBeanDefinitions(Document doc, XmlReaderContext readerContext)
throws BeanDefinitionStoreException;
+ /**
+ * Set the Environment to use when reading bean definitions. Used for evaluating
+ * profile information to determine whether a {@code } document/element should
+ * be included or omitted.
+ */
+ void setEnvironment(Environment environment);
+
}
diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionParserDelegate.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionParserDelegate.java
index de9123c2e3f..7236aa61c0f 100644
--- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionParserDelegate.java
+++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionParserDelegate.java
@@ -1476,7 +1476,7 @@ public class BeanDefinitionParserDelegate {
/**
* Determine whether the name of the supplied node is equal to the supplied name.
* The default implementation checks the supplied desired name against both
- * {@link Node#getNodeName()) and {@link Node#getLocalName()}.
+ * {@link Node#getNodeName()} and {@link Node#getLocalName()}.
*
Subclasses may override the default implementation to provide a different
* mechanism for comparing node names.
* @param node the node to compare
diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/xml/DefaultBeanDefinitionDocumentReader.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/xml/DefaultBeanDefinitionDocumentReader.java
index ab79610932a..4d0b5c260fe 100644
--- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/xml/DefaultBeanDefinitionDocumentReader.java
+++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/xml/DefaultBeanDefinitionDocumentReader.java
@@ -38,6 +38,7 @@ import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourcePatternUtils;
+import org.springframework.util.Assert;
import org.springframework.util.ResourceUtils;
import org.springframework.util.StringUtils;
@@ -88,18 +89,21 @@ public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocume
/**
- * TODO SPR-7508: document
- * @param environment
+ * {@inheritDoc}
+ *
Default value is {@code null}; property is required for parsing any
+ * {@code } element with a {@code profile} attribute present.
+ * @see #doRegisterBeanDefinitions
*/
public void setEnvironment(Environment environment) {
this.environment = environment;
}
/**
- * Parses bean definitions according to the "spring-beans" DTD. TODO SPR-7508 XSD
+ * {@inheritDoc}
+ *
This implementation parses bean definitions according to the "spring-beans" XSD
+ * (or DTD, historically).
*
Opens a DOM Document; then initializes the default settings
- * specified at <beans> level; then parses
- * the contained bean definitions.
+ * specified at the {@code } level; then parses the contained bean definitions.
*/
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
@@ -110,17 +114,24 @@ public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocume
doRegisterBeanDefinitions(root);
}
+ /**
+ * Register each bean definition within the given root {@code } element.
+ * @throws IllegalStateException if {@code elements will cause recursion in this method. in
+ // any nested elements will cause recursion in this method. In
// order to propagate and preserve default-* attributes correctly,
// keep track of the current (parent) delegate, which may be null. Create
// the new (child) delegate with a reference to the parent for fallback purposes,
@@ -212,7 +223,7 @@ public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocume
}
// Resolve system properties: e.g. "${user.dir}"
- location = environment.resolveRequiredPlaceholders(location);
+ location = environment.getPropertyResolver().resolveRequiredPlaceholders(location);
Set actualResources = new LinkedHashSet(4);
diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanDefinitionReader.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanDefinitionReader.java
index baf2ccc4e8e..d858fe4defa 100644
--- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanDefinitionReader.java
+++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanDefinitionReader.java
@@ -487,12 +487,8 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
* @see BeanDefinitionDocumentReader#registerBeanDefinitions
*/
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
- // Read document based on new BeanDefinitionDocumentReader SPI. // TODO SPR-7508: polish - remove comment
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
- // TODO SPR-7508: remove ugly cast
- if (documentReader instanceof DefaultBeanDefinitionDocumentReader) {
- ((DefaultBeanDefinitionDocumentReader)documentReader).setEnvironment(this.getEnvironment());
- }
+ documentReader.setEnvironment(this.getEnvironment());
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanFactory.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanFactory.java
index 4abaa002a6f..077d76cdaeb 100644
--- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanFactory.java
+++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2007 the original author or authors.
+ * 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.
@@ -19,7 +19,6 @@ package org.springframework.beans.factory.xml;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
-import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.io.Resource;
/**
@@ -35,7 +34,8 @@ import org.springframework.core.io.Resource;
* This class registers each bean definition with the {@link DefaultListableBeanFactory}
* superclass, and relies on the latter's implementation of the {@link BeanFactory} interface.
* It supports singletons, prototypes, and references to either of these kinds of bean.
- * See "spring-beans-2.0.dtd" for details on options and configuration style. // TODO SPR-7508 polish - s/dtd/xsd/
+ * See {@code "spring-beans-3.x.xsd"} (or historically, {@code "spring-beans-2.0.dtd"}) for
+ * details on options and configuration style.
*
*
For advanced needs, consider using a {@link DefaultListableBeanFactory} with
* an {@link XmlBeanDefinitionReader}. The latter allows for reading from multiple XML
@@ -63,7 +63,7 @@ public class XmlBeanFactory extends DefaultListableBeanFactory {
* @throws BeansException in case of loading or parsing errors
*/
public XmlBeanFactory(Resource resource) throws BeansException {
- this(resource, (BeanFactory)null);
+ this(resource, null);
}
/**
diff --git a/org.springframework.beans/src/main/resources/org/springframework/beans/factory/xml/spring-beans-3.1.xsd b/org.springframework.beans/src/main/resources/org/springframework/beans/factory/xml/spring-beans-3.1.xsd
index e4837178674..ec75e95e074 100644
--- a/org.springframework.beans/src/main/resources/org/springframework/beans/factory/xml/spring-beans-3.1.xsd
+++ b/org.springframework.beans/src/main/resources/org/springframework/beans/factory/xml/spring-beans-3.1.xsd
@@ -36,8 +36,6 @@
There is also support for lists, sets, maps, and java.util.Properties
as bean property types or constructor argument types.
-
- TODO:SPR-7508: Document use of nested beans elements.
]]>
@@ -63,8 +61,11 @@
and other elements, typically the root element in the document.
+ Allows the definition of default values for all nested bean definitions. May itself
+ be nested for the purpose of defining a subset of beans with certain default values or
+ to be registered only when certain profile(s) are active. Any such nested element
+ must be declared as the last element in the document.
]]>
@@ -81,16 +82,20 @@
element may be parsed. May be a comma-delimited
+ list in the case of multiple profiles. If one or more of the specified profiles are active at time
+ of parsing, the element will be parsed, and all of its elements registered,
+ <import> elements followed, etc. If none of the specified profiles are active at time of parsing,
+ then the entire element and its contents will be ignored.
+
+ Profiles are activated in one of two ways:
+ Programmatic:
+ ConfigurableEnvironment#setActiveProfiles(String...)
+ ConfigurableEnvironment#setDefaultProfiles(String...)
+
+ Properties (typically through -D system properties, environment variables, or servlet context init params):
+ spring.profile.active=p1,p2
+ spring.profile.default=p1,p2
]]>
@@ -115,7 +120,7 @@
diff --git a/org.springframework.beans/src/test/java/org/springframework/beans/factory/config/PropertyPlaceholderConfigurerTests.java b/org.springframework.beans/src/test/java/org/springframework/beans/factory/config/PropertyPlaceholderConfigurerTests.java
index bbbd1eaacee..9e039550a74 100644
--- a/org.springframework.beans/src/test/java/org/springframework/beans/factory/config/PropertyPlaceholderConfigurerTests.java
+++ b/org.springframework.beans/src/test/java/org/springframework/beans/factory/config/PropertyPlaceholderConfigurerTests.java
@@ -20,6 +20,7 @@ import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertThat;
+import static org.springframework.beans.factory.support.BeanDefinitionBuilder.genericBeanDefinition;
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.rootBeanDefinition;
import static org.springframework.beans.factory.support.BeanDefinitionReaderUtils.registerWithGeneratedName;
@@ -33,13 +34,16 @@ import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
import test.beans.TestBean;
/**
- * Unit tests for {@link EnvironmentAwarePropertyPlaceholderConfigurer}.
+ * Unit tests for {@link PropertyPlaceholderConfigurer}.
*
+ * @see PropertySourcesPlaceholderConfigurerTests
* @see PropertyResourceConfigurerTests
* @author Chris Beams
*/
@@ -50,7 +54,7 @@ public class PropertyPlaceholderConfigurerTests {
private static final String P1_SYSTEM_ENV_VAL = "p1SystemEnvVal";
private DefaultListableBeanFactory bf;
- private AbstractPropertyPlaceholderConfigurer ppc;
+ private PropertyPlaceholderConfigurer ppc;
private Properties ppcProperties;
private AbstractBeanDefinition p1BeanDef;
@@ -79,9 +83,19 @@ public class PropertyPlaceholderConfigurerTests {
}
- // -------------------------------------------------------------------------
- // Tests to ensure backward-compatibility for Environment refactoring
- // -------------------------------------------------------------------------
+ @Test
+ public void localPropertiesViaResource() {
+ DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
+ bf.registerBeanDefinition("testBean",
+ genericBeanDefinition(TestBean.class)
+ .addPropertyValue("name", "${my.name}")
+ .getBeanDefinition());
+
+ PropertyPlaceholderConfigurer pc = new PropertyPlaceholderConfigurer();
+ Resource resource = new ClassPathResource("PropertyPlaceholderConfigurerTests.properties", this.getClass());
+ pc.setLocation(resource);
+ pc.postProcessBeanFactory(bf);
+ }
@Test
public void resolveFromSystemProperties() {
@@ -115,7 +129,6 @@ public class PropertyPlaceholderConfigurerTests {
assertThat(bean.getName(), equalTo(P1_LOCAL_PROPS_VAL));
}
- /*
@Test
public void setSystemSystemPropertiesMode_toOverride_andResolveFromSystemProperties() {
registerWithGeneratedName(p1BeanDef, bf);
@@ -145,7 +158,6 @@ public class PropertyPlaceholderConfigurerTests {
TestBean bean = bf.getBean(TestBean.class);
assertThat(bean.getName(), equalTo(P1_LOCAL_PROPS_VAL)); // has to resort to local props
}
- */
/**
* Creates a scenario in which two PPCs are configured, each with different
@@ -231,34 +243,6 @@ public class PropertyPlaceholderConfigurerTests {
}
- // -------------------------------------------------------------------------
- // Tests for functionality not possible prior to Environment refactoring
- // -------------------------------------------------------------------------
-
- /**
- * Tests that properties against a BeanFactory's Environment are used by
- * PropertyPlaceholderConfigurer during placeholder resolution.
- @Test @SuppressWarnings({ "unchecked", "rawtypes", "serial" })
- public void replacePlaceholdersFromBeanFactoryEnvironmentPropertySources() {
- System.setProperty("key1", "systemValue");
-
- DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
- bf.getEnvironment().addPropertySource("psCustom", new HashMap() {{ put("key1", "customValue"); }});
- bf.registerBeanDefinition("testBean",
- rootBeanDefinition(TestBean.class).addPropertyValue("name", "${key1}").getBeanDefinition());
-
- new PropertyPlaceholderConfigurer().postProcessBeanFactory(bf);
- assertThat(bf.getBean(TestBean.class).getName(), is("customValue"));
-
- System.clearProperty("key1");
- }
- */
-
-
- // -------------------------------------------------------------------------
- // Utilities
- // -------------------------------------------------------------------------
-
// TODO SPR-7508: duplicated from EnvironmentPropertyResolutionSearchTests
@SuppressWarnings("unchecked")
private static Map getModifiableSystemEnvironment() {
diff --git a/org.springframework.beans/src/test/java/org/springframework/beans/factory/config/PropertyPlaceholderConfigurerTests.properties b/org.springframework.beans/src/test/java/org/springframework/beans/factory/config/PropertyPlaceholderConfigurerTests.properties
new file mode 100644
index 00000000000..b8f69781481
--- /dev/null
+++ b/org.springframework.beans/src/test/java/org/springframework/beans/factory/config/PropertyPlaceholderConfigurerTests.properties
@@ -0,0 +1 @@
+my.name=foo
\ No newline at end of file
diff --git a/org.springframework.beans/src/test/java/org/springframework/beans/factory/config/PropertyResourceConfigurerTests.java b/org.springframework.beans/src/test/java/org/springframework/beans/factory/config/PropertyResourceConfigurerTests.java
index ca4d5afbefb..fa790bdd99c 100644
--- a/org.springframework.beans/src/test/java/org/springframework/beans/factory/config/PropertyResourceConfigurerTests.java
+++ b/org.springframework.beans/src/test/java/org/springframework/beans/factory/config/PropertyResourceConfigurerTests.java
@@ -800,7 +800,7 @@ public final class PropertyResourceConfigurerTests {
Preferences.systemRoot().node("mySystemPath/mypath").remove("myName");
}
- /* TODO SPR-7508: uncomment after EnvironmentAwarePropertyPlaceholderConfigurer implementation
+ /* TODO SPR-7508: uncomment after PropertySourcesPlaceholderConfigurer implementation
@Test
public void testPreferencesPlaceholderConfigurerWithCustomPropertiesInEnvironment() {
factory.registerBeanDefinition("tb",
diff --git a/org.springframework.context/src/main/java/org/springframework/context/ConfigurableApplicationContext.java b/org.springframework.context/src/main/java/org/springframework/context/ConfigurableApplicationContext.java
index 08fa69d06ab..fa57b64cb56 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/ConfigurableApplicationContext.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/ConfigurableApplicationContext.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2009 the original author or authors.
+ * Copyright 2002-2010 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.
@@ -96,12 +96,13 @@ public interface ConfigurableApplicationContext extends ApplicationContext, Life
void setParent(ApplicationContext parent);
/**
- * TODO SPR-7508: document
+ * Return the Environment for this application context in configurable form.
*/
ConfigurableEnvironment getEnvironment();
/**
- * TODO SPR-7508: document
+ * Set the {@code Environment} for this application context.
+ * @param environment the new environment
*/
void setEnvironment(ConfigurableEnvironment environment);
diff --git a/org.springframework.context/src/main/java/org/springframework/context/EnvironmentAware.java b/org.springframework.context/src/main/java/org/springframework/context/EnvironmentAware.java
index 7ec52896b4c..d71b5ee3043 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/EnvironmentAware.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/EnvironmentAware.java
@@ -19,12 +19,17 @@ package org.springframework.context;
import org.springframework.core.env.Environment;
/**
- * TODO SPR-7515: document
+ * Interface to be implemented by any bean that wishes to be notified
+ * of the {@link Environment} that it runs in.
*
* @author Chris Beams
+ * @since 3.1
*/
public interface EnvironmentAware {
+ /**
+ * Set the {@code Environment} that this object runs in.
+ */
void setEnvironment(Environment environment);
}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java
index 8aebaa07b96..aa8a2117fd3 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2009 the original author or authors.
+ * Copyright 2002-2010 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.
@@ -50,9 +50,9 @@ public class AnnotatedBeanDefinitionReader {
/**
- * Create a new AnnotatedBeanDefinitionReader for the given bean factory.
- * @param registry the BeanFactory to load bean definitions into,
- * in the form of a BeanDefinitionRegistry
+ * Create a new {@code AnnotatedBeanDefinitionReader} for the given bean factory.
+ * @param registry the {@code BeanFactory} to load bean definitions into,
+ * in the form of a {@code BeanDefinitionRegistry}
*/
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
this.registry = registry;
@@ -68,8 +68,10 @@ public class AnnotatedBeanDefinitionReader {
}
/**
- * Set the Environment to use when registering classes.
+ * Set the Environment to use when evaluating whether
+ * {@link Profile @Profile}-annotated component classes should be registered.
* The default is a {@link DefaultEnvironment}.
+ * @see #registerBean(Class, String, Class...)
*/
public void setEnvironment(Environment environment) {
this.environment = environment;
@@ -80,7 +82,8 @@ public class AnnotatedBeanDefinitionReader {
*
The default is a {@link AnnotationBeanNameGenerator}.
*/
public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) {
- this.beanNameGenerator = (beanNameGenerator != null ? beanNameGenerator : new AnnotationBeanNameGenerator());
+ this.beanNameGenerator = (beanNameGenerator != null ?
+ beanNameGenerator : new AnnotationBeanNameGenerator());
}
/**
@@ -88,7 +91,8 @@ public class AnnotatedBeanDefinitionReader {
*
The default is an {@link AnnotationScopeMetadataResolver}.
*/
public void setScopeMetadataResolver(ScopeMetadataResolver scopeMetadataResolver) {
- this.scopeMetadataResolver = (scopeMetadataResolver != null ? scopeMetadataResolver : new AnnotationScopeMetadataResolver());
+ this.scopeMetadataResolver = (scopeMetadataResolver != null ?
+ scopeMetadataResolver : new AnnotationScopeMetadataResolver());
}
@@ -110,10 +114,8 @@ public class AnnotatedBeanDefinitionReader {
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
AnnotationMetadata metadata = abd.getMetadata();
- if (metadata.hasAnnotation(Profile.class.getName())) {
- String[] specifiedProfiles =
- (String[])metadata.getAnnotationAttributes(Profile.class.getName()).get(Profile.CANDIDATE_PROFILES_ATTRIB_NAME);
- if (!this.environment.acceptsProfiles(specifiedProfiles)) {
+ if (Profile.Helper.isProfileAnnotationPresent(metadata)) {
+ if (!this.environment.acceptsProfiles(Profile.Helper.getCandidateProfiles(metadata))) {
// TODO SPR-7508: log that this bean is being rejected on profile mismatch
return;
}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotationConfigApplicationContext.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotationConfigApplicationContext.java
index bf696d13b87..c596447a97b 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotationConfigApplicationContext.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotationConfigApplicationContext.java
@@ -16,17 +16,15 @@
package org.springframework.context.annotation;
-import org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
-import org.springframework.core.env.Environment;
/**
* Standalone application context, accepting annotated classes as input - in particular
* {@link org.springframework.context.annotation.Configuration @Configuration}-annotated
* classes, but also plain {@link org.springframework.stereotype.Component @Components}
- * and JSR-330 compliant classes using {@literal javax.inject} annotations. Allows for
+ * and JSR-330 compliant classes using {@code javax.inject} annotations. Allows for
* registering classes one by one ({@link #register}) as well as for classpath scanning
* ({@link #scan}).
*
@@ -49,15 +47,13 @@ public class AnnotationConfigApplicationContext extends GenericApplicationContex
private final ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this);
- { // TODO: rework this, it's a bit confusing
- this.setEnvironment(this.getEnvironment());
- }
/**
* Create a new AnnotationConfigApplicationContext that needs to be populated
- * through {@link #register} calls and then manually {@link #refresh refreshed}.
+ * through {@link #register} calls and then manually {@linkplain #refresh refreshed}.
*/
public AnnotationConfigApplicationContext() {
+ this.delegateEnvironment(super.getEnvironment());
}
/**
@@ -67,6 +63,7 @@ public class AnnotationConfigApplicationContext extends GenericApplicationContex
* e.g. {@link Configuration @Configuration} classes
*/
public AnnotationConfigApplicationContext(Class>... annotatedClasses) {
+ this();
register(annotatedClasses);
refresh();
}
@@ -77,16 +74,24 @@ public class AnnotationConfigApplicationContext extends GenericApplicationContex
* @param basePackages the packages to check for annotated classes
*/
public AnnotationConfigApplicationContext(String... basePackages) {
+ this();
scan(basePackages);
refresh();
}
+
/**
- * TODO SPR-7508: document
+ * {@inheritDoc}
+ *
Delegates given environment to underlying {@link AnnotatedBeanDefinitionReader}
+ * and {@link ClassPathBeanDefinitionScanner} members.
*/
@Override
public void setEnvironment(ConfigurableEnvironment environment) {
super.setEnvironment(environment);
+ delegateEnvironment(environment);
+ }
+
+ private void delegateEnvironment(ConfigurableEnvironment environment) {
this.reader.setEnvironment(environment);
this.scanner.setEnvironment(environment);
}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/Bean.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/Bean.java
index de79ccbb16a..5d65222a1c7 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/Bean.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/Bean.java
@@ -27,11 +27,11 @@ import org.springframework.beans.factory.annotation.Autowire;
/**
* Indicates that a method produces a bean to be managed by the Spring container. The
* names and semantics of the attributes to this annotation are intentionally similar
- * to those of the {@literal } element in the Spring XML schema.
+ * to those of the {@code } element in the Spring XML schema.
*
- *
Note that the @Bean annotation does not provide attributes for scope,
- * primary or lazy. Rather, it should be used in conjunction with {@link Scope @Scope},
- * {@link Primary @Primary}, and {@link Lazy @Lazy} annotations to achieve
+ *
Note that the {@code @Bean} annotation does not provide attributes for scope,
+ * primary or lazy. Rather, it should be used in conjunction with {@link Scope @Scope},
+ * {@link Primary @Primary}, and {@link Lazy @Lazy} annotations to achieve
* those semantics. The same annotations can also be used at the type level, e.g. for
* component scanning.
*
@@ -96,7 +96,7 @@ public @interface Bean {
/**
* The optional name of a method to call on the bean instance upon closing the
- * application context, for example a {@literal close()} method on a {@literal DataSource}.
+ * application context, for example a {@code close()} method on a {@code DataSource}.
* The method must have no arguments but may throw any exception.
*
Note: Only invoked on beans whose lifecycle is under the full control of the
* factory, which is always the case for singletons but not guaranteed
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java
index 72662c44f62..ca9c47d73fe 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java
@@ -116,15 +116,15 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
}
/**
- * TODO SPR-7508: document
+ * Set the Environment to use when resolving placeholders and evaluating
+ * {@link Profile @Profile}-annotated component classes.
+ *
The default is a {@link DefaultEnvironment}
+ * @param environment the Environment to use
*/
public void setEnvironment(Environment environment) {
this.environment = environment;
}
- /**
- * TODO SPR-7508: document
- */
public Environment getEnvironment() {
return this.environment;
}
@@ -280,7 +280,7 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
* @return the pattern specification to be used for package searching
*/
protected String resolveBasePackage(String basePackage) {
- return ClassUtils.convertClassNameToResourcePath(environment.resolveRequiredPlaceholders(basePackage));
+ return ClassUtils.convertClassNameToResourcePath(environment.getPropertyResolver().resolveRequiredPlaceholders(basePackage));
}
/**
@@ -298,12 +298,11 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, this.metadataReaderFactory)) {
AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
- if (!metadata.hasAnnotation(Profile.class.getName())) {
+ if (!Profile.Helper.isProfileAnnotationPresent(metadata)) {
return true;
}
- String[] specifiedProfiles =
- (String[])metadata.getAnnotationAttributes(Profile.class.getName()).get(Profile.CANDIDATE_PROFILES_ATTRIB_NAME);
- return this.environment.acceptsProfiles(specifiedProfiles);
+ // TODO SPR-7508: log that this bean is being rejected on profile mismatch
+ return this.environment.acceptsProfiles(Profile.Helper.getCandidateProfiles(metadata));
}
}
return false;
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ComponentScan.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ComponentScan.java
index 9acfd337ad8..043a485ea3c 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ComponentScan.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ComponentScan.java
@@ -27,7 +27,7 @@ import org.springframework.beans.factory.support.BeanNameGenerator;
/**
* Configures component scanning directives for use with {@link Configuration}
* classes. Provides support parallel with Spring XML's
- * <context:component-scan> element.
+ * {@code } element.
*
* TODO SPR-7508: complete documentation.
*
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
index f9270f545a5..21d1fd3e56d 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
@@ -101,16 +101,14 @@ class ConfigurationClassParser {
}
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
- if (this.environment != null && configClass.getMetadata().hasAnnotation(Profile.class.getName())) {
- String[] specifiedProfiles =
- (String[])configClass.getMetadata().getAnnotationAttributes(Profile.class.getName()).get(Profile.CANDIDATE_PROFILES_ATTRIB_NAME);
- if (!this.environment.acceptsProfiles(specifiedProfiles)) {
+ AnnotationMetadata metadata = configClass.getMetadata();
+ if (this.environment != null && Profile.Helper.isProfileAnnotationPresent(metadata)) {
+ if (!this.environment.acceptsProfiles(Profile.Helper.getCandidateProfiles(metadata))) {
// TODO SPR-7508: log that this bean is being rejected on profile mismatch
return;
}
}
- AnnotationMetadata metadata = configClass.getMetadata();
while (metadata != null) {
doProcessConfigurationClass(configClass, metadata);
String superClassName = metadata.getSuperClassName();
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java
index 217df8d2eb2..aa5681648c8 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java
@@ -49,8 +49,8 @@ import org.springframework.util.ClassUtils;
* {@link BeanFactoryPostProcessor} used for bootstrapping processing of
* {@link Configuration @Configuration} classes.
*
- * Registered by default when using {@literal } or
- * {@literal }. Otherwise, may be declared manually as
+ *
Registered by default when using {@code } or
+ * {@code }. Otherwise, may be declared manually as
* with any other BeanFactoryPostProcessor.
*
*
This post processor is {@link Ordered#HIGHEST_PRECEDENCE} as it is important
@@ -100,7 +100,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
/**
* Set the {@link ProblemReporter} to use.
*
Used to register any problems detected with {@link Configuration} or {@link Bean}
- * declarations. For instance, an @Bean method marked as {@literal final} is illegal
+ * declarations. For instance, an @Bean method marked as {@code final} is illegal
* and would be reported as a problem. Defaults to {@link FailFastProblemReporter}.
*/
public void setProblemReporter(ProblemReporter problemReporter) {
@@ -110,7 +110,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
/**
* Set the {@link MetadataReaderFactory} to use.
*
Default is a {@link CachingMetadataReaderFactory} for the specified
- * {@link #setBeanClassLoader bean class loader}.
+ * {@linkplain #setBeanClassLoader bean class loader}.
*/
public void setMetadataReaderFactory(MetadataReaderFactory metadataReaderFactory) {
Assert.notNull(metadataReaderFactory, "MetadataReaderFactory must not be null");
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/DependsOn.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/DependsOn.java
index 7380032a4ab..1fee1a0f9fa 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/DependsOn.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/DependsOn.java
@@ -38,7 +38,7 @@ import java.lang.annotation.Documented;
*
Using {@link DependsOn} at the class level has no effect unless component-scanning
* is being used. If a {@link DependsOn}-annotated class is declared via XML,
* {@link DependsOn} annotation metadata is ignored, and
- * {@literal } is respected instead.
+ * {@code } is respected instead.
*
* @author Juergen Hoeller
* @since 3.0
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/Import.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/Import.java
index 61c14a9a6c5..4f406fdd663 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/Import.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/Import.java
@@ -23,18 +23,18 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
- * Indicates one or more {@link Configuration} classes to import.
+ * Indicates one or more {@link Configuration @Configuration} classes to import.
*
- *
Provides functionality equivalent to the {@literal } element in Spring XML.
- * Only supported for actual {@literal @Configuration}-annotated classes.
+ *
Provides functionality equivalent to the {@code } element in Spring XML.
+ * Only supported for actual {@code @Configuration}-annotated classes.
*
- *
{@literal @Bean} definitions declared in imported {@literal @Configuration} classes
+ *
{@code @Bean} definitions declared in imported {@code @Configuration} classes
* should be accessed by using {@link Autowired @Autowired} injection. Either the bean
* itself can be autowired, or the configuration class instance declaring the bean can be
* autowired. The latter approach allows for explicit, IDE-friendly navigation between
- * {@literal @Configuration} class methods.
+ * {@code @Configuration} class methods.
*
- *
If XML or other non-{@literal @Configuration} bean definition resources need to be
+ *
If XML or other non-{@code @Configuration} bean definition resources need to be
* imported, use {@link ImportResource @ImportResource}
*
* @author Chris Beams
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ImportResource.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ImportResource.java
index 9aa10b99f5a..159ba67b647 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ImportResource.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ImportResource.java
@@ -30,14 +30,14 @@ import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
* Indicates one or more resources containing bean definitions to import.
*
*
Like {@link Import @Import}, this annotation provides functionality similar to the
- * {@literal } element in Spring XML. It is typically used when
+ * {@code } element in Spring XML. It is typically used when
* designing {@link Configuration @Configuration} classes to be bootstrapped by
* {@link AnnotationConfigApplicationContext}, but where some XML functionality such as
* namespaces is still necessary.
*
*
By default, arguments to the {@link #value()} attribute will be processed using
* an {@link XmlBeanDefinitionReader}, i.e. it is assumed that resources are Spring
- * {@literal } XML files. Optionally, the {@link #reader()} attribute may be
+ * {@code } XML files. Optionally, the {@link #reader()} attribute may be
* supplied, allowing the user to specify a different {@link BeanDefinitionReader}
* implementation, such as
* {@link org.springframework.beans.factory.support.PropertiesBeanDefinitionReader}.
@@ -53,8 +53,8 @@ import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
public @interface ImportResource {
/**
- * Resource paths to import. Resource-loading prefixes such as {@literal classpath:} and
- * {@literal file:}, etc may be used.
+ * Resource paths to import. Resource-loading prefixes such as {@code classpath:} and
+ * {@code file:}, etc may be used.
*/
String[] value();
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/Lazy.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/Lazy.java
index fa17b493978..0c0890f2aa4 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/Lazy.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/Lazy.java
@@ -25,25 +25,25 @@ import java.lang.annotation.Inherited;
/**
* Indicates whether a bean is to be lazily initialized.
- *
+ *
*
May be used on any class directly or indirectly annotated with
* {@link org.springframework.stereotype.Component} or on methods annotated with
* {@link Bean}.
- *
+ *
*
If this annotation is not present on a Component or Bean definition, eager
- * initialization will occur. If present and set to {@literal true}, the
+ * initialization will occur. If present and set to {@code true}, the
* Bean/Component will not be initialized until referenced by another bean or
* explicitly retrieved from the enclosing
* {@link org.springframework.beans.factory.BeanFactory}. If present and set to
- * {@literal false}, the bean will be instantiated on startup by bean factories
+ * {@code false}, the bean will be instantiated on startup by bean factories
* that perform eager initialization of singletons.
- *
- *
If Lazy is present on a {@link Configuration} class, this indicates that all
- * {@link Bean} methods within that {@literal Configuration} should be lazily
- * initialized. If Lazy is present and false on a Bean method within a
- * Lazy-annotated Configuration class, this indicates overriding the 'default
- * lazy' behavior and that the bean should be eagerly initialized.
- *
+ *
+ *
If Lazy is present on a {@link Configuration @Configuration} class, this
+ * indicates that all {@link Bean @Bean} methods within that {@code @Configuration}
+ * should be lazily initialized. If Lazy is present and false on a Bean method
+ * within a Lazy-annotated Configuration class, this indicates overriding the
+ * 'default lazy' behavior and that the bean should be eagerly initialized.
+ *
* @author Chris Beams
* @since 3.0
* @see Primary
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/Primary.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/Primary.java
index 56307e81f32..91facf5f4f5 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/Primary.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/Primary.java
@@ -27,16 +27,16 @@ import java.lang.annotation.Target;
* Indicates that a bean should be given preference when multiple candidates
* are qualified to autowire a single-valued dependency. If exactly one 'primary'
* bean exists among the candidates, it will be the autowired value.
- *
+ *
*
May be used on any class directly or indirectly annotated with
* {@link org.springframework.stereotype.Component} or on methods annotated
* with {@link Bean}.
- *
+ *
*
Using {@link Primary} at the class level has no effect unless component-scanning
* is being used. If a {@link Primary}-annotated class is declared via XML,
* {@link Primary} annotation metadata is ignored, and
- * {@literal } is respected instead.
- *
+ * {@code } is respected instead.
+ *
* @author Chris Beams
* @since 3.0
* @see Lazy
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/Profile.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/Profile.java
index 6e6c2ea88cb..0ce97302b89 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/Profile.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/Profile.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2010 the original author or authors.
+ * 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.
@@ -16,42 +16,78 @@
package org.springframework.context.annotation;
-import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
-import static java.lang.annotation.ElementType.TYPE;
-
+import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import org.springframework.core.env.AbstractEnvironment;
+import org.springframework.core.env.ConfigurableEnvironment;
+import org.springframework.core.type.AnnotationMetadata;
+
/**
- * TODO SPR-7508: document
- *
- * Components not @Profile-annotated will always be registered
- * ConfigurableEnvironment.setActiveProfiles(String...) sets which profiles are active
- * 'spring.profile.active' sets which profiles are active (typically as a -D system property)
- servlet context/init param)
- * ConfigurableEnvironment.setDefaultProfiles(String...) or 'spring.profile.default' property specifies one
- or more default profiles, e.g., 'default'
- * if 'default' is specified as a default profile, @Profile({"xyz,default}) means that beans will be
- registered if 'xyz' is active or if no profile is active
+ * Indicates that a component is eligible for registration when one or more {@linkplain #value
+ * specified profiles} are active.
+ *
+ *
A profile is a named logical grouping that may be activated programatically via
+ * {@link ConfigurableEnvironment#setActiveProfiles} or declaratively through setting the
+ * {@link AbstractEnvironment#ACTIVE_PROFILES_PROPERTY_NAME spring.profile.active} property,
+ * usually through JVM system properties, as an environment variable, or for web applications
+ * as a Servlet context parameter in {@code web.xml}.
+ *
+ *
The {@code @Profile} annotation may be used in any of the following ways:
+ *
+ * as a type-level annotation on any class directly or indirectly annotated with
+ * {@code @Component}, including {@link Configuration @Configuration} classes
+ * as a meta-annotation, for the purpose of composing custom stereotype annotations
+ *
+ *
+ * If a {@code @Configuration} class is marked with {@code @Profile}, all of the
+ * {@code @Bean} methods and {@link Import @Import} annotations associated with that class will
+ * be bypassed unless one or more the specified profiles are active. This is very similar to
+ * the behavior in Spring XML: if the {@code profile} attribute of the {@code beans} element is
+ * supplied e.g., {@code }, the {@code beans} element will not be parsed unless
+ * profiles 'p1' and/or 'p2' have been activated. Likewise, if a {@code @Component} or
+ * {@code @Configuration} class is marked with @Profile({"p1", "p2"}), that class will
+ * not be registered/processed unless profiles 'p1' and/or 'p2' have been activated.
+ *
+ * If the {@code @Profile} annotation is omitted, registration will occur, regardless of which,
+ * if any, profiles are active.
+ *
+ *
When defining Spring beans via XML, the {@code "profile"} attribute of the {@code }
+ * element may be used. See the documentation in {@code spring-beans-3.1.xsd} for details.
*
* @author Chris Beams
* @since 3.1
+ * @see ConfigurableEnvironment#setActiveProfiles
+ * @see ConfigurableEnvironment#setDefaultProfiles
+ * @see AbstractEnvironment#ACTIVE_PROFILES_PROPERTY_NAME
+ * @see AbstractEnvironment#DEFAULT_PROFILES_PROPERTY_NAME
*/
@Retention(RetentionPolicy.RUNTIME)
-@Target({
- ANNOTATION_TYPE, // @Profile may be used as a meta-annotation
- TYPE // In conjunction with @Component and its derivatives
-})
+@Target(ElementType.TYPE)
public @interface Profile {
/**
- * @see #value()
- */
- static final String CANDIDATE_PROFILES_ATTRIB_NAME = "value";
-
- /**
- * TODO SPR-7508: document
+ * The set profiles for which this component should be registered.
*/
String[] value();
+
+
+ static class Helper {
+ /**
+ * Return whether the given metadata includes Profile information, whether directly or
+ * through meta-annotation.
+ */
+ static boolean isProfileAnnotationPresent(AnnotationMetadata metadata) {
+ return metadata.isAnnotated(Profile.class.getName());
+ }
+
+ /**
+ * Return the String[] of candidate profiles from {@link Profile#value()}.
+ */
+ static String[] getCandidateProfiles(AnnotationMetadata metadata) {
+ return (String[])metadata.getAnnotationAttributes(Profile.class.getName()).get("value");
+ }
+ }
}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/Scope.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/Scope.java
index 55409be3139..248430dc311 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/Scope.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/Scope.java
@@ -35,7 +35,7 @@ import org.springframework.stereotype.Component;
* the instance returned from the method.
*
* In this context, scope means the lifecycle of an instance, such as
- * {@literal singleton}, {@literal prototype}, and so forth.
+ * {@code singleton}, {@code prototype}, and so forth.
*
* @author Mark Fisher
* @author Chris Beams
@@ -59,7 +59,7 @@ public @interface Scope {
* and if so, whether the proxy should be interface-based or subclass-based.
*
Defaults to {@link ScopedProxyMode#NO}, indicating that no scoped
* proxy should be created.
- *
Analogous to {@literal } support in Spring XML.
+ *
Analogous to {@code } support in Spring XML.
*/
ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/package-info.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/package-info.java
index 580f39a5215..53688cb9ccc 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/package-info.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/package-info.java
@@ -1,10 +1,10 @@
/**
- *
+ *
* Annotation support for the Application Context, including JSR-250 "common"
* annotations, component-scanning, and Java-based metadata for creating
* Spring-managed objects.
- *
+ *
*/
package org.springframework.context.annotation;
diff --git a/org.springframework.context/src/main/java/org/springframework/context/config/PropertyPlaceholderBeanDefinitionParser.java b/org.springframework.context/src/main/java/org/springframework/context/config/PropertyPlaceholderBeanDefinitionParser.java
index afbc1a12231..256f61e65f0 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/config/PropertyPlaceholderBeanDefinitionParser.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/config/PropertyPlaceholderBeanDefinitionParser.java
@@ -20,7 +20,7 @@ import org.w3c.dom.Element;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
-import org.springframework.context.support.EnvironmentAwarePropertyPlaceholderConfigurer;
+import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.util.StringUtils;
/**
@@ -35,32 +35,24 @@ class PropertyPlaceholderBeanDefinitionParser extends AbstractPropertyLoadingBea
@Override
protected Class> getBeanClass(Element element) {
- // as of Spring 3.1, the default for system-properties-mode is DELEGATE,
- // meaning that the attribute should be disregarded entirely, instead
- // deferring to the order of PropertySource objects in the enclosing
- // application context's Environment object
- if (!"DELEGATE".equals(element.getAttribute("system-properties-mode"))) {
+ if (element.hasAttribute("system-properties-mode")) {
return PropertyPlaceholderConfigurer.class;
}
- return EnvironmentAwarePropertyPlaceholderConfigurer.class;
+ return PropertySourcesPlaceholderConfigurer.class;
}
@Override
protected void doParse(Element element, BeanDefinitionBuilder builder) {
-
super.doParse(element, builder);
builder.addPropertyValue("ignoreUnresolvablePlaceholders",
Boolean.valueOf(element.getAttribute("ignore-unresolvable")));
- if (!"DELEGATE".equals(element.getAttribute("system-properties-mode"))) {
- String systemPropertiesModeName = element.getAttribute("system-properties-mode");
- if (StringUtils.hasLength(systemPropertiesModeName)) {
- builder.addPropertyValue("systemPropertiesModeName", "SYSTEM_PROPERTIES_MODE_"+systemPropertiesModeName);
- }
+ String systemPropertiesModeName = element.getAttribute("system-properties-mode");
+ if (StringUtils.hasLength(systemPropertiesModeName)) {
+ builder.addPropertyValue("systemPropertiesModeName", "SYSTEM_PROPERTIES_MODE_"+systemPropertiesModeName);
}
-
}
}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/expression/EnvironmentAccessor.java b/org.springframework.context/src/main/java/org/springframework/context/expression/EnvironmentAccessor.java
index 0727a839c3e..6bd0542d7cd 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/expression/EnvironmentAccessor.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/expression/EnvironmentAccessor.java
@@ -44,11 +44,11 @@ public class EnvironmentAccessor implements PropertyAccessor {
}
/**
- * Access provided {@literal target} object by calling its {@link Environment#getProperty(String)}
- * method with the provided {@literal name}.
+ * Access the given target object by resolving the given property name against the given target
+ * environment.
*/
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
- return new TypedValue(((Environment)target).getProperty(name));
+ return new TypedValue(((Environment)target).getPropertyResolver().getProperty(name));
}
/**
diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java b/org.springframework.context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java
index 163f9945249..a09bec7fa2b 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java
@@ -206,8 +206,8 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
/** Statically specified listeners */
private Set> applicationListeners = new LinkedHashSet>();
- /** TODO SPR-7508: document */
- private ConfigurableEnvironment environment = new DefaultEnvironment();
+ /** Environment used by this context; initialized by {@link #createEnvironment()} */
+ private ConfigurableEnvironment environment;
/**
@@ -224,6 +224,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
public AbstractApplicationContext(ApplicationContext parent) {
this.parent = parent;
this.resourcePatternResolver = getResourcePatternResolver();
+ this.environment = this.createEnvironment();
}
@@ -279,6 +280,14 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
return this.environment;
}
+ /**
+ * {@inheritDoc}
+ * Default value is determined by {@link #createEnvironment()}. Replacing the
+ * default with this method is one option but configuration through {@link
+ * #getEnvironment()} should also be considered. In either case, such modifications
+ * should be performed before {@link #refresh()}.
+ * @see org.springframework.context.support.AbstractApplicationContext#createEnvironment
+ */
public void setEnvironment(ConfigurableEnvironment environment) {
this.environment = environment;
}
@@ -400,6 +409,12 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
return this.applicationListeners;
}
+ /**
+ * Create and return a new {@link DefaultEnvironment}.
+ */
+ protected ConfigurableEnvironment createEnvironment() {
+ return new DefaultEnvironment();
+ }
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
@@ -456,7 +471,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
/**
* Prepare this context for refreshing, setting its startup date and
- * active flag.
+ * active flag as well as performing any initialization of property sources.
*/
protected void prepareRefresh() {
this.startupDate = System.currentTimeMillis();
@@ -468,6 +483,18 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
if (logger.isInfoEnabled()) {
logger.info("Refreshing " + this);
}
+
+ // Initialize any placeholder property sources in the context environment
+ initPropertySources();
+ }
+
+ /**
+ *
Replace any stub property sources with actual instances.
+ * @see org.springframework.core.env.PropertySource.StubPropertySource
+ * @see org.springframework.web.context.support.WebApplicationContextUtils#initSerlvetPropertySources
+ */
+ protected void initPropertySources() {
+ // For subclasses: do nothing by default.
}
/**
diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/AbstractRefreshableApplicationContext.java b/org.springframework.context/src/main/java/org/springframework/context/support/AbstractRefreshableApplicationContext.java
index b0a7aa9c87a..b868db75aa4 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/support/AbstractRefreshableApplicationContext.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/support/AbstractRefreshableApplicationContext.java
@@ -28,7 +28,7 @@ import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
/**
* Base class for {@link org.springframework.context.ApplicationContext}
- * implementations which are supposed to support multiple calls to {@literal refresh},
+ * implementations which are supposed to support multiple calls to {@link #refresh()},
* creating a new internal bean factory instance every time.
* Typically (but not necessarily), such a context will be driven by
* a set of config locations to load bean definitions from.
@@ -50,7 +50,7 @@ import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
* and {@link FileSystemXmlApplicationContext}, which both derive from the
* common {@link AbstractXmlApplicationContext} base class;
* {@link org.springframework.context.annotation.AnnotationConfigApplicationContext}
- * supports {@literal @Configuration}-annotated classes as a source of bean definitions.
+ * supports {@code @Configuration}-annotated classes as a source of bean definitions.
*
* @author Juergen Hoeller
* @author Chris Beams
@@ -182,7 +182,7 @@ public abstract class AbstractRefreshableApplicationContext extends AbstractAppl
* Called for each {@link #refresh()} attempt.
*
The default implementation creates a
* {@link org.springframework.beans.factory.support.DefaultListableBeanFactory}
- * with the {@link #getInternalParentBeanFactory() internal bean factory} of this
+ * with the {@linkplain #getInternalParentBeanFactory() internal bean factory} of this
* context's parent as parent bean factory. Can be overridden in subclasses,
* for example to customize DefaultListableBeanFactory's settings.
* @return the bean factory for this context
@@ -199,8 +199,8 @@ public abstract class AbstractRefreshableApplicationContext extends AbstractAppl
* Customize the internal bean factory used by this context.
* Called for each {@link #refresh()} attempt.
*
The default implementation applies this context's
- * {@link #setAllowBeanDefinitionOverriding "allowBeanDefinitionOverriding"}
- * and {@link #setAllowCircularReferences "allowCircularReferences"} settings,
+ * {@linkplain #setAllowBeanDefinitionOverriding "allowBeanDefinitionOverriding"}
+ * and {@linkplain #setAllowCircularReferences "allowCircularReferences"} settings,
* if specified. Can be overridden in subclasses to customize any of
* {@link DefaultListableBeanFactory}'s settings.
* @param beanFactory the newly created bean factory for this context
diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/AbstractRefreshableConfigApplicationContext.java b/org.springframework.context/src/main/java/org/springframework/context/support/AbstractRefreshableConfigApplicationContext.java
index 79a40093232..66859badd2c 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/support/AbstractRefreshableConfigApplicationContext.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/support/AbstractRefreshableConfigApplicationContext.java
@@ -17,11 +17,11 @@
package org.springframework.context.support;
import org.springframework.beans.factory.BeanNameAware;
+
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
-import org.springframework.util.SystemPropertyUtils;
/**
* {@link AbstractRefreshableApplicationContext} subclass that adds common handling
@@ -117,14 +117,9 @@ public abstract class AbstractRefreshableConfigApplicationContext extends Abstra
* system property values if necessary. Applied to config locations.
* @param path the original file path
* @return the resolved file path
- * @see SystemPropertyUtils#resolveRequiredPlaceholders(String)
*/
protected String resolvePath(String path) {
- // TODO SPR-7508: note that ARAC cannot delegate to its beanFactory's environment
- // to call Environment.resolve[Required]Placeholders(String), as the bean factory
- // has not yet been initialized. This amounts to one more reason not to use the ARAC
- // hierarchy - it won't have early access to environment property resolution.
- return SystemPropertyUtils.resolvePlaceholders(path);
+ return this.getEnvironment().getPropertyResolver().resolveRequiredPlaceholders(path);
}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/EnvironmentAwarePropertyPlaceholderConfigurer.java b/org.springframework.context/src/main/java/org/springframework/context/support/EnvironmentAwarePropertyPlaceholderConfigurer.java
deleted file mode 100644
index ac9693a0f9f..00000000000
--- a/org.springframework.context/src/main/java/org/springframework/context/support/EnvironmentAwarePropertyPlaceholderConfigurer.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright 2002-2010 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.context.support;
-
-import java.util.LinkedList;
-import java.util.Properties;
-
-import org.springframework.beans.BeansException;
-import org.springframework.beans.factory.config.AbstractPropertyPlaceholderConfigurer;
-import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
-import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
-import org.springframework.context.EnvironmentAware;
-import org.springframework.core.env.AbstractEnvironment;
-import org.springframework.core.env.ConfigurableEnvironment;
-import org.springframework.core.env.Environment;
-import org.springframework.core.env.PropertiesPropertySource;
-import org.springframework.core.env.PropertySource;
-import org.springframework.util.Assert;
-import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver;
-
-
-/**
- * TODO SPR-7508: document
- *
- * Local properties are added as a property source in any case. Precedence is based
- * on the value of the {@link #setLocalOverride(boolean) localOverride} property.
- *
- * @author Chris Beams
- * @since 3.1
- * @see PropertyPlaceholderConfigurer
- * @see EnvironmentAwarePropertyOverrideConfigurer
- */
-public class EnvironmentAwarePropertyPlaceholderConfigurer
- extends AbstractPropertyPlaceholderConfigurer implements EnvironmentAware {
-
- private ConfigurableEnvironment environment;
- private Environment wrappedEnvironment;
-
- public void setEnvironment(Environment environment) {
- this.wrappedEnvironment = environment;
- }
-
- @Override
- protected PlaceholderResolver getPlaceholderResolver(Properties props) {
- return new PlaceholderResolver() {
- public String resolvePlaceholder(String placeholderName) {
- return environment.getProperty(placeholderName);
- }
- };
- }
-
- @Override
- public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
- Assert.notNull(this.wrappedEnvironment, "Environment must not be null. Did you call setEnvironment()?");
- environment = new AbstractEnvironment() { };
-
- LinkedList> propertySources = environment.getPropertySources();
- EnvironmentPropertySource environmentPropertySource =
- new EnvironmentPropertySource("wrappedEnvironment", wrappedEnvironment);
-
- if (!this.localOverride) {
- propertySources.add(environmentPropertySource);
- }
-
- if (this.localProperties != null) {
- int cx=0;
- for (Properties localProps : this.localProperties) {
- propertySources.add(new PropertiesPropertySource("localProperties"+cx++, localProps));
- }
- }
-
- if (this.localOverride) {
- propertySources.add(environmentPropertySource);
- }
-
- super.postProcessBeanFactory(beanFactory);
- }
-
- static class EnvironmentPropertySource extends PropertySource {
-
- public EnvironmentPropertySource(String name, Environment source) {
- super(name, source);
- }
-
- @Override
- public boolean containsProperty(String key) {
- return source.containsProperty(key);
- }
-
- @Override
- public String getProperty(String key) {
- return source.getProperty(key);
- }
-
- @Override
- public int size() {
- // TODO Auto-generated method stub
- return source.getPropertyCount();
- }
- }
-}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/GenericApplicationContext.java b/org.springframework.context/src/main/java/org/springframework/context/support/GenericApplicationContext.java
index ccd1323f51e..470af7f8260 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/support/GenericApplicationContext.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/support/GenericApplicationContext.java
@@ -28,7 +28,6 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
-import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternResolver;
diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/GenericXmlApplicationContext.java b/org.springframework.context/src/main/java/org/springframework/context/support/GenericXmlApplicationContext.java
index 24cc25e4ace..b276ba04a03 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/support/GenericXmlApplicationContext.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/support/GenericXmlApplicationContext.java
@@ -44,8 +44,8 @@ public class GenericXmlApplicationContext extends GenericApplicationContext {
/**
- * Create a new GenericXmlApplicationContext that needs to be populated
- * through {@link #load} calls and then manually {@link #refresh refreshed}.
+ * Create a new GenericXmlApplicationContext that needs to be
+ * {@linkplain #load loaded} and then manually {@link #refresh refreshed}.
*/
public GenericXmlApplicationContext() {
reader.setEnvironment(this.getEnvironment());
@@ -91,8 +91,9 @@ public class GenericXmlApplicationContext extends GenericApplicationContext {
}
/**
- * Set a custom environment. Should be called before any call to
- * {@link #load}. TODO SPR-7508: document
+ * {@inheritDoc}
+ * Delegates the given environment to underlying {@link XmlBeanDefinitionReader}.
+ * Should be called before any call to {@link #load}.
*/
@Override
public void setEnvironment(ConfigurableEnvironment environment) {
diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/PropertySourcesPlaceholderConfigurer.java b/org.springframework.context/src/main/java/org/springframework/context/support/PropertySourcesPlaceholderConfigurer.java
new file mode 100644
index 00000000000..8a775d5e5b4
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/support/PropertySourcesPlaceholderConfigurer.java
@@ -0,0 +1,134 @@
+/*
+ * 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.context.support;
+
+import java.io.IOException;
+import java.util.Properties;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanInitializationException;
+import org.springframework.beans.factory.config.AbstractPropertyPlaceholderConfigurer;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
+import org.springframework.context.EnvironmentAware;
+import org.springframework.core.env.Environment;
+import org.springframework.core.env.MutablePropertySources;
+import org.springframework.core.env.PropertiesPropertySource;
+import org.springframework.core.env.PropertyResolver;
+import org.springframework.core.env.PropertySource;
+import org.springframework.core.env.PropertySources;
+import org.springframework.core.env.PropertySourcesPropertyResolver;
+import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver;
+
+/**
+ * Specialization of {@link AbstractPropertyPlaceholderConfigurer}
+ *
+ *
Local properties are added as a property source in any case. Precedence is based
+ * on the value of the {@link #setLocalOverride localOverride} property.
+ *
+ * @author Chris Beams
+ * @since 3.1
+ * @see AbstractPropertyPlaceholderConfigurer
+ * @see PropertyPlaceholderConfigurer
+ */
+public class PropertySourcesPlaceholderConfigurer extends AbstractPropertyPlaceholderConfigurer
+ implements EnvironmentAware {
+
+ /**
+ * {@value} is the name given to the {@link PropertySource} for the set of
+ * {@linkplain #mergeProperties() merged properties} supplied to this configurer.
+ */
+ public static final String LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME = "localProperties";
+
+ private MutablePropertySources propertySources;
+
+ private PropertyResolver propertyResolver;
+
+ private Environment environment;
+
+
+ /**
+ * {@inheritDoc}
+ *
{@code PropertySources} from this environment will be searched when replacing ${...} placeholders
+ * @see #setPropertySources
+ * @see #postProcessBeanFactory
+ */
+ public void setEnvironment(Environment environment) {
+ this.environment = environment;
+ }
+
+ /**
+ * Customize the set of {@link PropertySources} to be used by this configurer.
+ * Setting this property indicates that environment property sources and local
+ * properties should be ignored.
+ * @see #postProcessBeanFactory
+ */
+ public void setPropertySources(PropertySources propertySources) {
+ this.propertySources = new MutablePropertySources(propertySources);
+ }
+
+ @Override
+ protected PlaceholderResolver getPlaceholderResolver(Properties props) {
+ return new PlaceholderResolver() {
+ public String resolvePlaceholder(String placeholderName) {
+ return propertyResolver.getProperty(placeholderName);
+ }
+ };
+ }
+
+ /**
+ * {@inheritDoc}
+ *
Processing occurs by replacing ${...} placeholders in bean definitions by resolving each
+ * against this configurer's set of {@link PropertySources}, which includes:
+ *
+ * all {@linkplain Environment#getPropertySources environment property sources}, if an
+ * {@code Environment} {@linkplain #setEnvironment is present}
+ * {@linkplain #mergeProperties merged local properties}, if {@linkplain #setLocation any}
+ * {@linkplain #setLocations have} {@linkplain #setProperties been}
+ * {@linkplain #setPropertiesArray specified}
+ * any property sources set by calling {@link #setPropertySources}
+ *
+ * If {@link #setPropertySources} is called, environment and local properties will be
+ * ignored . This method is designed to give the user fine-grained control over property
+ * sources, and once set, the configurer makes no assumptions about adding additional sources.
+ */
+ @Override
+ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
+ if (this.propertySources == null) {
+ this.propertySources = new MutablePropertySources();
+ if (this.environment != null) {
+ this.propertySources.addAll(this.environment.getPropertySources());
+ }
+ try {
+ PropertySource> localPropertySource =
+ new PropertiesPropertySource(LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME, this.mergeProperties());
+ if (this.localOverride) {
+ this.propertySources.addFirst(localPropertySource);
+ } else {
+ this.propertySources.addLast(localPropertySource);
+ }
+ }
+ catch (IOException ex) {
+ throw new BeanInitializationException("Could not load properties", ex);
+ }
+ }
+
+ this.propertyResolver = new PropertySourcesPropertyResolver(this.propertySources);
+ this.processProperties(beanFactory, this.propertyResolver.asProperties());
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/jndi/support/SimpleJndiBeanFactory.java b/org.springframework.context/src/main/java/org/springframework/jndi/support/SimpleJndiBeanFactory.java
index 4d39882719a..ae62286051a 100644
--- a/org.springframework.context/src/main/java/org/springframework/jndi/support/SimpleJndiBeanFactory.java
+++ b/org.springframework.context/src/main/java/org/springframework/jndi/support/SimpleJndiBeanFactory.java
@@ -30,8 +30,6 @@ import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanNotOfRequiredTypeException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
-import org.springframework.core.env.ConfigurableEnvironment;
-import org.springframework.core.env.DefaultEnvironment;
import org.springframework.jndi.JndiLocatorSupport;
import org.springframework.jndi.TypeMismatchNamingException;
@@ -70,9 +68,6 @@ public class SimpleJndiBeanFactory extends JndiLocatorSupport implements BeanFac
/** Cache of the types of nonshareable resources: bean name --> bean type */
private final Map resourceTypes = new HashMap();
- /** TODO SPR-7508: should be JNDI-specific environment */
- private ConfigurableEnvironment environment = new DefaultEnvironment();
-
public SimpleJndiBeanFactory() {
setResourceRef(true);
diff --git a/org.springframework.context/src/main/resources/org/springframework/context/config/spring-context-3.1.xsd b/org.springframework.context/src/main/resources/org/springframework/context/config/spring-context-3.1.xsd
index f5366683b2a..ad93b101270 100644
--- a/org.springframework.context/src/main/resources/org/springframework/context/config/spring-context-3.1.xsd
+++ b/org.springframework.context/src/main/resources/org/springframework/context/config/spring-context-3.1.xsd
@@ -90,42 +90,17 @@
+ type="org.springframework.context.support.PropertySourcesPlaceholderConfigurer" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/org.springframework.context/src/test/java/example/profilescan/DevComponent.java b/org.springframework.context/src/test/java/example/profilescan/DevComponent.java
new file mode 100644
index 00000000000..5126e8dd6cf
--- /dev/null
+++ b/org.springframework.context/src/test/java/example/profilescan/DevComponent.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2002-2010 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 example.profilescan;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Component;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Profile(DevComponent.PROFILE_NAME)
+@Component
+public @interface DevComponent {
+
+ public static final String PROFILE_NAME = "dev";
+
+ String value() default "";
+
+}
\ No newline at end of file
diff --git a/org.springframework.context/src/test/java/example/profilescan/ProfileMetaAnnotatedComponent.java b/org.springframework.context/src/test/java/example/profilescan/ProfileMetaAnnotatedComponent.java
new file mode 100644
index 00000000000..68b9d2d639f
--- /dev/null
+++ b/org.springframework.context/src/test/java/example/profilescan/ProfileMetaAnnotatedComponent.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2002-2010 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 example.profilescan;
+
+
+@DevComponent(ProfileMetaAnnotatedComponent.BEAN_NAME)
+public class ProfileMetaAnnotatedComponent {
+
+ public static final String BEAN_NAME = "profileMetaAnnotatedComponent";
+
+}
\ No newline at end of file
diff --git a/org.springframework.context/src/test/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProviderTests.java b/org.springframework.context/src/test/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProviderTests.java
index bf27e63b672..0a45a5d02e0 100644
--- a/org.springframework.context/src/test/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProviderTests.java
+++ b/org.springframework.context/src/test/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProviderTests.java
@@ -39,7 +39,9 @@ import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
+import example.profilescan.DevComponent;
import example.profilescan.ProfileAnnotatedComponent;
+import example.profilescan.ProfileMetaAnnotatedComponent;
import example.scannable.FooDao;
import example.scannable.FooService;
import example.scannable.FooServiceImpl;
@@ -207,6 +209,15 @@ public class ClassPathScanningCandidateComponentProviderTests {
assertThat(ctx.containsBean(ProfileAnnotatedComponent.BEAN_NAME), is(true));
}
+ @Test
+ public void testIntegrationWithAnnotationConfigApplicationContext_validMetaAnnotatedProfile() {
+ AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
+ ctx.getEnvironment().setActiveProfiles(DevComponent.PROFILE_NAME);
+ ctx.register(ProfileMetaAnnotatedComponent.class);
+ ctx.refresh();
+ assertThat(ctx.containsBean(ProfileMetaAnnotatedComponent.BEAN_NAME), is(true));
+ }
+
@Test
public void testIntegrationWithAnnotationConfigApplicationContext_invalidProfile() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
@@ -216,6 +227,15 @@ public class ClassPathScanningCandidateComponentProviderTests {
assertThat(ctx.containsBean(ProfileAnnotatedComponent.BEAN_NAME), is(false));
}
+ @Test
+ public void testIntegrationWithAnnotationConfigApplicationContext_invalidMetaAnnotatedProfile() {
+ AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
+ ctx.getEnvironment().setActiveProfiles("other");
+ ctx.register(ProfileMetaAnnotatedComponent.class);
+ ctx.refresh();
+ assertThat(ctx.containsBean(ProfileMetaAnnotatedComponent.BEAN_NAME), is(false));
+ }
+
@Test
public void testIntegrationWithAnnotationConfigApplicationContext_defaultProfile() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
diff --git a/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationClassAspectIntegrationTests.java b/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationClassAspectIntegrationTests.java
index 68379d78d38..2b3924bb4f4 100644
--- a/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationClassAspectIntegrationTests.java
+++ b/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationClassAspectIntegrationTests.java
@@ -40,7 +40,7 @@ import test.beans.TestBean;
* particularly convenient syntax requiring no extra artifact for the aspect.
*
* Currently it is assumed that the user is bootstrapping Configuration class processing via XML (using
- * annotation-config or component-scan), and thus will also use {@literal } to enable
+ * annotation-config or component-scan), and thus will also use {@code } to enable
* processing of the Aspect annotation.
*
* @author Chris Beams
diff --git a/org.springframework.context/src/test/java/org/springframework/context/config/ContextNamespaceHandlerTests.java b/org.springframework.context/src/test/java/org/springframework/context/config/ContextNamespaceHandlerTests.java
index c760e5d7eca..21c15807cf2 100644
--- a/org.springframework.context/src/test/java/org/springframework/context/config/ContextNamespaceHandlerTests.java
+++ b/org.springframework.context/src/test/java/org/springframework/context/config/ContextNamespaceHandlerTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2010 the original author or authors.
+ * 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.
@@ -72,7 +72,7 @@ public class ContextNamespaceHandlerTests {
@Test
public void propertyPlaceholderEnvironmentProperties() throws Exception {
- MockEnvironment env = MockEnvironment.withProperty("foo", "spam");
+ MockEnvironment env = new MockEnvironment().withProperty("foo", "spam");
GenericXmlApplicationContext applicationContext = new GenericXmlApplicationContext();
applicationContext.setEnvironment(env);
applicationContext.load(new ClassPathResource("contextNamespaceHandlerTests-simple.xml", getClass()));
diff --git a/org.springframework.context/src/test/java/org/springframework/context/expression/EnvironmentAccessorIntegrationTests.java b/org.springframework.context/src/test/java/org/springframework/context/expression/EnvironmentAccessorIntegrationTests.java
index 755b50acefc..4dda9faf6c6 100644
--- a/org.springframework.context/src/test/java/org/springframework/context/expression/EnvironmentAccessorIntegrationTests.java
+++ b/org.springframework.context/src/test/java/org/springframework/context/expression/EnvironmentAccessorIntegrationTests.java
@@ -20,12 +20,11 @@ import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.genericBeanDefinition;
-import java.util.HashMap;
-
import org.junit.Test;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
+import org.springframework.mock.env.MockPropertySource;
import test.beans.TestBean;
@@ -49,7 +48,7 @@ public class EnvironmentAccessorIntegrationTests {
.getBeanDefinition());
GenericApplicationContext ctx = new GenericApplicationContext(bf);
- ctx.getEnvironment().addPropertySource("testMap", new HashMap() {{ put("my.name", "myBean"); }});
+ ctx.getEnvironment().getPropertySources().addFirst(new MockPropertySource().withProperty("my.name", "myBean"));
ctx.refresh();
assertThat(ctx.getBean(TestBean.class).getName(), equalTo("myBean"));
diff --git a/org.springframework.context/src/test/java/org/springframework/context/support/EnvironmentAwarePropertyPlaceholderConfigurerTests.java b/org.springframework.context/src/test/java/org/springframework/context/support/EnvironmentAwarePropertyPlaceholderConfigurerTests.java
deleted file mode 100644
index 852330cefba..00000000000
--- a/org.springframework.context/src/test/java/org/springframework/context/support/EnvironmentAwarePropertyPlaceholderConfigurerTests.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright 2002-2010 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.context.support;
-
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.junit.Assert.assertThat;
-import static org.springframework.beans.factory.support.BeanDefinitionBuilder.genericBeanDefinition;
-
-import org.junit.Test;
-import org.springframework.beans.factory.support.DefaultListableBeanFactory;
-import org.springframework.mock.env.MockEnvironment;
-
-import test.beans.TestBean;
-
-/**
- * Unit tests for {@link EnvironmentAwarePropertyPlaceholderConfigurer}.
- *
- * @author Chris Beams
- * @since 3.1
- * @see EnvironmentAwarePropertyPlaceholderConfigurerTests
- */
-public class EnvironmentAwarePropertyPlaceholderConfigurerTests {
-
- @Test(expected=IllegalArgumentException.class)
- public void environmentNotNull() {
- new EnvironmentAwarePropertyPlaceholderConfigurer().postProcessBeanFactory(new DefaultListableBeanFactory());
- }
-
- @Test
- public void localPropertiesOverrideFalse() {
- localPropertiesOverride(false);
- }
-
- @Test
- public void localPropertiesOverrideTrue() {
- localPropertiesOverride(true);
- }
-
- private void localPropertiesOverride(boolean override) {
- DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
- bf.registerBeanDefinition("testBean",
- genericBeanDefinition(TestBean.class)
- .addPropertyValue("name", "${foo}")
- .getBeanDefinition());
-
- EnvironmentAwarePropertyPlaceholderConfigurer ppc = new EnvironmentAwarePropertyPlaceholderConfigurer();
-
- ppc.setLocalOverride(override);
- ppc.setProperties(MockEnvironment.withProperty("foo", "local").asProperties());
- ppc.setEnvironment(MockEnvironment.withProperty("foo", "enclosing"));
- ppc.postProcessBeanFactory(bf);
- if (override) {
- assertThat(bf.getBean(TestBean.class).getName(), equalTo("local"));
- } else {
- assertThat(bf.getBean(TestBean.class).getName(), equalTo("enclosing"));
- }
- }
-
- @Test
- public void simpleReplacement() {
- DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
- bf.registerBeanDefinition("testBean",
- genericBeanDefinition(TestBean.class)
- .addPropertyValue("name", "${my.name}")
- .getBeanDefinition());
-
- MockEnvironment env = new MockEnvironment();
- env.setProperty("my.name", "myValue");
-
- EnvironmentAwarePropertyPlaceholderConfigurer ppc =
- new EnvironmentAwarePropertyPlaceholderConfigurer();
- ppc.setEnvironment(env);
- ppc.postProcessBeanFactory(bf);
- assertThat(bf.getBean(TestBean.class).getName(), equalTo("myValue"));
- }
-
-}
diff --git a/org.springframework.context/src/test/java/org/springframework/context/support/GenericXmlApplicationContextTests.java b/org.springframework.context/src/test/java/org/springframework/context/support/GenericXmlApplicationContextTests.java
index 1ead59fe472..c6be2f1474a 100644
--- a/org.springframework.context/src/test/java/org/springframework/context/support/GenericXmlApplicationContextTests.java
+++ b/org.springframework.context/src/test/java/org/springframework/context/support/GenericXmlApplicationContextTests.java
@@ -9,7 +9,7 @@ import org.springframework.util.ClassUtils;
/**
* Unit tests for {@link GenericXmlApplicationContext}.
- *
+ *
* See SPR-7530.
*
* @author Chris Beams
diff --git a/org.springframework.context/src/test/java/org/springframework/context/support/PropertySourcesPlaceholderConfigurerTests.java b/org.springframework.context/src/test/java/org/springframework/context/support/PropertySourcesPlaceholderConfigurerTests.java
new file mode 100644
index 00000000000..bae62fd624a
--- /dev/null
+++ b/org.springframework.context/src/test/java/org/springframework/context/support/PropertySourcesPlaceholderConfigurerTests.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2002-2010 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.context.support;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+import static org.springframework.beans.factory.support.BeanDefinitionBuilder.genericBeanDefinition;
+
+import java.util.Properties;
+
+import org.junit.Test;
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.core.env.MutablePropertySources;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
+import org.springframework.mock.env.MockEnvironment;
+import org.springframework.mock.env.MockPropertySource;
+
+import test.beans.TestBean;
+
+/**
+ * Unit tests for {@link PropertySourcesPlaceholderConfigurer}.
+ *
+ * @author Chris Beams
+ * @since 3.1
+ */
+public class PropertySourcesPlaceholderConfigurerTests {
+
+ @Test
+ public void replacementFromEnvironmentProperties() {
+ DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
+ bf.registerBeanDefinition("testBean",
+ genericBeanDefinition(TestBean.class)
+ .addPropertyValue("name", "${my.name}")
+ .getBeanDefinition());
+
+ MockEnvironment env = new MockEnvironment();
+ env.setProperty("my.name", "myValue");
+
+ PropertySourcesPlaceholderConfigurer ppc =
+ new PropertySourcesPlaceholderConfigurer();
+ ppc.setEnvironment(env);
+ ppc.postProcessBeanFactory(bf);
+ assertThat(bf.getBean(TestBean.class).getName(), equalTo("myValue"));
+ }
+
+ @Test
+ public void localPropertiesViaResource() {
+ DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
+ bf.registerBeanDefinition("testBean",
+ genericBeanDefinition(TestBean.class)
+ .addPropertyValue("name", "${my.name}")
+ .getBeanDefinition());
+
+ PropertySourcesPlaceholderConfigurer pc = new PropertySourcesPlaceholderConfigurer();
+ Resource resource = new ClassPathResource("PropertySourcesPlaceholderConfigurerTests.properties", this.getClass());
+ pc.setLocation(resource);
+ pc.postProcessBeanFactory(bf);
+ assertThat(bf.getBean(TestBean.class).getName(), equalTo("foo"));
+ }
+
+ @Test
+ public void localPropertiesOverrideFalse() {
+ localPropertiesOverride(false);
+ }
+
+ @Test
+ public void localPropertiesOverrideTrue() {
+ localPropertiesOverride(true);
+ }
+
+ @Test
+ public void explicitPropertySources() {
+ DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
+ bf.registerBeanDefinition("testBean",
+ genericBeanDefinition(TestBean.class)
+ .addPropertyValue("name", "${my.name}")
+ .getBeanDefinition());
+
+ MutablePropertySources propertySources = new MutablePropertySources();
+ propertySources.addLast(new MockPropertySource().withProperty("my.name", "foo"));
+
+ PropertySourcesPlaceholderConfigurer pc = new PropertySourcesPlaceholderConfigurer();
+ pc.setPropertySources(propertySources);
+ pc.postProcessBeanFactory(bf);
+ assertThat(bf.getBean(TestBean.class).getName(), equalTo("foo"));
+ }
+
+ @Test
+ public void explicitPropertySourcesExcludesEnvironment() {
+ DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
+ bf.registerBeanDefinition("testBean",
+ genericBeanDefinition(TestBean.class)
+ .addPropertyValue("name", "${my.name}")
+ .getBeanDefinition());
+
+ MutablePropertySources propertySources = new MutablePropertySources();
+ propertySources.addLast(new MockPropertySource());
+
+ PropertySourcesPlaceholderConfigurer pc = new PropertySourcesPlaceholderConfigurer();
+ pc.setPropertySources(propertySources);
+ pc.setEnvironment(new MockEnvironment().withProperty("my.name", "env"));
+ pc.setIgnoreUnresolvablePlaceholders(true);
+ pc.postProcessBeanFactory(bf);
+ assertThat(bf.getBean(TestBean.class).getName(), equalTo("${my.name}"));
+ }
+
+ @Test
+ @SuppressWarnings("serial")
+ public void explicitPropertySourcesExcludesLocalProperties() {
+ DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
+ bf.registerBeanDefinition("testBean",
+ genericBeanDefinition(TestBean.class)
+ .addPropertyValue("name", "${my.name}")
+ .getBeanDefinition());
+
+ MutablePropertySources propertySources = new MutablePropertySources();
+ propertySources.addLast(new MockPropertySource());
+
+ PropertySourcesPlaceholderConfigurer pc = new PropertySourcesPlaceholderConfigurer();
+ pc.setPropertySources(propertySources);
+ pc.setProperties(new Properties() {{ put("my.name", "local"); }});
+ pc.setIgnoreUnresolvablePlaceholders(true);
+ pc.postProcessBeanFactory(bf);
+ assertThat(bf.getBean(TestBean.class).getName(), equalTo("${my.name}"));
+ }
+
+ @SuppressWarnings("serial")
+ private void localPropertiesOverride(boolean override) {
+ DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
+ bf.registerBeanDefinition("testBean",
+ genericBeanDefinition(TestBean.class)
+ .addPropertyValue("name", "${foo}")
+ .getBeanDefinition());
+
+ PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer();
+
+ ppc.setLocalOverride(override);
+ ppc.setProperties(new Properties() {{ setProperty("foo", "local"); }});
+ ppc.setEnvironment(new MockEnvironment().withProperty("foo", "enclosing"));
+ ppc.postProcessBeanFactory(bf);
+ if (override) {
+ assertThat(bf.getBean(TestBean.class).getName(), equalTo("local"));
+ } else {
+ assertThat(bf.getBean(TestBean.class).getName(), equalTo("enclosing"));
+ }
+ }
+
+}
diff --git a/org.springframework.context/src/test/java/org/springframework/context/support/PropertySourcesPlaceholderConfigurerTests.properties b/org.springframework.context/src/test/java/org/springframework/context/support/PropertySourcesPlaceholderConfigurerTests.properties
new file mode 100644
index 00000000000..b8f69781481
--- /dev/null
+++ b/org.springframework.context/src/test/java/org/springframework/context/support/PropertySourcesPlaceholderConfigurerTests.properties
@@ -0,0 +1 @@
+my.name=foo
\ No newline at end of file
diff --git a/org.springframework.context/src/test/java/org/springframework/mock/env/MockEnvironment.java b/org.springframework.context/src/test/java/org/springframework/mock/env/MockEnvironment.java
index 2a6ec523686..e8f55721c26 100644
--- a/org.springframework.context/src/test/java/org/springframework/mock/env/MockEnvironment.java
+++ b/org.springframework.context/src/test/java/org/springframework/mock/env/MockEnvironment.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2010 the original author or authors.
+ * 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.
@@ -19,30 +19,41 @@ package org.springframework.mock.env;
import org.springframework.core.env.AbstractEnvironment;
import org.springframework.core.env.ConfigurableEnvironment;
-
/**
* Simple {@link ConfigurableEnvironment} implementation exposing a
* {@link #setProperty(String, String)} and {@link #withProperty(String, String)}
* methods for testing purposes.
*
* @author Chris Beams
+ * @since 3.1
* @see MockPropertySource
*/
public class MockEnvironment extends AbstractEnvironment {
private MockPropertySource propertySource = new MockPropertySource();
+ /**
+ * Create a new {@code MockEnvironment} with a single {@link MockPropertySource}.
+ */
public MockEnvironment() {
- getPropertySources().add(propertySource);
+ getPropertySources().addLast(propertySource);
}
+ /**
+ * Set a property on the underlying {@link MockPropertySource} for this environment.
+ */
public void setProperty(String key, String value) {
propertySource.setProperty(key, value);
}
- public static MockEnvironment withProperty(String key, String value) {
- MockEnvironment environment = new MockEnvironment();
- environment.setProperty(key, value);
- return environment;
+ /**
+ * Convenient synonym for {@link #setProperty} that returns the current instance.
+ * Useful for method chaining and fluent-style use.
+ * @return this {@link MockEnvironment} instance
+ * @see MockPropertySource#withProperty(String, String)
+ */
+ public MockEnvironment withProperty(String key, String value) {
+ this.setProperty(key, value);
+ return this;
}
}
diff --git a/org.springframework.context/src/test/java/org/springframework/mock/env/MockPropertySource.java b/org.springframework.context/src/test/java/org/springframework/mock/env/MockPropertySource.java
index f1e7285c8ec..14f239e848e 100644
--- a/org.springframework.context/src/test/java/org/springframework/mock/env/MockPropertySource.java
+++ b/org.springframework.context/src/test/java/org/springframework/mock/env/MockPropertySource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2010 the original author or authors.
+ * 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.
@@ -19,24 +19,85 @@ package org.springframework.mock.env;
import java.util.Properties;
import org.springframework.core.env.PropertiesPropertySource;
+import org.springframework.core.env.PropertySource;
+/**
+ * Simple {@link PropertySource} implementation for use in testing. Accepts
+ * a user-provided {@link Properties} object, or if omitted during construction,
+ * the implementation will initialize its own.
+ *
+ * The {@link #setProperty} and {@link #withProperty} methods are exposed for
+ * convenience, for example:
+ *
+ * {@code
+ * PropertySource> source = new MockPropertySource().withProperty("foo", "bar");
+ * }
+ *
+ *
+ * @author Chris Beams
+ * @since 3.1
+ * @see MockEnvironment
+ */
public class MockPropertySource extends PropertiesPropertySource {
+ /**
+ * {@value} is the default name for {@link MockPropertySource} instances not
+ * otherwise given an explicit name.
+ * @see #MockPropertySource()
+ * @see #MockPropertySource(String)
+ */
+ public static final String MOCK_PROPERTIES_PROPERTY_SOURCE_NAME = "mockProperties";
+
+ /**
+ * Create a new {@code MockPropertySource} named {@value #MOCK_PROPERTIES_PROPERTY_SOURCE_NAME}
+ * that will maintain its own internal {@link Properties} instance.
+ */
public MockPropertySource() {
this(new Properties());
}
- private MockPropertySource(Properties properties) {
- super("mockProperties", properties);
+ /**
+ * Create a new {@code MockPropertySource} with the given name that will
+ * maintain its own internal {@link Properties} instance.
+ * @param name the {@linkplain #getName() name} of the property source
+ */
+ public MockPropertySource(String name) {
+ this(name, new Properties());
}
+ /**
+ * Create a new {@code MockPropertySource} named {@value #MOCK_PROPERTIES_PROPERTY_SOURCE_NAME}
+ * and backed by the given {@link Properties} object.
+ * @param properties the properties to use
+ */
+ public MockPropertySource(Properties properties) {
+ this(MOCK_PROPERTIES_PROPERTY_SOURCE_NAME, properties);
+ }
+
+ /**
+ * Create a new {@code MockPropertySource} with with the given name and backed by the given
+ * {@link Properties} object
+ * @param name the {@linkplain #getName() name} of the property source
+ * @param properties the properties to use
+ */
+ public MockPropertySource(String name, Properties properties) {
+ super(name, properties);
+ }
+
+ /**
+ * Set the given property on the underlying {@link Properties} object.
+ */
public void setProperty(String key, String value) {
- this.source.setProperty(key, value);
+ this.source.put(key, value);
}
- public static MockPropertySource withProperty(String key, String value) {
- Properties properties = new Properties();
- properties.setProperty(key, value);
- return new MockPropertySource(properties);
+ /**
+ * Convenient synonym for {@link #setProperty} that returns the current instance.
+ * Useful for method chaining and fluent-style use.
+ * @return this {@link MockPropertySource} instance
+ */
+ public MockPropertySource withProperty(String key, String value) {
+ this.setProperty(key, value);
+ return this;
}
}
diff --git a/org.springframework.context/src/test/resources/org/springframework/context/config/contextNamespaceHandlerTests-system.xml b/org.springframework.context/src/test/resources/org/springframework/context/config/contextNamespaceHandlerTests-system.xml
index d5268c52bcc..9c475282fc0 100644
--- a/org.springframework.context/src/test/resources/org/springframework/context/config/contextNamespaceHandlerTests-system.xml
+++ b/org.springframework.context/src/test/resources/org/springframework/context/config/contextNamespaceHandlerTests-system.xml
@@ -3,7 +3,7 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
- http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
+ http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd">
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 02f8c43a289..b840ffebef7 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
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2010 the original author or authors.
+ * 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.
@@ -16,39 +16,40 @@
package org.springframework.core.env;
+import static java.lang.String.format;
+import static org.springframework.util.StringUtils.commaDelimitedListToSet;
+import static org.springframework.util.StringUtils.trimAllWhitespace;
+
import java.security.AccessControlException;
import java.util.Arrays;
-import java.util.Collections;
import java.util.LinkedHashSet;
-import java.util.LinkedList;
import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Properties;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-
-import org.springframework.core.convert.ConversionService;
-import org.springframework.core.convert.support.ConversionServiceFactory;
-import org.springframework.util.PropertyPlaceholderHelper;
-import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver;
+import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
-import static java.lang.String.*;
-import static org.springframework.util.StringUtils.*;
-import static org.springframework.util.SystemPropertyUtils.*;
-
-
/**
- * TODO SPR-7508: document
+ * Abstract base class for {@link Environment} implementations.
*
* @author Chris Beams
* @since 3.1
+ * @see DefaultEnvironment
*/
public abstract class AbstractEnvironment implements ConfigurableEnvironment {
+ /**
+ * Name of property to set to specify active profiles: {@value}. May be comma delimited.
+ * @see ConfigurableEnvironment#setActiveProfiles
+ */
public static final String ACTIVE_PROFILES_PROPERTY_NAME = "spring.profile.active";
+
+ /**
+ * Name of property to set to specify default profiles: {@value}. May be comma delimited.
+ * @see ConfigurableEnvironment#setDefaultProfiles
+ */
public static final String DEFAULT_PROFILES_PROPERTY_NAME = "spring.profile.default";
protected final Log logger = LogFactory.getLog(getClass());
@@ -56,138 +57,22 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment {
private Set activeProfiles = new LinkedHashSet();
private Set defaultProfiles = new LinkedHashSet();
- private LinkedList> propertySources = new LinkedList>();
- private ConversionService conversionService = ConversionServiceFactory.createDefaultConversionService();
-
- private final PropertyPlaceholderHelper nonStrictHelper =
- new PropertyPlaceholderHelper(PLACEHOLDER_PREFIX, PLACEHOLDER_SUFFIX, VALUE_SEPARATOR, true);
-
- private final PropertyPlaceholderHelper strictHelper =
- new PropertyPlaceholderHelper(PLACEHOLDER_PREFIX, PLACEHOLDER_SUFFIX, VALUE_SEPARATOR, false);
+ private MutablePropertySources propertySources = new MutablePropertySources();
+ private ConfigurablePropertyResolver propertyResolver = new PropertySourcesPropertyResolver(propertySources);
- public ConversionService getConversionService() {
- return this.conversionService;
+ public String[] getActiveProfiles() {
+ return this.doGetActiveProfiles().toArray(new String[]{});
}
- public void setConversionService(ConversionService conversionService) {
- this.conversionService = conversionService;
- }
-
- public void addPropertySource(PropertySource> propertySource) {
- propertySources.addFirst(propertySource);
- }
-
- public void addPropertySource(String name, Properties properties) {
- addPropertySource(new PropertiesPropertySource(name, properties));
- }
-
- public void addPropertySource(String name, Map propertiesMap) {
- addPropertySource(new MapPropertySource(name, propertiesMap));
- }
-
- public LinkedList> getPropertySources() {
- return propertySources;
- }
-
- public boolean containsProperty(String key) {
- for (PropertySource> propertySource : propertySources) {
- if (propertySource.containsProperty(key)) {
- return true;
- }
- }
- return false;
- }
-
- public String getProperty(String key) {
- if (logger.isTraceEnabled()) {
- logger.trace(format("getProperty(\"%s\") (implicit targetType [String])", key));
- }
- return getProperty(key, String.class);
- }
-
- public String getRequiredProperty(String key) {
- String value = getProperty(key);
- if (value == null) {
- throw new IllegalArgumentException(format("required key [%s] not found", key));
- }
- return value;
- }
-
- public T getProperty(String key, Class targetValueType) {
- boolean debugEnabled = logger.isDebugEnabled();
- if (logger.isTraceEnabled()) {
- logger.trace(format("getProperty(\"%s\", %s)", key, targetValueType.getSimpleName()));
- }
-
- for (PropertySource> propertySource : propertySources) {
- if (debugEnabled) {
- logger.debug(format("Searching for key '%s' in [%s]", key, propertySource.getName()));
- }
- if (propertySource.containsProperty(key)) {
- Object value = propertySource.getProperty(key);
- Class> valueType = value == null ? null : value.getClass();
- if (debugEnabled) {
- logger.debug(
- format("Found key '%s' in [%s] with type [%s] and value '%s'",
- key, propertySource.getName(),
- valueType == null ? "" : valueType.getSimpleName(), value));
- }
- if (value == null) {
- return null;
- }
- if (!conversionService.canConvert(valueType, targetValueType)) {
- throw new IllegalArgumentException(
- format("Cannot convert value [%s] from source type [%s] to target type [%s]",
- value, valueType.getSimpleName(), targetValueType.getSimpleName()));
- }
- return conversionService.convert(value, targetValueType);
- }
- }
-
- if (debugEnabled) {
- logger.debug(format("Could not find key '%s' in any property source. Returning [null]", key));
- }
- return null;
- }
-
- public T getRequiredProperty(String key, Class valueType) {
- T value = getProperty(key, valueType);
- if (value == null) {
- throw new IllegalArgumentException(format("required key [%s] not found", key));
- }
- return value;
- }
-
- public int getPropertyCount() {
- return asProperties().size();
- }
-
- public Properties asProperties() {
- // TODO SPR-7508: refactor, simplify. only handles map-based propertysources right now.
- Properties mergedProps = new Properties();
- for (int i = propertySources.size() -1; i >= 0; i--) {
- PropertySource> propertySource = propertySources.get(i);
- Object object = propertySource.getSource();
- if (object instanceof Map) {
- for (Entry, ?> entry : ((Map, ?>)object).entrySet()) {
- mergedProps.put(entry.getKey(), entry.getValue());
- }
- } else {
- throw new IllegalArgumentException("unknown PropertySource source type: " + object.getClass().getName());
- }
- }
- return mergedProps;
- }
-
- public Set getActiveProfiles() {
+ protected Set doGetActiveProfiles() {
if (this.activeProfiles.isEmpty()) {
- String profiles = getProperty(ACTIVE_PROFILES_PROPERTY_NAME);
+ String profiles = this.propertyResolver.getProperty(ACTIVE_PROFILES_PROPERTY_NAME);
if (StringUtils.hasText(profiles)) {
this.activeProfiles = commaDelimitedListToSet(trimAllWhitespace(profiles));
}
}
- return Collections.unmodifiableSet(activeProfiles);
+ return this.activeProfiles;
}
public void setActiveProfiles(String... profiles) {
@@ -195,14 +80,18 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment {
this.activeProfiles.addAll(Arrays.asList(profiles));
}
- public Set getDefaultProfiles() {
+ public String[] getDefaultProfiles() {
+ return this.doGetDefaultProfiles().toArray(new String[]{});
+ }
+
+ protected Set doGetDefaultProfiles() {
if (this.defaultProfiles.isEmpty()) {
- String profiles = getProperty(DEFAULT_PROFILES_PROPERTY_NAME);
+ String profiles = this.propertyResolver.getProperty(DEFAULT_PROFILES_PROPERTY_NAME);
if (StringUtils.hasText(profiles)) {
- this.defaultProfiles = commaDelimitedListToSet(profiles);
+ this.defaultProfiles = commaDelimitedListToSet(trimAllWhitespace(profiles));
}
}
- return Collections.unmodifiableSet(this.defaultProfiles);
+ return this.defaultProfiles;
}
public void setDefaultProfiles(String... profiles) {
@@ -210,6 +99,30 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment {
this.defaultProfiles.addAll(Arrays.asList(profiles));
}
+ public boolean acceptsProfiles(String... profiles) {
+ Assert.notEmpty(profiles, "Must specify at least one profile");
+ boolean activeProfileFound = false;
+ Set activeProfiles = this.doGetActiveProfiles();
+ Set defaultProfiles = this.doGetDefaultProfiles();
+ for (String profile : profiles) {
+ Assert.hasText(profile, "profile must not be empty");
+ if (activeProfiles.contains(profile)
+ || (activeProfiles.isEmpty() && defaultProfiles.contains(profile))) {
+ activeProfileFound = true;
+ break;
+ }
+ }
+ return activeProfileFound;
+ }
+
+ public MutablePropertySources getPropertySources() {
+ return this.propertySources;
+ }
+
+ public ConfigurablePropertyResolver getPropertyResolver() {
+ return this.propertyResolver;
+ }
+
public Map getSystemEnvironment() {
Map systemEnvironment;
try {
@@ -235,17 +148,6 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment {
return systemEnvironment;
}
- /**
- * TODO SPR-7508: document
- *
- * Returns a string, string map even though the underlying system properties
- * are a properties object that can technically contain non-string keys and values.
- * Thus, the unchecked conversions and raw map type being used. In practice, it will
- * always be 'safe' to interact with the properties map as if it contains only strings,
- * because Properties copes with this in its getProperty method. We never access the
- * properties object via its Hashtable.get() method, so any non-string keys/values
- * get effectively ignored.
- */
@SuppressWarnings({"unchecked", "rawtypes"})
public Map getSystemProperties() {
Map systemProperties;
@@ -272,40 +174,10 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment {
return systemProperties;
}
- public String resolvePlaceholders(String text) {
- return doResolvePlaceholders(text, nonStrictHelper);
- }
-
- public String resolveRequiredPlaceholders(String text) {
- return doResolvePlaceholders(text, strictHelper);
- }
-
- public boolean acceptsProfiles(String[] specifiedProfiles) {
- boolean activeProfileFound = false;
- Set activeProfiles = this.getActiveProfiles();
- Set defaultProfiles = this.getDefaultProfiles();
- for (String profile : specifiedProfiles) {
- if (activeProfiles.contains(profile)
- || (activeProfiles.isEmpty() && defaultProfiles.contains(profile))) {
- activeProfileFound = true;
- break;
- }
- }
- return activeProfileFound;
- }
-
- private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
- return helper.replacePlaceholders(text, new PlaceholderResolver() {
- public String resolvePlaceholder(String placeholderName) {
- return AbstractEnvironment.this.getProperty(placeholderName);
- }
- });
- }
-
@Override
public String toString() {
- return String.format("%s [activeProfiles=%s, defaultProfiles=%s, propertySources=%s]",
- getClass().getSimpleName(), activeProfiles, defaultProfiles, propertySources);
+ return format("%s [activeProfiles=%s, defaultProfiles=%s, propertySources=%s]",
+ getClass().getSimpleName(), this.activeProfiles, this.defaultProfiles, this.propertySources);
}
}
diff --git a/org.springframework.core/src/main/java/org/springframework/core/env/AbstractPropertyResolver.java b/org.springframework.core/src/main/java/org/springframework/core/env/AbstractPropertyResolver.java
new file mode 100644
index 00000000000..89f22eb7741
--- /dev/null
+++ b/org.springframework.core/src/main/java/org/springframework/core/env/AbstractPropertyResolver.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2002-2010 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.core.env;
+
+import static java.lang.String.format;
+
+import static org.springframework.util.SystemPropertyUtils.PLACEHOLDER_PREFIX;
+import static org.springframework.util.SystemPropertyUtils.PLACEHOLDER_SUFFIX;
+import static org.springframework.util.SystemPropertyUtils.VALUE_SEPARATOR;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.core.convert.ConversionService;
+import org.springframework.core.convert.support.ConversionServiceFactory;
+import org.springframework.util.PropertyPlaceholderHelper;
+import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver;
+
+
+/**
+ * Abstract base class for resolving properties against any underlying source.
+ *
+ * @author Chris Beams
+ * @since 3.1
+ */
+public abstract class AbstractPropertyResolver implements ConfigurablePropertyResolver {
+
+ protected final Log logger = LogFactory.getLog(getClass());
+
+ protected ConversionService conversionService = ConversionServiceFactory.createDefaultConversionService();
+
+ private final PropertyPlaceholderHelper nonStrictHelper =
+ new PropertyPlaceholderHelper(PLACEHOLDER_PREFIX, PLACEHOLDER_SUFFIX, VALUE_SEPARATOR, true);
+
+ private final PropertyPlaceholderHelper strictHelper =
+ new PropertyPlaceholderHelper(PLACEHOLDER_PREFIX, PLACEHOLDER_SUFFIX, VALUE_SEPARATOR, false);
+
+
+ public ConversionService getConversionService() {
+ return this.conversionService;
+ }
+
+ public void setConversionService(ConversionService conversionService) {
+ this.conversionService = conversionService;
+ }
+
+ public String getRequiredProperty(String key) throws IllegalStateException {
+ String value = getProperty(key);
+ if (value == null) {
+ throw new IllegalStateException(format("required key [%s] not found", key));
+ }
+ return value;
+ }
+
+ public T getRequiredProperty(String key, Class valueType) throws IllegalStateException {
+ T value = getProperty(key, valueType);
+ if (value == null) {
+ throw new IllegalStateException(format("required key [%s] not found", key));
+ }
+ return value;
+ }
+
+ public int getPropertyCount() {
+ return asProperties().size();
+ }
+
+ public String resolvePlaceholders(String text) {
+ return doResolvePlaceholders(text, this.nonStrictHelper);
+ }
+
+ public String resolveRequiredPlaceholders(String text) {
+ return doResolvePlaceholders(text, this.strictHelper);
+ }
+
+ private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
+ return helper.replacePlaceholders(text, new PlaceholderResolver() {
+ public String resolvePlaceholder(String placeholderName) {
+ return getProperty(placeholderName);
+ }
+ });
+ }
+
+}
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 d24e402a244..be1de7174a4 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
@@ -16,46 +16,45 @@
package org.springframework.core.env;
-import java.util.LinkedList;
-import java.util.Map;
-import java.util.Properties;
-
-import org.springframework.core.convert.ConversionService;
-
/**
- * TODO SPR-7508: document
+ * Configuration interface to be implemented by most if not all {@link Environment
+ * Environments}. Provides facilities for setting active and default profiles as well
+ * as specializing the return types for {@link #getPropertySources()} and
+ * {@link #getPropertyResolver()} such that they return types that may be manipulated.
*
* @author Chris Beams
* @since 3.1
+ * @see DefaultEnvironment
+ * @see org.springframework.context.ConfigurableApplicationContext#getEnvironment
*/
public interface ConfigurableEnvironment extends Environment {
+ /**
+ * Specify the set of profiles active for this Environment. Profiles are
+ * evaluated during container bootstrap to determine whether bean definitions
+ * should be registered with the container.
+ *
+ * @see #setDefaultProfiles
+ * @see org.springframework.context.annotation.Profile
+ * @see AbstractEnvironment#ACTIVE_PROFILES_PROPERTY_NAME
+ */
void setActiveProfiles(String... profiles);
+ /**
+ * Specify the set of profiles to be made active by default if no other profiles
+ * are explicitly made active through {@link #setActiveProfiles}.
+ * @see AbstractEnvironment#DEFAULT_PROFILES_PROPERTY_NAME
+ */
void setDefaultProfiles(String... profiles);
- public ConversionService getConversionService();
-
- public void setConversionService(ConversionService conversionService);
-
- void addPropertySource(PropertySource> propertySource);
-
- void addPropertySource(String name, Properties properties);
-
- void addPropertySource(String name, Map propertiesMap);
+ /**
+ * Return the {@link PropertySources} for this environment in mutable form
+ */
+ MutablePropertySources getPropertySources();
/**
- * TODO: SPR-7508 document
- *
- * Care should be taken to ensure duplicates are not introduced.
- *
- * Recommend using {@link LinkedList#set(int, Object)} for replacing items,
- * and combining {@link LinkedList#remove()} with other methods like
- * {@link LinkedList#add(Object)} to prevent duplicates.
- *
- * Explain how {@link PropertySource#equals(Object)} and hashCode work, and that
- * recommend using {@link PropertySource#named(String)} for lookups in the list.
+ * Return the {@link PropertyResolver} for this environment in configurable form
*/
- LinkedList> getPropertySources();
+ ConfigurablePropertyResolver getPropertyResolver();
}
diff --git a/org.springframework.core/src/main/java/org/springframework/core/env/ConfigurablePropertyResolver.java b/org.springframework.core/src/main/java/org/springframework/core/env/ConfigurablePropertyResolver.java
new file mode 100644
index 00000000000..1623107da43
--- /dev/null
+++ b/org.springframework.core/src/main/java/org/springframework/core/env/ConfigurablePropertyResolver.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2002-2010 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.core.env;
+
+import org.springframework.core.convert.ConversionService;
+
+
+/**
+ * Configuration interface to be implemented by most if not all {@link PropertyResolver
+ * PropertyResolvers}. Provides facilities for accessing and customizing the
+ * {@link ConversionService} used when converting property values from one type to
+ * another.
+ *
+ * @author Chris Beams
+ * @since 3.1
+ */
+public interface ConfigurablePropertyResolver extends PropertyResolver {
+
+ /**
+ * @return the {@link ConversionService} used when performing type
+ * conversions on properties.
+ * @see PropertyResolver#getProperty(String, Class)
+ */
+ ConversionService getConversionService();
+
+ /**
+ * Set the {@link ConversionService} to be used when performing type
+ * conversions on properties.
+ * @see PropertyResolver#getProperty(String, Class)
+ */
+ void setConversionService(ConversionService conversionService);
+
+}
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 6e6190e0079..9aa2e18a248 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
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2010 the original author or authors.
+ * 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.
@@ -16,24 +16,85 @@
package org.springframework.core.env;
-
/**
- * TODO SPR-7508: document
+ * Default implementation of the {@link Environment} interface. Used throughout all non-Web*
+ * ApplicationContext implementations.
*
- * Explain why the default ordering of property sources is the way it is.
+ * In addition to the usual functions of a {@link ConfigurableEnvironment} such as property
+ * resolution and profile-related operations, this implementation configures two default property
+ * sources, to be searched in the following order:
+ *
+ * {@linkplain AbstractEnvironment#getSystemProperties() system properties}
+ * {@linkplain AbstractEnvironment#getSystemEnvironment() system environment variables}
+ *
+ *
+ * That is, if the key "xyz" is present both in the JVM system properties as well as in the
+ * set of environment variables for the current process, the value of key "xyz" from system properties
+ * will return from a call to {@code environment.getPropertyResolver().getProperty("xyz")}.
+ * This ordering is chosen by default because system properties are per-JVM, while environment
+ * variables may be the same across many JVMs on a given system. Giving system properties
+ * precedence allows for overriding of environment variables on a per-JVM basis.
+ *
+ * These default property sources may be removed, reordered, or replaced; and additional
+ * property sources may be added using the {@link MutablePropertySources} instance available
+ * from {@link #getPropertySources()}.
+ *
+ *
Example: adding a new property source with highest search priority
+ *
+ * ConfigurableEnvironment environment = new DefaultEnvironment();
+ * MutablePropertySources propertySources = environment.getPropertySources();
+ * Map myMap = new HashMap();
+ * myMap.put("xyz", "myValue");
+ * propertySources.addFirst(new MapPropertySource("MY_MAP", myMap));
+ *
+ *
+ * Example: removing the default system properties property source
+ *
+ * MutablePropertySources propertySources = environment.getPropertySources();
+ * propertySources.remove(DefaultEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME)
+ *
+ *
+ * Example: mocking the system environment for testing purposes
+ *
+ * MutablePropertySources propertySources = environment.getPropertySources();
+ * MockPropertySource mockEnvVars = new MockPropertySource().withProperty("xyz", "myValue");
+ * propertySources.replace(DefaultEnvironment.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 PropertySources are available during the container bootstrap process,
+ * including use by {@link org.springframework.context.support.PropertySourcesPlaceholderConfigurer
+ * property placeholder configurers}.
*
* @author Chris Beams
* @since 3.1
+ * @see ConfigurableEnvironment
+ * @see org.springframework.web.context.support.DefaultWebEnvironment
*/
public class DefaultEnvironment extends AbstractEnvironment {
+ /** System environment property source name: {@value} */
public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";
+
+ /** JVM system properties property source name: {@value} */
public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";
+ /**
+ * Create a new {@code Environment} populated with property sources in the following order:
+ *
+ * {@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}.
+ */
public DefaultEnvironment() {
- addPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment());
- addPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties());
+ this.getPropertySources().addFirst(new MapPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, this.getSystemEnvironment()));
+ this.getPropertySources().addFirst(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, this.getSystemProperties()));
}
}
diff --git a/org.springframework.core/src/main/java/org/springframework/core/env/Environment.java b/org.springframework.core/src/main/java/org/springframework/core/env/Environment.java
index b85ebc2a351..eb1153c5714 100644
--- a/org.springframework.core/src/main/java/org/springframework/core/env/Environment.java
+++ b/org.springframework.core/src/main/java/org/springframework/core/env/Environment.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2010 the original author or authors.
+ * 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.
@@ -17,98 +17,131 @@
package org.springframework.core.env;
import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
-
/**
- * TODO SPR-7508: document
- * TODO: Consider extracting a PropertyResolutionService interface
+ * Interface representing the environment in which the current application is running.
+ * Models two key aspects of the application environment:
+ *
+ * profiles
+ * properties
+ *
+ *
+ * A profile is a named, logical group of bean definitions to be registered with the
+ * container only if the given profile is active . Beans may be assigned to a profile
+ * whether defined in XML or annotations; see the spring-beans 3.1 schema or the {@link
+ * org.springframework.context.annotation.Profile @Profile} annotation for syntax details.
+ * The role of the Environment object with relation to profiles is in determining which profiles
+ * (if any) are currently {@linkplain #getActiveProfiles active}, and which profiles (if any)
+ * should be {@linkplain #getDefaultProfiles active by default}.
+ *
+ * Properties play an important role in almost all applications, and may originate
+ * from a variety of sources: properties files, JVM system properties, system environment
+ * variables, JNDI, servlet context parameters, ad-hoc Properties objects, Maps, and so on.
+ * The role of the environment object with relation to properties is to provide the user with a
+ * convenient service interface for configuring property sources and resolving properties from them.
+ *
+ *
Beans managed within an ApplicationContext may register to be {@link
+ * org.springframework.context.EnvironmentAware EnvironmentAware}, where they can query profile state
+ * or resolve properties directly.
+ *
+ *
More commonly, beans will not interact with the Environment directly, but will have ${...}
+ * property values replaced by a property placeholder configurer such as {@link
+ * org.springframework.context.support.PropertySourcesPlaceholderConfigurer
+ * PropertySourcesPlaceholderConfigurer}, which itself is EnvironmentAware, and as of Spring 3.1 is
+ * registered by default when using {@code }.
+ *
+ *
Configuration of the environment object must be done through the {@link ConfigurableEnvironment}
+ * interface, returned from all AbstractApplicationContext subclass getEnvironment() methods. See
+ * {@link DefaultEnvironment} for several examples of using the ConfigurableEnvironment interface
+ * to manipulate property sources prior to application context refresh().
*
* @author Chris Beams
* @since 3.1
+ * @see EnvironmentCapable
+ * @see ConfigurableEnvironment
+ * @see DefaultEnvironment
+ * @see org.springframework.context.EnvironmentAware
+ * @see org.springframework.context.ConfigurableApplicationContext#getEnvironment
+ * @see org.springframework.context.ConfigurableApplicationContext#setEnvironment
+ * @see org.springframework.context.support.AbstractApplicationContext#createEnvironment
*/
public interface Environment {
/**
- * TODO SPR-7508: document
+ * Return the set of profiles explicitly made active for this environment. Profiles are used for
+ * creating logical groupings of bean definitions to be registered conditionally, often based on
+ * deployment environment. Profiles can be activated by setting {@linkplain
+ * AbstractEnvironment#ACTIVE_PROFILES_PROPERTY_NAME "spring.profiles.active"} as a system property
+ * or by calling {@link ConfigurableEnvironment#setActiveProfiles(String...)}.
+ *
+ *
If no profiles have explicitly been specified as active, then any 'default' profiles will implicitly
+ * be considered active.
+ *
+ * @see #getDefaultProfiles
+ * @see ConfigurableEnvironment#setActiveProfiles
+ * @see AbstractEnvironment#ACTIVE_PROFILES_PROPERTY_NAME
*/
- Set getActiveProfiles();
+ String[] getActiveProfiles();
/**
- * TODO SPR-7508: document
+ * Return the set of profiles to be active by default when no active profiles have been set explicitly.
+ *
+ * @see #getActiveProfiles
+ * @see ConfigurableEnvironment#setDefaultProfiles
*/
- Set getDefaultProfiles();
+ String[] getDefaultProfiles();
/**
- * TODO SPR-7508: document
- * returns true if:
- * a) one or more of specifiedProfiles are active in the given environment - see {@link #getActiveProfiles()}
- * b) specifiedProfiles contains default profile - see {@link #getDefaultProfile()}
+ * @return whether one or more of the given profiles is active, or in the case of no explicit active
+ * profiles, whether one or more of the given profiles is included in the set of default profiles
+ * @throws IllegalArgumentException unless at least one profile has been specified
+ * @throws IllegalArgumentException if any profile is the empty string or consists only of whitespace
+ * @see #getActiveProfiles
+ * @see #getDefaultProfiles
*/
- boolean acceptsProfiles(String[] specifiedProfiles);
+ boolean acceptsProfiles(String... profiles);
/**
- * TODO SPR-7508: document
+ * @return the {@link PropertyResolver} used for accessing properties.
+ * @see PropertyResolver
+ * @see #getPropertySources
*/
- boolean containsProperty(String key);
+ PropertyResolver getPropertyResolver();
/**
- * TODO SPR-7508: document
+ * Return the set of {@link PropertySource} objects used by by this Environment's PropertyResolver
+ * @see #getPropertyResolver
*/
- String getProperty(String key);
+ PropertySources getPropertySources();
/**
- * TODO SPR-7508: document
- */
- T getProperty(String key, Class targetType);
-
- /**
- * TODO SPR-7508: document
- */
- String getRequiredProperty(String key);
-
- /**
- * TODO SPR-7508: document
- */
- T getRequiredProperty(String key, Class targetType);
-
- /**
- * TODO SPR-7508: document
- */
- int getPropertyCount();
-
- /**
- * TODO SPR-7508: document
- */
- Properties asProperties();
-
- /**
- * TODO SPR-7508: document that this returns {@link System#getenv()} if allowed, or
- * {@link ReadOnlySystemAttributesMap} if not.
+ * 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 issued noting the exception.
*/
Map getSystemEnvironment();
/**
- * TODO SPR-7508: document that this returns {@link System#getProperties()} if allowed, or
- * {@link ReadOnlySystemAttributesMap} if not. Actually, always returns
- * {@link ReadOnlySystemAttributesMap} now.
- * see notes within {@link AbstractEnvironment#getSystemProperties()}
+ * 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 issued noting the exception.
*/
Map getSystemProperties();
- /**
- * TODO SPR-7508: document
- * @see #resolveRequiredPlaceholders(String)
- * @see org.springframework.util.SystemPropertyUtils#resolvePlaceholders(String, int)
- */
- String resolvePlaceholders(String text);
-
- /**
- * TODO SPR-7508: document
- * @see #resolvePlaceholders(String)
- * @see org.springframework.util.SystemPropertyUtils#resolvePlaceholders(String, int)
- */
- String resolveRequiredPlaceholders(String path);
-
}
diff --git a/org.springframework.core/src/main/java/org/springframework/core/env/EnvironmentCapable.java b/org.springframework.core/src/main/java/org/springframework/core/env/EnvironmentCapable.java
index 5705502aecd..6e96cb26d02 100644
--- a/org.springframework.core/src/main/java/org/springframework/core/env/EnvironmentCapable.java
+++ b/org.springframework.core/src/main/java/org/springframework/core/env/EnvironmentCapable.java
@@ -18,15 +18,32 @@ package org.springframework.core.env;
/**
- * TODO SPR-7508: document
- *
+ * Interface indicating a component contains and makes available an {@link Environment} object.
+ *
+ * All Spring application contexts are EnvironmentCapable, and the interface is used primarily
+ * for performing {@code instanceof} checks in framework methods that accept BeanFactory
+ * instances that may or may not actually be ApplicationContext instances in order to interact
+ * with the environment if indeed it is available.
+ *
+ *
As mentioned, {@link org.springframework.context.ApplicationContext ApplicationContext}
+ * extends EnvironmentCapable, and thus exposes a {@link #getEnvironment()} method; however,
+ * {@link org.springframework.context.ConfigurableApplicationContext ConfigurableApplicationContext}
+ * redefines {@link org.springframework.context.ConfigurableApplicationContext#getEnvironment
+ * getEnvironment()} and narrows the signature to return a {@link ConfigurableEnvironment}. The effect
+ * is that an Environment object is 'read-only' until it accessed from a ConfigurableApplicationContext,
+ * at which point it too may be configured.
+ *
* @author Chris Beams
* @since 3.1
* @see Environment
- * @see ConfigurableEnvironmentCapable
+ * @see ConfigurableEnvironment
+ * @see org.springframework.context.ConfigurableApplicationContext#getEnvironment
*/
public interface EnvironmentCapable {
+ /**
+ * Return the Environment for this object
+ */
Environment getEnvironment();
}
diff --git a/org.springframework.core/src/main/java/org/springframework/core/env/MapPropertySource.java b/org.springframework.core/src/main/java/org/springframework/core/env/MapPropertySource.java
index 469c590cc97..2efadf30561 100644
--- a/org.springframework.core/src/main/java/org/springframework/core/env/MapPropertySource.java
+++ b/org.springframework.core/src/main/java/org/springframework/core/env/MapPropertySource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2010 the original author or authors.
+ * 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.
@@ -18,18 +18,12 @@ package org.springframework.core.env;
import java.util.Map;
-
/**
- * TODO SPR-7508: document
- *
- * Consider adding a TypeConvertingMapPropertySource to accommodate
- * non-string keys and values. Could be confusing when used in conjunction
- * with Environment.getProperty(), which also does type conversions. If this
- * is added, consider renaming this class to SimpleMapPropertySource and
- * rename PropertiesPropertySource to SimplePropertiesPropertySource.
+ * {@link PropertySource} that reads keys and values from a {@code Map} object.
*
* @author Chris Beams
* @since 3.1
+ * @see PropertiesPropertySource
*/
public class MapPropertySource extends PropertySource> {
@@ -37,17 +31,14 @@ public class MapPropertySource extends PropertySource> {
super(name, source);
}
- public boolean containsProperty(String key) {
- return source.containsKey(key);
- }
-
- public String getProperty(String key) {
- return source.get(key);
+ @Override
+ public String[] getPropertyNames() {
+ return this.source.keySet().toArray(EMPTY_NAMES_ARRAY);
}
@Override
- public int size() {
- return source.size();
+ public String getProperty(String key) {
+ return this.source.get(key);
}
}
\ No newline at end of file
diff --git a/org.springframework.core/src/main/java/org/springframework/core/env/MutablePropertySources.java b/org.springframework.core/src/main/java/org/springframework/core/env/MutablePropertySources.java
new file mode 100644
index 00000000000..9483c645775
--- /dev/null
+++ b/org.springframework.core/src/main/java/org/springframework/core/env/MutablePropertySources.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2002-2010 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.core.env;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.springframework.util.Assert;
+
+
+public class MutablePropertySources implements PropertySources {
+
+ private final LinkedList> propertySourceList = new LinkedList>();
+
+ static final String NON_EXISTENT_PROPERTY_SOURCE_MESSAGE = "PropertySource named [%s] does not exist";
+ static final String ILLEGAL_RELATIVE_ADDITION_MESSAGE = "PropertySource named [%s] cannot be added relative to itself";
+
+
+ public MutablePropertySources() {
+ }
+
+ public MutablePropertySources(PropertySources propertySources) {
+ this.addAll(propertySources);
+ }
+
+ public void addAll(PropertySources propertySources) {
+ for (PropertySource> propertySource : propertySources.asList()) {
+ this.addLast(propertySource);
+ }
+ }
+
+ public void addFirst(PropertySource> propertySource) {
+ removeIfPresent(propertySource);
+ this.propertySourceList.addFirst(propertySource);
+ }
+
+ public void addLast(PropertySource> propertySource) {
+ removeIfPresent(propertySource);
+ this.propertySourceList.addLast(propertySource);
+ }
+
+ public void addBefore(String relativePropertySourceName, PropertySource> propertySource) {
+ assertLegalRelativeAddition(relativePropertySourceName, propertySource);
+ removeIfPresent(propertySource);
+ int index = assertPresentAndGetIndex(relativePropertySourceName);
+ addAtIndex(index, propertySource);
+ }
+
+ public void addAfter(String relativePropertySourceName, PropertySource> propertySource) {
+ assertLegalRelativeAddition(relativePropertySourceName, propertySource);
+ removeIfPresent(propertySource);
+ int index = assertPresentAndGetIndex(relativePropertySourceName);
+ addAtIndex(index+1, propertySource);
+ }
+
+ protected void assertLegalRelativeAddition(String relativePropertySourceName, PropertySource> propertySource) {
+ String newPropertySourceName = propertySource.getName();
+ Assert.isTrue(!relativePropertySourceName.equals(newPropertySourceName),
+ String.format(ILLEGAL_RELATIVE_ADDITION_MESSAGE, newPropertySourceName));
+ }
+
+ protected void addAtIndex(int index, PropertySource> propertySource) {
+ removeIfPresent(propertySource);
+ this.propertySourceList.add(index, propertySource);
+ }
+
+ protected void removeIfPresent(PropertySource> propertySource) {
+ if (this.propertySourceList.contains(propertySource)) {
+ this.propertySourceList.remove(propertySource);
+ }
+ }
+
+ public boolean contains(String propertySourceName) {
+ return propertySourceList.contains(PropertySource.named(propertySourceName));
+ }
+
+ public PropertySource> remove(String propertySourceName) {
+ int index = propertySourceList.indexOf(PropertySource.named(propertySourceName));
+ if (index >= 0) {
+ return propertySourceList.remove(index);
+ }
+ return null;
+ }
+
+ public void replace(String propertySourceName, PropertySource> propertySource) {
+ int index = assertPresentAndGetIndex(propertySourceName);
+ this.propertySourceList.set(index, propertySource);
+ }
+
+ protected int assertPresentAndGetIndex(String propertySourceName) {
+ int index = this.propertySourceList.indexOf(PropertySource.named(propertySourceName));
+ Assert.isTrue(index >= 0, String.format(NON_EXISTENT_PROPERTY_SOURCE_MESSAGE, propertySourceName));
+ return index;
+ }
+
+ public int size() {
+ return propertySourceList.size();
+ }
+
+ public List> asList() {
+ return Collections.unmodifiableList(this.propertySourceList);
+ }
+
+ public PropertySource> get(String propertySourceName) {
+ return propertySourceList.get(propertySourceList.indexOf(PropertySource.named(propertySourceName)));
+ }
+
+}
diff --git a/org.springframework.core/src/main/java/org/springframework/core/env/PropertiesPropertySource.java b/org.springframework.core/src/main/java/org/springframework/core/env/PropertiesPropertySource.java
index 98ac9647fbf..1b52a7c1e9b 100644
--- a/org.springframework.core/src/main/java/org/springframework/core/env/PropertiesPropertySource.java
+++ b/org.springframework.core/src/main/java/org/springframework/core/env/PropertiesPropertySource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2010 the original author or authors.
+ * 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.
@@ -16,39 +16,26 @@
package org.springframework.core.env;
+import java.util.Map;
import java.util.Properties;
-
/**
- * TODO SPR-7508: document how this does accept a Properties object,
- * which is capable of holding non-string keys and values (because
- * Properties is a Hashtable), but is limited to resolving string-based
- * keys and values.
+ * {@link PropertySource} implementation that extracts properties from a {@link java.util.Properties} object.
*
- * Consider adding a TypeConvertingPropertiesPropertySource to accommodate
- * non-string keys and values (such as is technically possible with
- * System.getProperties())
+ * Note that because a {@code Properties} object is technically an {@code }
+ * {@link java.util.Hashtable Hashtable}, one may contain non-{@code String} keys or values. This
+ * implementation, however is restricted to accessing only {@code String}-based keys and values, in
+ * the same fashion as {@link Properties#getProperty} and {@link Properties#setProperty}.
*
* @author Chris Beams
* @since 3.1
+ * @see org.springframework.mock.env.MockPropertySource
*/
-public class PropertiesPropertySource extends PropertySource {
+public class PropertiesPropertySource extends MapPropertySource {
+ @SuppressWarnings({ "unchecked", "rawtypes" })
public PropertiesPropertySource(String name, Properties source) {
- super(name, source);
- }
-
- public boolean containsProperty(String key) {
- return source.containsKey(key);
- }
-
- public String getProperty(String key) {
- return source.getProperty(key);
- }
-
- @Override
- public int size() {
- return source.size();
+ super(name, (Map)source);
}
}
diff --git a/org.springframework.core/src/main/java/org/springframework/core/env/PropertyResolver.java b/org.springframework.core/src/main/java/org/springframework/core/env/PropertyResolver.java
new file mode 100644
index 00000000000..ccc2b828cc1
--- /dev/null
+++ b/org.springframework.core/src/main/java/org/springframework/core/env/PropertyResolver.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2002-2010 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.core.env;
+
+import java.util.Properties;
+
+
+/**
+ * Interface for resolving properties against any underlying source.
+ *
+ * @author Chris Beams
+ * @since 3.1
+ * @see Environment#getPropertyResolver()
+ */
+public interface PropertyResolver {
+
+ /**
+ * @return whether the given property key is available for resolution
+ */
+ boolean containsProperty(String key);
+
+ /**
+ * @return the property value associated with the given key
+ * @see #getProperty(String, Class)
+ */
+ String getProperty(String key);
+
+ /**
+ * @return the property value associated with the given key, or {@code null}
+ * if the key cannot be resolved
+ */
+ T getProperty(String key, Class targetType);
+
+ /**
+ * @return the property value associated with the given key, converted to the given
+ * targetType (never {@code null})
+ * @throws IllegalStateException if the key cannot be resolved
+ * @see #getRequiredProperty(String, Class)
+ */
+ String getRequiredProperty(String key) throws IllegalStateException;
+
+ /**
+ * @return the property value associated with the given key, converted to the given
+ * targetType (never {@code null})
+ * @throws IllegalStateException if the given key cannot be resolved
+ */
+ T getRequiredProperty(String key, Class targetType) throws IllegalStateException;
+
+ /**
+ * @return the number of unique properties keys resolvable
+ */
+ int getPropertyCount();
+
+ /**
+ * @return all property key/value pairs as a {@link java.util.Properties} instance
+ */
+ Properties asProperties();
+
+ /**
+ * Resolve ${...} placeholders in the given text, replacing them with corresponding
+ * property values as resolved by {@link #getProperty}. Unresolvable placeholders with
+ * no default value are ignored and passed through unchanged.
+ * @param text the String to resolve
+ * @return the resolved String (never {@code null})
+ * @throws IllegalArgumentException if given text is {@code null}
+ * @see #resolveRequiredPlaceholders
+ * @see org.springframework.util.SystemPropertyUtils#resolvePlaceholders(String)
+ */
+ String resolvePlaceholders(String text);
+
+ /**
+ * Resolve ${...} placeholders in the given text, replacing them with corresponding
+ * property values as resolved by {@link #getProperty}. Unresolvable placeholders with
+ * no default value will cause an IllegalArgumentException to be thrown.
+ * @return the resolved String (never {@code null})
+ * @throws IllegalArgumentException if given text is {@code null}
+ * @throws IllegalArgumentException if any placeholders are unresolvable
+ * @see org.springframework.util.SystemPropertyUtils#resolvePlaceholders(String, boolean)
+ */
+ String resolveRequiredPlaceholders(String path) throws IllegalArgumentException;
+
+}
diff --git a/org.springframework.core/src/main/java/org/springframework/core/env/PropertySource.java b/org.springframework.core/src/main/java/org/springframework/core/env/PropertySource.java
index 31ee0124a2c..18443dd28ca 100644
--- a/org.springframework.core/src/main/java/org/springframework/core/env/PropertySource.java
+++ b/org.springframework.core/src/main/java/org/springframework/core/env/PropertySource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2010 the original author or authors.
+ * 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.
@@ -18,43 +18,122 @@ package org.springframework.core.env;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.springframework.util.Assert;
-
+/**
+ * Abstract base class representing a source of key/value property pairs. The underlying
+ * {@linkplain #getSource() source object} may be of any type {@code T} that encapsulates
+ * properties. Examples include {@link java.util.Properties} objects, {@link java.util.Map}
+ * objects, {@code ServletContext} and {@code ServletConfig} objects (for access to init parameters).
+ * Explore the {@code PropertySource} type hierarchy to see provided implementations.
+ *
+ * {@code PropertySource} objects are not typically used in isolation, but rather through a
+ * {@link PropertySources} object, which aggregates property sources and in conjunction with
+ * a {@link PropertyResolver} implementation that can perform precedence-based searches across
+ * the set of {@code PropertySources}.
+ *
+ *
{@code PropertySource} identity is determined not based on the content of encapsulated
+ * properties, but rather based on the {@link #getName() name} of the {@code PropertySource}
+ * alone. This is useful for manipulating {@code PropertySource} objects when in collection
+ * contexts. See operations in {@link MutablePropertySources} as well as the
+ * {@link #named(String)} and {@link #toString()} methods for details.
+ *
+ * @author Chris Beams
+ * @since 3.1
+ * @see PropertySources
+ * @see PropertyResolver
+ * @see PropertySourcesPropertyResolver
+ * @see MutablePropertySources
+ */
public abstract class PropertySource {
+ protected static final String[] EMPTY_NAMES_ARRAY = new String[0];
+
protected final Log logger = LogFactory.getLog(this.getClass());
protected final String name;
+
protected final T source;
+ /**
+ * Create a new {@code PropertySource} with the given name and source object.
+ */
public PropertySource(String name, T source) {
+ Assert.hasText(name, "Property source name must contain at least one character");
+ Assert.notNull(source, "Property source must not be null");
this.name = name;
this.source = source;
}
+ /**
+ * Return the name of this {@code PropertySource}
+ */
public String getName() {
- return name;
+ return this.name;
}
+ /**
+ * Return the underlying source object for this {@code PropertySource}.
+ */
public T getSource() {
return source;
}
- public abstract boolean containsProperty(String key);
+ /**
+ * Return the names of all properties contained by the {@linkplain #getSource() source}
+ * object (never {@code null}).
+ */
+ public abstract String[] getPropertyNames();
+ /**
+ * Return the value associated with the given key, {@code null} if not found.
+ * @param key the property key to find
+ * @see PropertyResolver#getRequiredProperty(String)
+ */
public abstract String getProperty(String key);
- public abstract int size();
+ /**
+ * Return whether this {@code PropertySource} contains a property with the given key.
+ * @param key the property key to find
+ */
+ public boolean containsProperty(String name) {
+ Assert.notNull(name, "property name must not be null");
+ for (String candidate : this.getPropertyNames()) {
+ if (candidate.equals(name)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ /**
+ * Return the number of unique property keys available to this {@code PropertySource}.
+ */
+ public int size() {
+ return this.getPropertyNames().length;
+ }
+ /**
+ * Return a hashcode derived from the {@code name} property of this {@code PropertySource}
+ * object.
+ */
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
- result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + ((this.name == null) ? 0 : this.name.hashCode());
return result;
}
+ /**
+ * This {@code PropertySource} object is equal to the given object if:
+ *
+ * they are the same instance
+ * the {@code name} properties for both objects are equal
+ *
+ *
+ * No properties other than {@code name} are evaluated.
+ */
@Override
public boolean equals(Object obj) {
if (this == obj)
@@ -64,10 +143,10 @@ public abstract class PropertySource {
if (!(obj instanceof PropertySource))
return false;
PropertySource> other = (PropertySource>) obj;
- if (name == null) {
+ if (this.name == null) {
if (other.name != null)
return false;
- } else if (!name.equals(other.name))
+ } else if (!this.name.equals(other.name))
return false;
return true;
}
@@ -87,17 +166,35 @@ public abstract class PropertySource {
public String toString() {
if (logger.isDebugEnabled()) {
return String.format("%s@%s [name='%s', properties=%s]",
- getClass().getSimpleName(), System.identityHashCode(this), name, source);
+ this.getClass().getSimpleName(), System.identityHashCode(this), this.name, this.source);
}
return String.format("%s [name='%s', propertyCount=%d]",
- getClass().getSimpleName(), name, this.size());
+ this.getClass().getSimpleName(), this.name, this.size());
}
/**
- * For collection comparison purposes
- * TODO SPR-7508: document
+ * Return a {@code PropertySource} implementation intended for collection comparison purposes only.
+ *
+ * Primarily for internal use, but given a collection of {@code PropertySource} objects, may be
+ * used as follows:
+ *
+ * {@code
+ * List> sources = new ArrayList>();
+ * sources.add(new MapPropertySource("sourceA", mapA));
+ * sources.add(new MapPropertySource("sourceB", mapB));
+ * assert sources.contains(PropertySource.named("sourceA"));
+ * assert sources.contains(PropertySource.named("sourceB"));
+ * assert !sources.contains(PropertySource.named("sourceC"));
+ * }
+ *
+ *
+ * The returned {@code PropertySource} will throw {@code UnsupportedOperationException}
+ * if any methods other than {@code equals(Object)}, {@code hashCode()}, and {@code toString()}
+ * are called.
+ *
+ * @param name the name of the comparison {@code PropertySource} to be created and returned.
*/
public static PropertySource> named(String name) {
return new ComparisonPropertySource(name);
@@ -105,35 +202,69 @@ public abstract class PropertySource {
/**
- * TODO: SPR-7508: document
+ * {@code PropertySource} to be used as a placeholder in cases where an actual
+ * property source cannot be eagerly initialized at application context
+ * creation time. For example, a {@code ServletContext}-based property source
+ * must wait until the {@code ServletContext} object is available to its enclosing
+ * {@code ApplicationContext}. In such cases, a stub should be used to hold the
+ * intended default position/order of the property source, then be replaced
+ * during context refresh.
+ *
+ * @see org.springframework.context.support.AbstractApplicationContext#initPropertySources()
+ * @see org.springframework.web.context.support.DefaultWebEnvironment
+ * @see org.springframework.web.context.support.ServletContextPropertySource
*/
- public static class ComparisonPropertySource extends PropertySource{
+ public static class StubPropertySource extends PropertySource {
+
+ public StubPropertySource(String name) {
+ super(name, new Object());
+ }
+
+ @Override
+ public String getProperty(String key) {
+ // TODO SPR-7408: logging
+ return null;
+ }
+
+ @Override
+ public String[] getPropertyNames() {
+ return EMPTY_NAMES_ARRAY;
+ }
+ }
+
+
+ /**
+ * @see PropertySource#named(String)
+ */
+ static class ComparisonPropertySource extends StubPropertySource {
private static final String USAGE_ERROR =
"ComparisonPropertySource instances are for collection comparison " +
"use only";
public ComparisonPropertySource(String name) {
- super(name, null);
+ super(name);
}
@Override
- public Void getSource() {
+ public Object getSource() {
throw new UnsupportedOperationException(USAGE_ERROR);
}
+
+ @Override
+ public String[] getPropertyNames() {
+ throw new UnsupportedOperationException(USAGE_ERROR);
+ }
+
+ @Override
public String getProperty(String key) {
throw new UnsupportedOperationException(USAGE_ERROR);
}
- public boolean containsProperty(String key) {
- throw new UnsupportedOperationException(USAGE_ERROR);
- }
- public int size() {
- throw new UnsupportedOperationException(USAGE_ERROR);
- }
@Override
public String toString() {
- return String.format("%s [name='%s']", getClass().getSimpleName(), name);
+ return String.format("%s [name='%s']", getClass().getSimpleName(), this.name);
}
}
+
}
diff --git a/org.springframework.core/src/main/java/org/springframework/core/env/DefaultWebEnvironment.java b/org.springframework.core/src/main/java/org/springframework/core/env/PropertySources.java
similarity index 66%
rename from org.springframework.core/src/main/java/org/springframework/core/env/DefaultWebEnvironment.java
rename to org.springframework.core/src/main/java/org/springframework/core/env/PropertySources.java
index 97c3986a0ac..ff0a6f4a51a 100644
--- a/org.springframework.core/src/main/java/org/springframework/core/env/DefaultWebEnvironment.java
+++ b/org.springframework.core/src/main/java/org/springframework/core/env/PropertySources.java
@@ -16,16 +16,17 @@
package org.springframework.core.env;
+import java.util.List;
-/**
- * TODO SPR-7508: document
- *
- * @author Chris Beams
- * @since 3.1
- */
-public class DefaultWebEnvironment extends DefaultEnvironment {
- public static final String SERVLET_CONTEXT_PARAMS_PROPERTY_SOURCE_NAME = "servletContextInitParams";
- public static final String SERVLET_CONFIG_PARAMS_PROPERTY_SOURCE_NAME = "servletConfigInitParams";
+public interface PropertySources {
+
+ PropertySource> get(String propertySourceName);
+
+ List> asList();
+
+ int size();
+
+ boolean contains(String propertySourceName);
}
diff --git a/org.springframework.core/src/main/java/org/springframework/core/env/PropertySourcesPropertyResolver.java b/org.springframework.core/src/main/java/org/springframework/core/env/PropertySourcesPropertyResolver.java
new file mode 100644
index 00000000000..a8c31452dec
--- /dev/null
+++ b/org.springframework.core/src/main/java/org/springframework/core/env/PropertySourcesPropertyResolver.java
@@ -0,0 +1,109 @@
+/*
+ * 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.core.env;
+
+import static java.lang.String.format;
+
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * {@link PropertyResolver} implementation that resolves property values against
+ * an underlying set of {@link PropertySources}.
+ *
+ * @author Chris Beams
+ * @since 3.1
+ */
+public class PropertySourcesPropertyResolver extends AbstractPropertyResolver {
+
+ private final PropertySources propertySources;
+
+ /**
+ * Create a new resolver against the given property sources.
+ * @param propertySources the set of {@link PropertySource} objects to use
+ */
+ public PropertySourcesPropertyResolver(PropertySources propertySources) {
+ this.propertySources = propertySources;
+ }
+
+
+ public boolean containsProperty(String key) {
+ for (PropertySource> propertySource : this.propertySources.asList()) {
+ if (propertySource.containsProperty(key)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public String getProperty(String key) {
+ if (logger.isTraceEnabled()) {
+ logger.trace(format("getProperty(\"%s\") (implicit targetType [String])", key));
+ }
+ return this.getProperty(key, String.class);
+ }
+
+ public T getProperty(String key, Class targetValueType) {
+ boolean debugEnabled = logger.isDebugEnabled();
+ if (logger.isTraceEnabled()) {
+ logger.trace(format("getProperty(\"%s\", %s)", key, targetValueType.getSimpleName()));
+ }
+
+ for (PropertySource> propertySource : this.propertySources.asList()) {
+ if (debugEnabled) {
+ logger.debug(format("Searching for key '%s' in [%s]", key, propertySource.getName()));
+ }
+ if (propertySource.containsProperty(key)) {
+ Object value = propertySource.getProperty(key);
+ Class> valueType = value == null ? null : value.getClass();
+ if (debugEnabled) {
+ logger.debug(
+ format("Found key '%s' in [%s] with type [%s] and value '%s'",
+ key, propertySource.getName(),
+ valueType == null ? "" : valueType.getSimpleName(), value));
+ }
+ if (value == null) {
+ return null;
+ }
+ if (!this.conversionService.canConvert(valueType, targetValueType)) {
+ throw new IllegalArgumentException(
+ format("Cannot convert value [%s] from source type [%s] to target type [%s]",
+ value, valueType.getSimpleName(), targetValueType.getSimpleName()));
+ }
+ return conversionService.convert(value, targetValueType);
+ }
+ }
+
+ if (debugEnabled) {
+ logger.debug(format("Could not find key '%s' in any property source. Returning [null]", key));
+ }
+ return null;
+ }
+
+ public Properties asProperties() {
+ Properties mergedProps = new Properties();
+ List> propertySourcesList = this.propertySources.asList();
+ for (int i = propertySourcesList.size() -1; i >= 0; i--) {
+ PropertySource> source = propertySourcesList.get(i);
+ for (String key : source.getPropertyNames()) {
+ mergedProps.put(key, source.getProperty(key));
+ }
+ }
+ return mergedProps;
+ }
+
+}
diff --git a/org.springframework.core/src/main/java/org/springframework/core/env/ReadOnlySystemAttributesMap.java b/org.springframework.core/src/main/java/org/springframework/core/env/ReadOnlySystemAttributesMap.java
index c31cb97d025..df310c6002b 100644
--- a/org.springframework.core/src/main/java/org/springframework/core/env/ReadOnlySystemAttributesMap.java
+++ b/org.springframework.core/src/main/java/org/springframework/core/env/ReadOnlySystemAttributesMap.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2009 the original author or authors.
+ * 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.
@@ -20,6 +20,7 @@ import java.util.Collection;
import java.util.Map;
import java.util.Set;
+import org.springframework.util.Assert;
/**
* Read-only {@code Map} implementation that is backed by system properties or environment
@@ -38,32 +39,16 @@ abstract class ReadOnlySystemAttributesMap implements Map {
return get(key) != null;
}
+ /**
+ * @param key the name of the system attribute to retrieve
+ * @throws IllegalArgumentException if given key is non-String
+ */
public String get(Object key) {
- if (key instanceof String) {
- String attributeName = (String) key;
- return getSystemAttribute(attributeName);
- }
- else {
- // TODO SPR-7508: technically breaks backward-compat. Used to return null
- // for non-string keys, now throws. Any callers who have coded to this
- // behavior will now break. It's highly unlikely, however; could be
- // a calculated risk to take. Throwing is a better choice, as returning
- // null represents a 'false negative' - it's not actually that the key
- // isn't present, it's simply that you cannot access it through the current
- // abstraction. Remember, this case would only come up if (a) there are
- // non-string keys or values in system properties, (b) there is a
- // SecurityManager present, and (c) the user attempts to access one
- // of those properties through this abstraction. This combination is
- // probably unlikely enough to merit the change.
- //
- // note also that the previous implementation didn't consider the
- // possibility of non-string values the anonymous implementation used
- // for System properties access now does.
- //
- // See AbstractEnvironment for relevant anonymous implementations
- // See DefaultEnvironmentTests for unit tests around these cases
- throw new IllegalStateException("TODO SPR-7508: message");
- }
+ Assert.isInstanceOf(String.class, key,
+ String.format("expected key [%s] to be of type String, got %s",
+ key, key.getClass().getName()));
+
+ return this.getSystemAttribute((String) key);
}
public boolean isEmpty() {
diff --git a/org.springframework.core/src/main/java/org/springframework/core/io/ResourceEditor.java b/org.springframework.core/src/main/java/org/springframework/core/io/ResourceEditor.java
index 6146040cc0c..8cca8d305c3 100644
--- a/org.springframework.core/src/main/java/org/springframework/core/io/ResourceEditor.java
+++ b/org.springframework.core/src/main/java/org/springframework/core/io/ResourceEditor.java
@@ -21,16 +21,16 @@ import java.io.IOException;
import org.springframework.core.env.DefaultEnvironment;
import org.springframework.core.env.Environment;
+import org.springframework.core.env.PropertyResolver;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* {@link java.beans.PropertyEditor Editor} for {@link Resource}
- * descriptors, to automatically convert String locations
- * e.g. "file:C:/myfile.txt" or
- * "classpath:myfile.txt") to Resource
- * properties instead of using a String location property.
+ * descriptors, to automatically convert {@code String} locations
+ * e.g. {@code file:C:/myfile.txt} or {@code classpath:myfile.txt} to
+ * {@code Resource} properties instead of using a {@code String} location property.
*
* The path may contain ${...} placeholders, to be
* resolved as {@link Environment} properties: e.g. ${user.dir}.
@@ -46,7 +46,7 @@ import org.springframework.util.StringUtils;
* @see Resource
* @see ResourceLoader
* @see DefaultResourceLoader
- * @see org.springframework.env.Environment#resolvePlaceholders
+ * @see Environment#resolvePlaceholders
*/
public class ResourceEditor extends PropertyEditorSupport {
@@ -137,9 +137,10 @@ public class ResourceEditor extends PropertyEditorSupport {
* @see Environment#resolveRequiredPlaceholders
*/
protected String resolvePath(String path) {
+ PropertyResolver resolver = environment.getPropertyResolver();
return this.ignoreUnresolvablePlaceholders ?
- environment.resolvePlaceholders(path) :
- environment.resolveRequiredPlaceholders(path);
+ resolver.resolvePlaceholders(path) :
+ resolver.resolveRequiredPlaceholders(path);
}
diff --git a/org.springframework.core/src/main/java/org/springframework/core/io/support/ResourceArrayPropertyEditor.java b/org.springframework.core/src/main/java/org/springframework/core/io/support/ResourceArrayPropertyEditor.java
index 89cb3786011..53d3c89faac 100644
--- a/org.springframework.core/src/main/java/org/springframework/core/io/support/ResourceArrayPropertyEditor.java
+++ b/org.springframework.core/src/main/java/org/springframework/core/io/support/ResourceArrayPropertyEditor.java
@@ -27,6 +27,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.env.DefaultEnvironment;
import org.springframework.core.env.Environment;
+import org.springframework.core.env.PropertyResolver;
import org.springframework.core.io.Resource;
/**
@@ -196,9 +197,10 @@ public class ResourceArrayPropertyEditor extends PropertyEditorSupport {
* @see Environment#resolveRequiredPlaceholders
*/
protected String resolvePath(String path) {
+ PropertyResolver resolver = environment.getPropertyResolver();
return this.ignoreUnresolvablePlaceholders ?
- environment.resolvePlaceholders(path) :
- environment.resolveRequiredPlaceholders(path);
+ resolver.resolvePlaceholders(path) :
+ resolver.resolveRequiredPlaceholders(path);
}
}
diff --git a/org.springframework.core/src/main/java/org/springframework/util/CollectionUtils.java b/org.springframework.core/src/main/java/org/springframework/util/CollectionUtils.java
index 249de6439a7..a6d87b323ca 100644
--- a/org.springframework.core/src/main/java/org/springframework/util/CollectionUtils.java
+++ b/org.springframework.core/src/main/java/org/springframework/util/CollectionUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2010 the original author or authors.
+ * 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.
@@ -16,6 +16,7 @@
package org.springframework.util;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Enumeration;
@@ -301,7 +302,20 @@ public abstract class CollectionUtils {
}
/**
- * Adapts an enumeration to an iterator.
+ * Marshal the elements from the given enumeration into an array of the given type.
+ * Enumeration elements must be assignable to the type of the given array. The array
+ * returned will be a different instance than the array given.
+ */
+ public static A[] toArray(Enumeration enumeration, A[] array) {
+ ArrayList elements = new ArrayList ();
+ while (enumeration.hasMoreElements()) {
+ elements.add(enumeration.nextElement());
+ }
+ return elements.toArray(array);
+ }
+
+ /**
+ * Adapt an enumeration to an iterator.
* @param enumeration the enumeration
* @return the iterator
*/
diff --git a/org.springframework.core/src/main/java/org/springframework/util/SystemPropertyUtils.java b/org.springframework.core/src/main/java/org/springframework/util/SystemPropertyUtils.java
index 8fbcda5510f..6e8eaff2f13 100644
--- a/org.springframework.core/src/main/java/org/springframework/util/SystemPropertyUtils.java
+++ b/org.springframework.core/src/main/java/org/springframework/util/SystemPropertyUtils.java
@@ -25,12 +25,6 @@ import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver;
* ${user.dir}. Default values can be supplied using the ":" separator between key
* and value.
*
- * TODO SPR-7508: review item - nearly all uses of {@link SystemPropertyUtils#resolvePlaceholders(String)}
- * have been replaced by Environment#resolvePlaceholder(), however, there are several locations in the
- * framework that cannot be so refactored as referring to Environment would introduce a cycle. Case in point
- * Log4JConfigurer and Log4JWebConfigurer. Need to unify this functionality one way or another. It's
- * currently pure duplication.
- *
* @author Juergen Hoeller
* @author Rob Harrop
* @author Dave Syer
diff --git a/org.springframework.core/src/test/java/org/springframework/core/env/DefaultEnvironmentTests.java b/org.springframework.core/src/test/java/org/springframework/core/env/DefaultEnvironmentTests.java
index b8c955e6f13..998e7da83e2 100644
--- a/org.springframework.core/src/test/java/org/springframework/core/env/DefaultEnvironmentTests.java
+++ b/org.springframework.core/src/test/java/org/springframework/core/env/DefaultEnvironmentTests.java
@@ -16,412 +16,22 @@
package org.springframework.core.env;
-import java.lang.reflect.Field;
-import java.security.AccessControlException;
-import java.security.Permission;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import java.util.List;
-import org.hamcrest.Description;
-import org.hamcrest.Matcher;
-import org.junit.Before;
import org.junit.Test;
-import org.junit.internal.matchers.TypeSafeMatcher;
-import static java.lang.String.format;
-import static org.hamcrest.CoreMatchers.*;
-import static org.junit.Assert.*;
-import static org.junit.matchers.JUnitMatchers.*;
-import static org.springframework.core.env.AbstractEnvironment.*;
-import static org.springframework.core.env.DefaultEnvironmentTests.CollectionMatchers.*;
-
-/**
- * Unit tests for {@link DefaultEnvironment}.
- *
- * @author Chris Beams
- */
public class DefaultEnvironmentTests {
- private static final String ALLOWED_PROPERTY_NAME = "theanswer";
- private static final String ALLOWED_PROPERTY_VALUE = "42";
-
- private static final String DISALLOWED_PROPERTY_NAME = "verboten";
- private static final String DISALLOWED_PROPERTY_VALUE = "secret";
-
- private static final String STRING_PROPERTY_NAME = "stringPropName";
- private static final String STRING_PROPERTY_VALUE = "stringPropValue";
- private static final Object NON_STRING_PROPERTY_NAME = new Object();
- private static final Object NON_STRING_PROPERTY_VALUE = new Object();
-
- private ConfigurableEnvironment environment;
- private Properties testProperties;
-
- @Before
- public void setUp() {
- environment = new DefaultEnvironment();
- testProperties = new Properties();
- environment.addPropertySource("testProperties", testProperties);
- }
-
- @Test @SuppressWarnings({ "unchecked", "rawtypes", "serial" })
- public void getPropertySources_manipulatePropertySourceOrder() {
- AbstractEnvironment env = new AbstractEnvironment() { };
- env.addPropertySource("system", new HashMap() {{ put("foo", "systemValue"); }});
- env.addPropertySource("local", new HashMap() {{ put("foo", "localValue"); }});
-
- // 'local' was added (pushed) last so has precedence
- assertThat(env.getProperty("foo"), equalTo("localValue"));
-
- // put 'system' at the front of the list
- LinkedList> propertySources = env.getPropertySources();
- propertySources.addFirst(propertySources.remove(propertySources.indexOf(PropertySource.named("system"))));
-
- // 'system' now has precedence
- assertThat(env.getProperty("foo"), equalTo("systemValue"));
-
- assertThat(propertySources.size(), is(2));
- }
-
- @Test @SuppressWarnings({ "unchecked", "rawtypes", "serial" })
- public void getPropertySources_replacePropertySource() {
- AbstractEnvironment env = new AbstractEnvironment() { };
- env.addPropertySource("system", new HashMap() {{ put("foo", "systemValue"); }});
- env.addPropertySource("local", new HashMap() {{ put("foo", "localValue"); }});
-
- // 'local' was added (pushed) last so has precedence
- assertThat(env.getProperty("foo"), equalTo("localValue"));
-
- // replace 'local' with new property source
- LinkedList> propertySources = env.getPropertySources();
- int localIndex = propertySources.indexOf(PropertySource.named("local"));
- MapPropertySource newSource = new MapPropertySource("new", new HashMap() {{ put("foo", "newValue"); }});
- propertySources.set(localIndex, newSource);
-
- // 'system' now has precedence
- assertThat(env.getProperty("foo"), equalTo("newValue"));
-
- assertThat(propertySources.size(), is(2));
- }
-
@Test
- public void getProperty() {
- assertThat(environment.getProperty("foo"), nullValue());
- testProperties.put("foo", "bar");
- assertThat(environment.getProperty("foo"), is("bar"));
- }
-
- @Test
- public void getProperty_withExplicitNullValue() {
- // java.util.Properties does not allow null values (because Hashtable does not)
- Map nullableProperties = new HashMap();
- environment.addPropertySource("nullableProperties", nullableProperties);
- nullableProperties.put("foo", null);
- assertThat(environment.getProperty("foo"), nullValue());
- }
-
- @Test
- public void getProperty_withStringArrayConversion() {
- testProperties.put("foo", "bar,baz");
- assertThat(environment.getProperty("foo", String[].class), equalTo(new String[] { "bar", "baz" }));
- }
-
- @Test
- public void getProperty_withNonConvertibleTargetType() {
- testProperties.put("foo", "bar");
-
- class TestType { }
-
- try {
- environment.getProperty("foo", TestType.class);
- fail("Expected IllegalArgumentException due to non-convertible types");
- } catch (IllegalArgumentException ex) {
- // expected
- }
- }
-
- @Test
- public void getRequiredProperty() {
- testProperties.put("exists", "xyz");
- assertThat(environment.getRequiredProperty("exists"), is("xyz"));
-
- try {
- environment.getRequiredProperty("bogus");
- fail("expected IllegalArgumentException");
- } catch (IllegalArgumentException ex) {
- // expected
- }
- }
-
- @Test
- public void getRequiredProperty_withStringArrayConversion() {
- testProperties.put("exists", "abc,123");
- assertThat(environment.getRequiredProperty("exists", String[].class), equalTo(new String[] { "abc", "123" }));
-
- try {
- environment.getRequiredProperty("bogus", String[].class);
- fail("expected IllegalArgumentException");
- } catch (IllegalArgumentException ex) {
- // expected
- }
- }
-
- @Test @SuppressWarnings({ "rawtypes", "serial", "unchecked" })
- public void asProperties() {
- ConfigurableEnvironment env = new AbstractEnvironment() { };
- assertThat(env.asProperties(), notNullValue());
-
- env.addPropertySource("lowestPrecedence", new HashMap() {{ put("common", "lowCommon"); put("lowKey", "lowVal"); }});
- env.addPropertySource("middlePrecedence", new HashMap() {{ put("common", "midCommon"); put("midKey", "midVal"); }});
- env.addPropertySource("highestPrecedence", new HashMap() {{ put("common", "highCommon"); put("highKey", "highVal"); }});
-
- Properties props = env.asProperties();
- assertThat(props.getProperty("common"), is("highCommon"));
- assertThat(props.getProperty("lowKey"), is("lowVal"));
- assertThat(props.getProperty("midKey"), is("midVal"));
- assertThat(props.getProperty("highKey"), is("highVal"));
- assertThat(props.size(), is(4));
- }
-
- @Test
- public void activeProfiles() {
- assertThat(environment.getActiveProfiles(), isEmpty());
- environment.setActiveProfiles("local", "embedded");
- Set activeProfiles = environment.getActiveProfiles();
- assertThat(activeProfiles, hasItems("local", "embedded"));
- assertThat(activeProfiles.size(), is(2));
- try {
- environment.getActiveProfiles().add("bogus");
- fail("activeProfiles should be unmodifiable");
- } catch (UnsupportedOperationException ex) {
- // expected
- }
- environment.setActiveProfiles("foo");
- assertThat(activeProfiles, hasItem("foo"));
- assertThat(environment.getActiveProfiles().size(), is(1));
- }
-
- @Test
- public void systemPropertiesEmpty() {
- assertThat(environment.getActiveProfiles(), isEmpty());
-
- System.setProperty(ACTIVE_PROFILES_PROPERTY_NAME, "");
- assertThat(environment.getActiveProfiles(), isEmpty());
-
- System.getProperties().remove(ACTIVE_PROFILES_PROPERTY_NAME);
- }
-
- @Test
- public void systemPropertiesResoloutionOfProfiles() {
- assertThat(environment.getActiveProfiles(), isEmpty());
-
- System.setProperty(ACTIVE_PROFILES_PROPERTY_NAME, "foo");
- assertThat(environment.getActiveProfiles(), hasItem("foo"));
-
- // clean up
- System.getProperties().remove(ACTIVE_PROFILES_PROPERTY_NAME);
- }
-
- @Test
- public void systemPropertiesResoloutionOfMultipleProfiles() {
- assertThat(environment.getActiveProfiles(), isEmpty());
- System.setProperty(ACTIVE_PROFILES_PROPERTY_NAME, "foo,bar");
- assertThat(environment.getActiveProfiles(), hasItems("foo", "bar"));
- System.getProperties().remove(ACTIVE_PROFILES_PROPERTY_NAME);
- }
-
- @Test
- public void systemPropertiesResolutionOfMulitpleProfiles_withWhitespace() {
- assertThat(environment.getActiveProfiles(), isEmpty());
- System.setProperty(ACTIVE_PROFILES_PROPERTY_NAME, " bar , baz "); // notice whitespace
- assertThat(environment.getActiveProfiles(), hasItems("bar", "baz"));
- System.getProperties().remove(ACTIVE_PROFILES_PROPERTY_NAME);
- }
-
- @Test
- public void environmentResolutionOfDefaultSpringProfileProperty_noneSet() {
- assertThat(environment.getDefaultProfiles(), isEmpty());
- }
-
- @Test
- public void environmentResolutionOfDefaultSpringProfileProperty_isSet() {
- testProperties.setProperty(DEFAULT_PROFILES_PROPERTY_NAME, "custom-default");
- assertTrue(environment.getDefaultProfiles().contains("custom-default"));
- }
-
- @Test
- public void systemPropertiesAccess() {
- System.setProperty(ALLOWED_PROPERTY_NAME, ALLOWED_PROPERTY_VALUE);
- System.setProperty(DISALLOWED_PROPERTY_NAME, DISALLOWED_PROPERTY_VALUE);
- System.getProperties().put(STRING_PROPERTY_NAME, NON_STRING_PROPERTY_VALUE);
- System.getProperties().put(NON_STRING_PROPERTY_NAME, STRING_PROPERTY_VALUE);
-
- {
- Map, ?> systemProperties = environment.getSystemProperties();
- assertThat(systemProperties, notNullValue());
- assertSame(systemProperties, System.getProperties());
- assertThat(systemProperties.get(ALLOWED_PROPERTY_NAME), equalTo((Object)ALLOWED_PROPERTY_VALUE));
- assertThat(systemProperties.get(DISALLOWED_PROPERTY_NAME), equalTo((Object)DISALLOWED_PROPERTY_VALUE));
-
- // non-string keys and values work fine... until the security manager is introduced below
- assertThat(systemProperties.get(STRING_PROPERTY_NAME), equalTo(NON_STRING_PROPERTY_VALUE));
- assertThat(systemProperties.get(NON_STRING_PROPERTY_NAME), equalTo((Object)STRING_PROPERTY_VALUE));
- }
-
- SecurityManager oldSecurityManager = System.getSecurityManager();
- SecurityManager securityManager = new SecurityManager() {
- @Override
- public void checkPropertiesAccess() {
- // see http://download.oracle.com/javase/1.5.0/docs/api/java/lang/System.html#getProperties()
- throw new AccessControlException("Accessing the system properties is disallowed");
- }
- @Override
- public void checkPropertyAccess(String key) {
- // see http://download.oracle.com/javase/1.5.0/docs/api/java/lang/System.html#getProperty(java.lang.String)
- if (DISALLOWED_PROPERTY_NAME.equals(key)) {
- throw new AccessControlException(
- format("Accessing the system property [%s] is disallowed", DISALLOWED_PROPERTY_NAME));
- }
- }
- @Override
- public void checkPermission(Permission perm) {
- // allow everything else
- }
- };
- System.setSecurityManager(securityManager);
-
- {
- Map, ?> systemProperties = environment.getSystemProperties();
- assertThat(systemProperties, notNullValue());
- assertThat(systemProperties, instanceOf(ReadOnlySystemAttributesMap.class));
- assertThat((String)systemProperties.get(ALLOWED_PROPERTY_NAME), equalTo(ALLOWED_PROPERTY_VALUE));
- assertThat(systemProperties.get(DISALLOWED_PROPERTY_NAME), equalTo(null));
-
- // nothing we can do here in terms of warning the user that there was
- // actually a (non-string) value available. By this point, we only
- // have access to calling System.getProperty(), which itself returns null
- // if the value is non-string. So we're stuck with returning a potentially
- // misleading null.
- assertThat(systemProperties.get(STRING_PROPERTY_NAME), nullValue());
-
- // in the case of a non-string *key*, however, we can do better. Alert
- // the user that under these very special conditions (non-object key +
- // SecurityManager that disallows access to system properties), they
- // cannot do what they're attempting.
- try {
- systemProperties.get(NON_STRING_PROPERTY_NAME);
- fail("Expected IllegalStateException when searching with non-string key against ReadOnlySystemAttributesMap");
- } catch (IllegalStateException ex) {
- // expected
- }
- }
-
- System.setSecurityManager(oldSecurityManager);
- System.clearProperty(ALLOWED_PROPERTY_NAME);
- System.clearProperty(DISALLOWED_PROPERTY_NAME);
- System.getProperties().remove(STRING_PROPERTY_NAME);
- System.getProperties().remove(NON_STRING_PROPERTY_NAME);
- }
-
- @Test
- public void systemEnvironmentAccess() throws Exception {
- getModifiableSystemEnvironment().put(ALLOWED_PROPERTY_NAME, ALLOWED_PROPERTY_VALUE);
- getModifiableSystemEnvironment().put(DISALLOWED_PROPERTY_NAME, DISALLOWED_PROPERTY_VALUE);
-
- {
- Map systemEnvironment = environment.getSystemEnvironment();
- assertThat(systemEnvironment, notNullValue());
- assertSame(systemEnvironment, System.getenv());
- }
-
- SecurityManager oldSecurityManager = System.getSecurityManager();
- SecurityManager securityManager = new SecurityManager() {
- @Override
- public void checkPermission(Permission perm) {
- //see http://download.oracle.com/javase/1.5.0/docs/api/java/lang/System.html#getenv()
- if ("getenv.*".equals(perm.getName())) {
- throw new AccessControlException("Accessing the system environment is disallowed");
- }
- //see http://download.oracle.com/javase/1.5.0/docs/api/java/lang/System.html#getenv(java.lang.String)
- if (("getenv."+DISALLOWED_PROPERTY_NAME).equals(perm.getName())) {
- throw new AccessControlException(
- format("Accessing the system environment variable [%s] is disallowed", DISALLOWED_PROPERTY_NAME));
- }
- }
- };
- System.setSecurityManager(securityManager);
-
- {
- Map systemEnvironment = environment.getSystemEnvironment();
- assertThat(systemEnvironment, notNullValue());
- assertThat(systemEnvironment, instanceOf(ReadOnlySystemAttributesMap.class));
- assertThat(systemEnvironment.get(ALLOWED_PROPERTY_NAME), equalTo(ALLOWED_PROPERTY_VALUE));
- assertThat(systemEnvironment.get(DISALLOWED_PROPERTY_NAME), nullValue());
- }
-
- System.setSecurityManager(oldSecurityManager);
- getModifiableSystemEnvironment().remove(ALLOWED_PROPERTY_NAME);
- getModifiableSystemEnvironment().remove(DISALLOWED_PROPERTY_NAME);
- }
-
- @Test
- public void resolvePlaceholders() {
- AbstractEnvironment env = new AbstractEnvironment() { };
- Properties testProperties = new Properties();
- testProperties.setProperty("foo", "bar");
- env.addPropertySource("testProperties", testProperties);
- String resolved = env.resolvePlaceholders("pre-${foo}-${unresolvable}-post");
- assertThat(resolved, is("pre-bar-${unresolvable}-post"));
- }
-
- @Test
- public void resolveRequiredPlaceholders() {
- AbstractEnvironment env = new AbstractEnvironment() { };
- Properties testProperties = new Properties();
- testProperties.setProperty("foo", "bar");
- env.addPropertySource("testProperties", testProperties);
- try {
- env.resolveRequiredPlaceholders("pre-${foo}-${unresolvable}-post");
- fail("expected exception");
- } catch (IllegalArgumentException ex) {
- assertThat(ex.getMessage(), is("Could not resolve placeholder 'unresolvable'"));
- }
- }
-
- public static class CollectionMatchers {
- public static Matcher> isEmpty() {
-
- return new TypeSafeMatcher>() {
-
- @Override
- public boolean matchesSafely(Collection> collection) {
- return collection.isEmpty();
- }
-
- public void describeTo(Description desc) {
- desc.appendText("an empty collection");
- }
- };
- }
- }
-
- // TODO SPR-7508: duplicated from EnvironmentPropertyResolutionSearchTests
- @SuppressWarnings("unchecked")
- private static Map getModifiableSystemEnvironment() throws Exception {
- Class>[] classes = Collections.class.getDeclaredClasses();
- Map systemEnv = System.getenv();
- for (Class> cl : classes) {
- if ("java.util.Collections$UnmodifiableMap".equals(cl.getName())) {
- Field field = cl.getDeclaredField("m");
- field.setAccessible(true);
- Object obj = field.get(systemEnv);
- return (Map) obj;
- }
- }
- throw new IllegalStateException();
+ public void propertySourceOrder() {
+ ConfigurableEnvironment env = new DefaultEnvironment();
+ List> sources = env.getPropertySources().asList();
+ assertThat(sources.size(), is(2));
+ assertThat(sources.get(0).getName(), equalTo(DefaultEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME));
+ assertThat(sources.get(1).getName(), equalTo(DefaultEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME));
}
}
diff --git a/org.springframework.core/src/test/java/org/springframework/core/env/EnvironmentPropertyResolutionLateBindingTests.java b/org.springframework.core/src/test/java/org/springframework/core/env/EnvironmentPropertyResolutionLateBindingTests.java
deleted file mode 100644
index f307415e26a..00000000000
--- a/org.springframework.core/src/test/java/org/springframework/core/env/EnvironmentPropertyResolutionLateBindingTests.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2002-2010 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.core.env;
-
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.junit.Assert.assertThat;
-
-import org.junit.Test;
-
-
-/**
- * Test that {@link Environment#getValue} performs late-resolution of property
- * values i.e., does not eagerly resolve and cache only at construction time.
- *
- * @see EnvironmentPropertyResolutionSearchTests
- * @author Chris Beams
- * @since 3.1
- */
-public class EnvironmentPropertyResolutionLateBindingTests {
- @Test
- public void replaceExistingKeyPostConstruction() {
- String key = "foo";
- String value1 = "bar";
- String value2 = "biz";
-
- System.setProperty(key, value1); // before construction
- DefaultEnvironment env = new DefaultEnvironment();
- assertThat(env.getProperty(key), equalTo(value1));
- System.setProperty(key, value2); // after construction and first resolution
- assertThat(env.getProperty(key), equalTo(value2));
- System.clearProperty(key); // clean up
- }
-
- @Test
- public void addNewKeyPostConstruction() {
- DefaultEnvironment env = new DefaultEnvironment();
- assertThat(env.getProperty("foo"), equalTo(null));
- System.setProperty("foo", "42");
- assertThat(env.getProperty("foo"), equalTo("42"));
- System.clearProperty("foo"); // clean up
- }
-}
diff --git a/org.springframework.core/src/test/java/org/springframework/core/env/EnvironmentPropertyResolutionSearchTests.java b/org.springframework.core/src/test/java/org/springframework/core/env/EnvironmentPropertyResolutionSearchTests.java
deleted file mode 100644
index 308da06244c..00000000000
--- a/org.springframework.core/src/test/java/org/springframework/core/env/EnvironmentPropertyResolutionSearchTests.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright 2002-2010 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.core.env;
-
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
-
-import java.lang.reflect.Field;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Properties;
-
-import org.junit.Test;
-
-/**
- * Unit tests for {@link DefaultEnvironment} proving that it (a) searches
- * standard property sources (b) in the correct order.
- *
- * @see AbstractEnvironment#getProperty(String)
- * @author Chris Beams
- * @since 3.1
- */
-public class EnvironmentPropertyResolutionSearchTests {
-
- @Test @SuppressWarnings({ "unchecked", "serial", "rawtypes" })
- public void propertySourcesHaveLIFOSearchOrder() {
- ConfigurableEnvironment env = new AbstractEnvironment() { };
- env.addPropertySource("ps1", new HashMap() {{ put("pName", "ps1Value"); }});
- assertThat(env.getProperty("pName"), equalTo("ps1Value"));
- env.addPropertySource("ps2", new HashMap() {{ put("pName", "ps2Value"); }});
- assertThat(env.getProperty("pName"), equalTo("ps2Value"));
- env.addPropertySource("ps3", new HashMap() {{ put("pName", "ps3Value"); }});
- assertThat(env.getProperty("pName"), equalTo("ps3Value"));
- }
-
- @Test
- public void resolveFromDefaultPropertySources() throws Exception {
- String key = "x";
- String localPropsValue = "local";
- String sysPropsValue = "sys";
- String envVarsValue = "env";
-
- Map systemEnvironment = getModifiableSystemEnvironment();
- Properties systemProperties = System.getProperties();
- Properties localProperties = new Properties();
-
- DefaultEnvironment env = new DefaultEnvironment();
- env.addPropertySource("localProperties", localProperties);
-
- // set all properties
- systemEnvironment.put(key, envVarsValue);
- systemProperties.setProperty(key, sysPropsValue);
- localProperties.setProperty(key, localPropsValue);
-
- // local properties should have highest resolution precedence
- assertThat(env.getProperty(key), equalTo(localPropsValue));
-
- // system properties should be next in line
- localProperties.remove(key);
- assertThat(env.getProperty(key), equalTo(sysPropsValue));
-
- // system environment variables should be final fallback
- systemProperties.remove(key);
- assertThat(env.getProperty(key), equalTo(envVarsValue));
-
- // with no propertysource containing the key in question, should return null
- systemEnvironment.remove(key);
- assertThat(env.getProperty(key), equalTo(null));
- }
-
- @SuppressWarnings("unchecked")
- private static Map getModifiableSystemEnvironment() throws Exception {
- Class>[] classes = Collections.class.getDeclaredClasses();
- Map env = System.getenv();
- for (Class> cl : classes) {
- if ("java.util.Collections$UnmodifiableMap".equals(cl.getName())) {
- Field field = cl.getDeclaredField("m");
- field.setAccessible(true);
- Object obj = field.get(env);
- return (Map) obj;
- }
- }
- throw new IllegalStateException();
- }
-
-}
diff --git a/org.springframework.core/src/test/java/org/springframework/core/env/EnvironmentTests.java b/org.springframework.core/src/test/java/org/springframework/core/env/EnvironmentTests.java
new file mode 100644
index 00000000000..da6149040a1
--- /dev/null
+++ b/org.springframework.core/src/test/java/org/springframework/core/env/EnvironmentTests.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright 2002-2010 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.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 java.lang.reflect.Field;
+import java.security.AccessControlException;
+import java.security.Permission;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
+
+import org.junit.Test;
+import org.springframework.mock.env.MockPropertySource;
+
+
+/**
+ * Unit tests for {@link DefaultEnvironment}.
+ *
+ * @author Chris Beams
+ */
+public class EnvironmentTests {
+
+ private static final String ALLOWED_PROPERTY_NAME = "theanswer";
+ private static final String ALLOWED_PROPERTY_VALUE = "42";
+
+ private static final String DISALLOWED_PROPERTY_NAME = "verboten";
+ private static final String DISALLOWED_PROPERTY_VALUE = "secret";
+
+ private static final String STRING_PROPERTY_NAME = "stringPropName";
+ private static final String STRING_PROPERTY_VALUE = "stringPropValue";
+ private static final Object NON_STRING_PROPERTY_NAME = new Object();
+ private static final Object NON_STRING_PROPERTY_VALUE = new Object();
+
+ private ConfigurableEnvironment environment = new DefaultEnvironment();
+
+ @Test
+ public void activeProfiles() {
+ assertThat(environment.getActiveProfiles().length, is(0));
+ environment.setActiveProfiles("local", "embedded");
+ String[] activeProfiles = environment.getActiveProfiles();
+ assertThat(Arrays.asList(activeProfiles), hasItems("local", "embedded"));
+ assertThat(activeProfiles.length, is(2));
+ }
+
+ @Test
+ public void getActiveProfiles_systemPropertiesEmpty() {
+ assertThat(environment.getActiveProfiles().length, is(0));
+ System.setProperty(ACTIVE_PROFILES_PROPERTY_NAME, "");
+ assertThat(environment.getActiveProfiles().length, is(0));
+ System.getProperties().remove(ACTIVE_PROFILES_PROPERTY_NAME);
+ }
+
+ @Test
+ public void getActiveProfiles_fromSystemProperties() {
+ assertThat(environment.getActiveProfiles().length, is(0));
+ System.setProperty(ACTIVE_PROFILES_PROPERTY_NAME, "foo");
+ assertThat(Arrays.asList(environment.getActiveProfiles()), hasItem("foo"));
+ System.getProperties().remove(ACTIVE_PROFILES_PROPERTY_NAME);
+ }
+
+ @Test
+ public void getActiveProfiles_fromSystemProperties_withMultipleProfiles() {
+ assertThat(environment.getActiveProfiles().length, is(0));
+ System.setProperty(ACTIVE_PROFILES_PROPERTY_NAME, "foo,bar");
+ assertThat(Arrays.asList(environment.getActiveProfiles()), hasItems("foo", "bar"));
+ System.getProperties().remove(ACTIVE_PROFILES_PROPERTY_NAME);
+ }
+
+ @Test
+ public void getActiveProfiles_fromSystemProperties_withMulitpleProfiles_withWhitespace() {
+ assertThat(environment.getActiveProfiles().length, is(0));
+ System.setProperty(ACTIVE_PROFILES_PROPERTY_NAME, " bar , baz "); // notice whitespace
+ assertThat(Arrays.asList(environment.getActiveProfiles()), hasItems("bar", "baz"));
+ System.getProperties().remove(ACTIVE_PROFILES_PROPERTY_NAME);
+ }
+
+ @Test
+ public void getDefaultProfiles() {
+ assertThat(environment.getDefaultProfiles().length, is(0));
+ environment.getPropertySources().addFirst(new MockPropertySource().withProperty(DEFAULT_PROFILES_PROPERTY_NAME, "pd1"));
+ assertThat(environment.getDefaultProfiles().length, is(1));
+ assertThat(Arrays.asList(environment.getDefaultProfiles()), hasItem("pd1"));
+ }
+
+ @Test
+ public void setDefaultProfiles() {
+ environment.setDefaultProfiles();
+ assertThat(environment.getDefaultProfiles().length, is(0));
+ environment.setDefaultProfiles("pd1");
+ assertThat(Arrays.asList(environment.getDefaultProfiles()), hasItem("pd1"));
+ environment.setDefaultProfiles("pd2", "pd3");
+ assertThat(Arrays.asList(environment.getDefaultProfiles()), not(hasItem("pd1")));
+ assertThat(Arrays.asList(environment.getDefaultProfiles()), hasItems("pd2", "pd3"));
+ }
+
+ @Test(expected=IllegalArgumentException.class)
+ public void acceptsProfiles_mustSpecifyAtLeastOne() {
+ environment.acceptsProfiles();
+ }
+
+ @Test
+ public void acceptsProfiles_activeProfileSetProgrammatically() {
+ assertThat(environment.acceptsProfiles("p1", "p2"), is(false));
+ environment.setActiveProfiles("p1");
+ assertThat(environment.acceptsProfiles("p1", "p2"), is(true));
+ environment.setActiveProfiles("p2");
+ assertThat(environment.acceptsProfiles("p1", "p2"), is(true));
+ environment.setActiveProfiles("p1", "p2");
+ assertThat(environment.acceptsProfiles("p1", "p2"), is(true));
+ }
+
+ @Test
+ public void acceptsProfiles_activeProfileSetViaProperty() {
+ assertThat(environment.acceptsProfiles("p1"), is(false));
+ environment.getPropertySources().addFirst(new MockPropertySource().withProperty(ACTIVE_PROFILES_PROPERTY_NAME, "p1"));
+ assertThat(environment.acceptsProfiles("p1"), is(true));
+ }
+
+ @Test
+ public void acceptsProfiles_defaultProfile() {
+ assertThat(environment.acceptsProfiles("pd"), is(false));
+ environment.setDefaultProfiles("pd");
+ assertThat(environment.acceptsProfiles("pd"), is(true));
+ environment.setActiveProfiles("p1");
+ assertThat(environment.acceptsProfiles("pd"), is(false));
+ assertThat(environment.acceptsProfiles("p1"), is(true));
+ }
+
+ @Test
+ public void getSystemProperties_withAndWithoutSecurityManager() {
+ System.setProperty(ALLOWED_PROPERTY_NAME, ALLOWED_PROPERTY_VALUE);
+ System.setProperty(DISALLOWED_PROPERTY_NAME, DISALLOWED_PROPERTY_VALUE);
+ System.getProperties().put(STRING_PROPERTY_NAME, NON_STRING_PROPERTY_VALUE);
+ System.getProperties().put(NON_STRING_PROPERTY_NAME, STRING_PROPERTY_VALUE);
+
+ {
+ Map, ?> systemProperties = environment.getSystemProperties();
+ assertThat(systemProperties, notNullValue());
+ assertSame(systemProperties, System.getProperties());
+ assertThat(systemProperties.get(ALLOWED_PROPERTY_NAME), equalTo((Object)ALLOWED_PROPERTY_VALUE));
+ assertThat(systemProperties.get(DISALLOWED_PROPERTY_NAME), equalTo((Object)DISALLOWED_PROPERTY_VALUE));
+
+ // non-string keys and values work fine... until the security manager is introduced below
+ assertThat(systemProperties.get(STRING_PROPERTY_NAME), equalTo(NON_STRING_PROPERTY_VALUE));
+ assertThat(systemProperties.get(NON_STRING_PROPERTY_NAME), equalTo((Object)STRING_PROPERTY_VALUE));
+ }
+
+ SecurityManager oldSecurityManager = System.getSecurityManager();
+ SecurityManager securityManager = new SecurityManager() {
+ @Override
+ public void checkPropertiesAccess() {
+ // see http://download.oracle.com/javase/1.5.0/docs/api/java/lang/System.html#getProperties()
+ throw new AccessControlException("Accessing the system properties is disallowed");
+ }
+ @Override
+ public void checkPropertyAccess(String key) {
+ // see http://download.oracle.com/javase/1.5.0/docs/api/java/lang/System.html#getProperty(java.lang.String)
+ if (DISALLOWED_PROPERTY_NAME.equals(key)) {
+ throw new AccessControlException(
+ format("Accessing the system property [%s] is disallowed", DISALLOWED_PROPERTY_NAME));
+ }
+ }
+ @Override
+ public void checkPermission(Permission perm) {
+ // allow everything else
+ }
+ };
+ System.setSecurityManager(securityManager);
+
+ {
+ Map, ?> systemProperties = environment.getSystemProperties();
+ assertThat(systemProperties, notNullValue());
+ assertThat(systemProperties, instanceOf(ReadOnlySystemAttributesMap.class));
+ assertThat((String)systemProperties.get(ALLOWED_PROPERTY_NAME), equalTo(ALLOWED_PROPERTY_VALUE));
+ assertThat(systemProperties.get(DISALLOWED_PROPERTY_NAME), equalTo(null));
+
+ // nothing we can do here in terms of warning the user that there was
+ // actually a (non-string) value available. By this point, we only
+ // have access to calling System.getProperty(), which itself returns null
+ // if the value is non-string. So we're stuck with returning a potentially
+ // misleading null.
+ assertThat(systemProperties.get(STRING_PROPERTY_NAME), nullValue());
+
+ // in the case of a non-string *key*, however, we can do better. Alert
+ // the user that under these very special conditions (non-object key +
+ // SecurityManager that disallows access to system properties), they
+ // cannot do what they're attempting.
+ try {
+ systemProperties.get(NON_STRING_PROPERTY_NAME);
+ fail("Expected IllegalArgumentException when searching with non-string key against ReadOnlySystemAttributesMap");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+
+ System.setSecurityManager(oldSecurityManager);
+ System.clearProperty(ALLOWED_PROPERTY_NAME);
+ System.clearProperty(DISALLOWED_PROPERTY_NAME);
+ System.getProperties().remove(STRING_PROPERTY_NAME);
+ System.getProperties().remove(NON_STRING_PROPERTY_NAME);
+ }
+
+ @Test
+ public void getSystemEnvironment_withAndWithoutSecurityManager() throws Exception {
+ getModifiableSystemEnvironment().put(ALLOWED_PROPERTY_NAME, ALLOWED_PROPERTY_VALUE);
+ getModifiableSystemEnvironment().put(DISALLOWED_PROPERTY_NAME, DISALLOWED_PROPERTY_VALUE);
+
+ {
+ Map systemEnvironment = environment.getSystemEnvironment();
+ assertThat(systemEnvironment, notNullValue());
+ assertSame(systemEnvironment, System.getenv());
+ }
+
+ SecurityManager oldSecurityManager = System.getSecurityManager();
+ SecurityManager securityManager = new SecurityManager() {
+ @Override
+ public void checkPermission(Permission perm) {
+ //see http://download.oracle.com/javase/1.5.0/docs/api/java/lang/System.html#getenv()
+ if ("getenv.*".equals(perm.getName())) {
+ throw new AccessControlException("Accessing the system environment is disallowed");
+ }
+ //see http://download.oracle.com/javase/1.5.0/docs/api/java/lang/System.html#getenv(java.lang.String)
+ if (("getenv."+DISALLOWED_PROPERTY_NAME).equals(perm.getName())) {
+ throw new AccessControlException(
+ format("Accessing the system environment variable [%s] is disallowed", DISALLOWED_PROPERTY_NAME));
+ }
+ }
+ };
+ System.setSecurityManager(securityManager);
+
+ {
+ Map systemEnvironment = environment.getSystemEnvironment();
+ assertThat(systemEnvironment, notNullValue());
+ assertThat(systemEnvironment, instanceOf(ReadOnlySystemAttributesMap.class));
+ assertThat(systemEnvironment.get(ALLOWED_PROPERTY_NAME), equalTo(ALLOWED_PROPERTY_VALUE));
+ assertThat(systemEnvironment.get(DISALLOWED_PROPERTY_NAME), nullValue());
+ }
+
+ System.setSecurityManager(oldSecurityManager);
+ getModifiableSystemEnvironment().remove(ALLOWED_PROPERTY_NAME);
+ getModifiableSystemEnvironment().remove(DISALLOWED_PROPERTY_NAME);
+ }
+
+ // TODO SPR-7508: duplicated from EnvironmentPropertyResolutionSearchTests
+ @SuppressWarnings("unchecked")
+ private static Map getModifiableSystemEnvironment() throws Exception {
+ Class>[] classes = Collections.class.getDeclaredClasses();
+ Map systemEnv = System.getenv();
+ for (Class> cl : classes) {
+ if ("java.util.Collections$UnmodifiableMap".equals(cl.getName())) {
+ Field field = cl.getDeclaredField("m");
+ field.setAccessible(true);
+ Object obj = field.get(systemEnv);
+ return (Map) obj;
+ }
+ }
+ throw new IllegalStateException();
+ }
+}
diff --git a/org.springframework.core/src/test/java/org/springframework/core/env/PropertyResolverTests.java b/org.springframework.core/src/test/java/org/springframework/core/env/PropertyResolverTests.java
new file mode 100644
index 00000000000..466ef83ffba
--- /dev/null
+++ b/org.springframework.core/src/test/java/org/springframework/core/env/PropertyResolverTests.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright 2002-2010 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.core.env;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.mock.env.MockPropertySource;
+
+
+/**
+ * Unit tests for {@link PropertyResolver}.
+ *
+ * @author Chris Beams
+ * @since 3.1
+ * @see PropertySourcesPropertyResolver
+ */
+public class PropertyResolverTests {
+ private Properties testProperties;
+ private MutablePropertySources propertySources;
+ private ConfigurablePropertyResolver propertyResolver;
+
+ @Before
+ public void setUp() {
+ propertySources = new MutablePropertySources();
+ propertyResolver = new PropertySourcesPropertyResolver(propertySources);
+ testProperties = new Properties();
+ propertySources.addFirst(new PropertiesPropertySource("testProperties", testProperties));
+ }
+
+ @Test
+ public void containsProperty() {
+ assertThat(propertyResolver.containsProperty("foo"), is(false));
+ testProperties.put("foo", "bar");
+ assertThat(propertyResolver.containsProperty("foo"), is(true));
+ }
+
+ @Test
+ public void getProperty() {
+ assertThat(propertyResolver.getProperty("foo"), nullValue());
+ testProperties.put("foo", "bar");
+ assertThat(propertyResolver.getProperty("foo"), is("bar"));
+ }
+
+ @Test
+ public void getProperty_propertySourceSearchOrderIsFIFO() {
+ MutablePropertySources sources = new MutablePropertySources();
+ PropertyResolver resolver = new PropertySourcesPropertyResolver(sources);
+ sources.addFirst(new MockPropertySource("ps1").withProperty("pName", "ps1Value"));
+ assertThat(resolver.getProperty("pName"), equalTo("ps1Value"));
+ sources.addFirst(new MockPropertySource("ps2").withProperty("pName", "ps2Value"));
+ assertThat(resolver.getProperty("pName"), equalTo("ps2Value"));
+ sources.addFirst(new MockPropertySource("ps3").withProperty("pName", "ps3Value"));
+ assertThat(resolver.getProperty("pName"), equalTo("ps3Value"));
+ }
+
+ @Test
+ public void getProperty_withExplicitNullValue() {
+ // java.util.Properties does not allow null values (because Hashtable does not)
+ Map nullableProperties = new HashMap();
+ propertySources.addLast(new MapPropertySource("nullableProperties", nullableProperties));
+ nullableProperties.put("foo", null);
+ assertThat(propertyResolver.getProperty("foo"), nullValue());
+ }
+
+ @Test
+ public void getProperty_withStringArrayConversion() {
+ testProperties.put("foo", "bar,baz");
+ assertThat(propertyResolver.getProperty("foo", String[].class), equalTo(new String[] { "bar", "baz" }));
+ }
+
+
+ @Test
+ public void getProperty_withNonConvertibleTargetType() {
+ testProperties.put("foo", "bar");
+
+ class TestType { }
+
+ try {
+ propertyResolver.getProperty("foo", TestType.class);
+ fail("Expected IllegalArgumentException due to non-convertible types");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+
+ @Test
+ public void getProperty_doesNotCache_replaceExistingKeyPostConstruction() {
+ String key = "foo";
+ String value1 = "bar";
+ String value2 = "biz";
+
+ HashMap map = new HashMap();
+ map.put(key, value1); // before construction
+ MutablePropertySources propertySources = new MutablePropertySources();
+ propertySources.addFirst(new MapPropertySource("testProperties", map));
+ PropertyResolver propertyResolver = new PropertySourcesPropertyResolver(propertySources);
+ assertThat(propertyResolver.getProperty(key), equalTo(value1));
+ map.put(key, value2); // after construction and first resolution
+ assertThat(propertyResolver.getProperty(key), equalTo(value2));
+ }
+
+ @Test
+ public void getProperty_doesNotCache_addNewKeyPostConstruction() {
+ HashMap map = new HashMap();
+ MutablePropertySources propertySources = new MutablePropertySources();
+ propertySources.addFirst(new MapPropertySource("testProperties", map));
+ PropertyResolver propertyResolver = new PropertySourcesPropertyResolver(propertySources);
+ assertThat(propertyResolver.getProperty("foo"), equalTo(null));
+ map.put("foo", "42");
+ assertThat(propertyResolver.getProperty("foo"), equalTo("42"));
+ }
+
+ @Test
+ public void getPropertySources_replacePropertySource() {
+ propertySources = new MutablePropertySources();
+ propertyResolver = new PropertySourcesPropertyResolver(propertySources);
+ propertySources.addLast(new MockPropertySource("local").withProperty("foo", "localValue"));
+ propertySources.addLast(new MockPropertySource("system").withProperty("foo", "systemValue"));
+
+ // 'local' was added first so has precedence
+ assertThat(propertyResolver.getProperty("foo"), equalTo("localValue"));
+
+ // replace 'local' with new property source
+ propertySources.replace("local", new MockPropertySource("new").withProperty("foo", "newValue"));
+
+ // 'system' now has precedence
+ assertThat(propertyResolver.getProperty("foo"), equalTo("newValue"));
+
+ assertThat(propertySources.size(), is(2));
+ }
+
+ @Test
+ public void getRequiredProperty() {
+ testProperties.put("exists", "xyz");
+ assertThat(propertyResolver.getRequiredProperty("exists"), is("xyz"));
+
+ try {
+ propertyResolver.getRequiredProperty("bogus");
+ fail("expected IllegalStateException");
+ } catch (IllegalStateException ex) {
+ // expected
+ }
+ }
+
+ @Test
+ public void getRequiredProperty_withStringArrayConversion() {
+ testProperties.put("exists", "abc,123");
+ assertThat(propertyResolver.getRequiredProperty("exists", String[].class), equalTo(new String[] { "abc", "123" }));
+
+ try {
+ propertyResolver.getRequiredProperty("bogus", String[].class);
+ fail("expected IllegalStateException");
+ } catch (IllegalStateException ex) {
+ // expected
+ }
+ }
+
+ @Test
+ public void asProperties() {
+ propertySources = new MutablePropertySources();
+ propertyResolver = new PropertySourcesPropertyResolver(propertySources);
+ assertThat(propertyResolver.asProperties(), notNullValue());
+
+ propertySources.addLast(new MockPropertySource("highestPrecedence").withProperty("common", "highCommon").withProperty("highKey", "highVal"));
+ propertySources.addLast(new MockPropertySource("middlePrecedence").withProperty("common", "midCommon").withProperty("midKey", "midVal"));
+ propertySources.addLast(new MockPropertySource("lowestPrecedence").withProperty("common", "lowCommon").withProperty("lowKey", "lowVal"));
+
+ Properties props = propertyResolver.asProperties();
+ assertThat(props.getProperty("common"), is("highCommon"));
+ assertThat(props.getProperty("lowKey"), is("lowVal"));
+ assertThat(props.getProperty("midKey"), is("midVal"));
+ assertThat(props.getProperty("highKey"), is("highVal"));
+ assertThat(props.size(), is(4));
+ }
+
+ @Test
+ public void asProperties_withMixedPropertySourceTypes() {
+ class Foo { }
+ class FooPropertySource extends PropertySource {
+ public FooPropertySource() { super("fooProperties", new Foo()); }
+ public String[] getPropertyNames() { return new String[] {"pName"}; }
+ public String getProperty(String key) { return "fooValue"; }
+ }
+ propertySources = new MutablePropertySources();
+ propertyResolver = new PropertySourcesPropertyResolver(propertySources);
+ assertThat(propertyResolver.asProperties(), notNullValue());
+
+ propertySources.addLast(new MockPropertySource());
+ propertySources.addLast(new FooPropertySource());
+
+ Properties props = propertyResolver.asProperties();
+ assertThat(props.getProperty("pName"), is("fooValue"));
+ assertThat(props.size(), is(1));
+ }
+
+
+ @Test
+ public void resolvePlaceholders() {
+ MutablePropertySources propertySources = new MutablePropertySources();
+ propertySources.addFirst(new MockPropertySource().withProperty("key", "value"));
+ PropertyResolver resolver = new PropertySourcesPropertyResolver(propertySources);
+ assertThat(resolver.resolvePlaceholders("Replace this ${key}"), equalTo("Replace this value"));
+ }
+
+ @Test
+ public void resolvePlaceholders_withUnresolvable() {
+ MutablePropertySources propertySources = new MutablePropertySources();
+ propertySources.addFirst(new MockPropertySource().withProperty("key", "value"));
+ PropertyResolver resolver = new PropertySourcesPropertyResolver(propertySources);
+ assertThat(resolver.resolvePlaceholders("Replace this ${key} plus ${unknown}"),
+ equalTo("Replace this value plus ${unknown}"));
+ }
+
+ @Test
+ public void resolvePlaceholders_withDefault() {
+ MutablePropertySources propertySources = new MutablePropertySources();
+ propertySources.addFirst(new MockPropertySource().withProperty("key", "value"));
+ PropertyResolver resolver = new PropertySourcesPropertyResolver(propertySources);
+ assertThat(resolver.resolvePlaceholders("Replace this ${key} plus ${unknown:defaultValue}"),
+ equalTo("Replace this value plus defaultValue"));
+ }
+
+ @Test(expected=IllegalArgumentException.class)
+ public void resolvePlaceholders_withNullInput() {
+ new PropertySourcesPropertyResolver(new MutablePropertySources()).resolvePlaceholders(null);
+ }
+
+ @Test
+ public void resolveRequiredPlaceholders() {
+ MutablePropertySources propertySources = new MutablePropertySources();
+ propertySources.addFirst(new MockPropertySource().withProperty("key", "value"));
+ PropertyResolver resolver = new PropertySourcesPropertyResolver(propertySources);
+ assertThat(resolver.resolveRequiredPlaceholders("Replace this ${key}"), equalTo("Replace this value"));
+ }
+
+ @Test(expected=IllegalArgumentException.class)
+ public void resolveRequiredPlaceholders_withUnresolvable() {
+ MutablePropertySources propertySources = new MutablePropertySources();
+ propertySources.addFirst(new MockPropertySource().withProperty("key", "value"));
+ PropertyResolver resolver = new PropertySourcesPropertyResolver(propertySources);
+ resolver.resolveRequiredPlaceholders("Replace this ${key} plus ${unknown}");
+ }
+
+ @Test
+ public void resolveRequiredPlaceholders_withDefault() {
+ MutablePropertySources propertySources = new MutablePropertySources();
+ propertySources.addFirst(new MockPropertySource().withProperty("key", "value"));
+ PropertyResolver resolver = new PropertySourcesPropertyResolver(propertySources);
+ assertThat(resolver.resolveRequiredPlaceholders("Replace this ${key} plus ${unknown:defaultValue}"),
+ equalTo("Replace this value plus defaultValue"));
+ }
+
+ @Test(expected=IllegalArgumentException.class)
+ public void resolveRequiredPlaceholders_withNullInput() {
+ new PropertySourcesPropertyResolver(new MutablePropertySources()).resolveRequiredPlaceholders(null);
+ }
+}
diff --git a/org.springframework.core/src/test/java/org/springframework/core/env/PropertySourceTests.java b/org.springframework.core/src/test/java/org/springframework/core/env/PropertySourceTests.java
index 5877ad4bd9f..d2d7fd468bb 100644
--- a/org.springframework.core/src/test/java/org/springframework/core/env/PropertySourceTests.java
+++ b/org.springframework.core/src/test/java/org/springframework/core/env/PropertySourceTests.java
@@ -20,8 +20,9 @@ import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
+import java.util.ArrayList;
import java.util.HashMap;
-import java.util.LinkedList;
+import java.util.List;
import java.util.Map;
import java.util.Properties;
@@ -66,7 +67,7 @@ public class PropertySourceTests {
PropertySource> ps1 = new MapPropertySource("ps1", map1);
ps1.getSource();
- LinkedList> propertySources = new LinkedList>();
+ List> propertySources = new ArrayList>();
assertThat(propertySources.add(ps1), equalTo(true));
assertThat(propertySources.contains(ps1), is(true));
assertThat(propertySources.contains(PropertySource.named("ps1")), is(true));
diff --git a/org.springframework.core/src/test/java/org/springframework/core/env/PropertySourcesTests.java b/org.springframework.core/src/test/java/org/springframework/core/env/PropertySourcesTests.java
new file mode 100644
index 00000000000..83c8ac2a786
--- /dev/null
+++ b/org.springframework.core/src/test/java/org/springframework/core/env/PropertySourcesTests.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2002-2010 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.core.env;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+import org.springframework.mock.env.MockPropertySource;
+
+public class PropertySourcesTests {
+ @Test
+ public void test() {
+ MutablePropertySources sources = new MutablePropertySources();
+ sources.addLast(new MockPropertySource("b").withProperty("p1", "bValue"));
+ sources.addLast(new MockPropertySource("d").withProperty("p1", "dValue"));
+ sources.addLast(new MockPropertySource("f").withProperty("p1", "fValue"));
+
+ assertThat(sources.size(), equalTo(3));
+ assertThat(sources.contains("a"), is(false));
+ assertThat(sources.contains("b"), is(true));
+ assertThat(sources.contains("c"), is(false));
+ assertThat(sources.contains("d"), is(true));
+ assertThat(sources.contains("e"), is(false));
+ assertThat(sources.contains("f"), is(true));
+ assertThat(sources.contains("g"), is(false));
+
+ assertThat(sources.get("b"), not(nullValue()));
+ assertThat(sources.get("b").getProperty("p1"), equalTo("bValue"));
+ assertThat(sources.get("d"), not(nullValue()));
+ assertThat(sources.get("d").getProperty("p1"), equalTo("dValue"));
+
+ sources.addBefore("b", new MockPropertySource("a"));
+ sources.addAfter("b", new MockPropertySource("c"));
+
+ assertThat(sources.size(), equalTo(5));
+ assertThat(sources.asList().indexOf(PropertySource.named("a")), is(0));
+ assertThat(sources.asList().indexOf(PropertySource.named("b")), is(1));
+ assertThat(sources.asList().indexOf(PropertySource.named("c")), is(2));
+ assertThat(sources.asList().indexOf(PropertySource.named("d")), is(3));
+ assertThat(sources.asList().indexOf(PropertySource.named("f")), is(4));
+
+ sources.addBefore("f", new MockPropertySource("e"));
+ sources.addAfter("f", new MockPropertySource("g"));
+
+ assertThat(sources.size(), equalTo(7));
+ assertThat(sources.asList().indexOf(PropertySource.named("a")), is(0));
+ assertThat(sources.asList().indexOf(PropertySource.named("b")), is(1));
+ assertThat(sources.asList().indexOf(PropertySource.named("c")), is(2));
+ assertThat(sources.asList().indexOf(PropertySource.named("d")), is(3));
+ assertThat(sources.asList().indexOf(PropertySource.named("e")), is(4));
+ assertThat(sources.asList().indexOf(PropertySource.named("f")), is(5));
+ assertThat(sources.asList().indexOf(PropertySource.named("g")), is(6));
+
+ sources.addLast(new MockPropertySource("a"));
+ assertThat(sources.size(), equalTo(7));
+ assertThat(sources.asList().indexOf(PropertySource.named("b")), is(0));
+ assertThat(sources.asList().indexOf(PropertySource.named("c")), is(1));
+ assertThat(sources.asList().indexOf(PropertySource.named("d")), is(2));
+ assertThat(sources.asList().indexOf(PropertySource.named("e")), is(3));
+ assertThat(sources.asList().indexOf(PropertySource.named("f")), is(4));
+ assertThat(sources.asList().indexOf(PropertySource.named("g")), is(5));
+ assertThat(sources.asList().indexOf(PropertySource.named("a")), is(6));
+
+ sources.addFirst(new MockPropertySource("a"));
+ assertThat(sources.size(), equalTo(7));
+ assertThat(sources.asList().indexOf(PropertySource.named("a")), is(0));
+ assertThat(sources.asList().indexOf(PropertySource.named("b")), is(1));
+ assertThat(sources.asList().indexOf(PropertySource.named("c")), is(2));
+ assertThat(sources.asList().indexOf(PropertySource.named("d")), is(3));
+ assertThat(sources.asList().indexOf(PropertySource.named("e")), is(4));
+ assertThat(sources.asList().indexOf(PropertySource.named("f")), is(5));
+ assertThat(sources.asList().indexOf(PropertySource.named("g")), is(6));
+
+ assertEquals(sources.remove("a"), PropertySource.named("a"));
+ assertThat(sources.size(), equalTo(6));
+ assertThat(sources.contains("a"), is(false));
+
+ assertEquals(sources.remove("a"), null);
+ assertThat(sources.size(), equalTo(6));
+
+ String bogusPS = "bogus";
+ try {
+ sources.addAfter(bogusPS, new MockPropertySource("h"));
+ fail("expected non-existent PropertySource exception");
+ } catch (IllegalArgumentException ex) {
+ assertThat(ex.getMessage(),
+ equalTo(String.format(MutablePropertySources.NON_EXISTENT_PROPERTY_SOURCE_MESSAGE, bogusPS)));
+ }
+
+ sources.addFirst(new MockPropertySource("a"));
+ assertThat(sources.size(), equalTo(7));
+ assertThat(sources.asList().indexOf(PropertySource.named("a")), is(0));
+ assertThat(sources.asList().indexOf(PropertySource.named("b")), is(1));
+ assertThat(sources.asList().indexOf(PropertySource.named("c")), is(2));
+
+ sources.replace("a", new MockPropertySource("a-replaced"));
+ assertThat(sources.size(), equalTo(7));
+ assertThat(sources.asList().indexOf(PropertySource.named("a-replaced")), is(0));
+ assertThat(sources.asList().indexOf(PropertySource.named("b")), is(1));
+ assertThat(sources.asList().indexOf(PropertySource.named("c")), is(2));
+
+ sources.replace("a-replaced", new MockPropertySource("a"));
+
+ try {
+ sources.replace(bogusPS, new MockPropertySource("bogus-replaced"));
+ fail("expected non-existent PropertySource exception");
+ } catch (IllegalArgumentException ex) {
+ assertThat(ex.getMessage(),
+ equalTo(String.format(MutablePropertySources.NON_EXISTENT_PROPERTY_SOURCE_MESSAGE, bogusPS)));
+ }
+
+ try {
+ sources.addBefore("b", new MockPropertySource("b"));
+ fail("expected exception");
+ } catch (IllegalArgumentException ex) {
+ assertThat(ex.getMessage(),
+ equalTo(String.format(MutablePropertySources.ILLEGAL_RELATIVE_ADDITION_MESSAGE, "b")));
+ }
+
+ try {
+ sources.addAfter("b", new MockPropertySource("b"));
+ fail("expected exception");
+ } catch (IllegalArgumentException ex) {
+ assertThat(ex.getMessage(),
+ equalTo(String.format(MutablePropertySources.ILLEGAL_RELATIVE_ADDITION_MESSAGE, "b")));
+ }
+ }
+
+}
diff --git a/org.springframework.core/src/test/java/org/springframework/mock/env/MockPropertySource.java b/org.springframework.core/src/test/java/org/springframework/mock/env/MockPropertySource.java
new file mode 100644
index 00000000000..14f239e848e
--- /dev/null
+++ b/org.springframework.core/src/test/java/org/springframework/mock/env/MockPropertySource.java
@@ -0,0 +1,103 @@
+/*
+ * 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.mock.env;
+
+import java.util.Properties;
+
+import org.springframework.core.env.PropertiesPropertySource;
+import org.springframework.core.env.PropertySource;
+
+/**
+ * Simple {@link PropertySource} implementation for use in testing. Accepts
+ * a user-provided {@link Properties} object, or if omitted during construction,
+ * the implementation will initialize its own.
+ *
+ * The {@link #setProperty} and {@link #withProperty} methods are exposed for
+ * convenience, for example:
+ *
+ * {@code
+ * PropertySource> source = new MockPropertySource().withProperty("foo", "bar");
+ * }
+ *
+ *
+ * @author Chris Beams
+ * @since 3.1
+ * @see MockEnvironment
+ */
+public class MockPropertySource extends PropertiesPropertySource {
+
+ /**
+ * {@value} is the default name for {@link MockPropertySource} instances not
+ * otherwise given an explicit name.
+ * @see #MockPropertySource()
+ * @see #MockPropertySource(String)
+ */
+ public static final String MOCK_PROPERTIES_PROPERTY_SOURCE_NAME = "mockProperties";
+
+ /**
+ * Create a new {@code MockPropertySource} named {@value #MOCK_PROPERTIES_PROPERTY_SOURCE_NAME}
+ * that will maintain its own internal {@link Properties} instance.
+ */
+ public MockPropertySource() {
+ this(new Properties());
+ }
+
+ /**
+ * Create a new {@code MockPropertySource} with the given name that will
+ * maintain its own internal {@link Properties} instance.
+ * @param name the {@linkplain #getName() name} of the property source
+ */
+ public MockPropertySource(String name) {
+ this(name, new Properties());
+ }
+
+ /**
+ * Create a new {@code MockPropertySource} named {@value #MOCK_PROPERTIES_PROPERTY_SOURCE_NAME}
+ * and backed by the given {@link Properties} object.
+ * @param properties the properties to use
+ */
+ public MockPropertySource(Properties properties) {
+ this(MOCK_PROPERTIES_PROPERTY_SOURCE_NAME, properties);
+ }
+
+ /**
+ * Create a new {@code MockPropertySource} with with the given name and backed by the given
+ * {@link Properties} object
+ * @param name the {@linkplain #getName() name} of the property source
+ * @param properties the properties to use
+ */
+ public MockPropertySource(String name, Properties properties) {
+ super(name, properties);
+ }
+
+ /**
+ * Set the given property on the underlying {@link Properties} object.
+ */
+ public void setProperty(String key, String value) {
+ this.source.put(key, value);
+ }
+
+ /**
+ * Convenient synonym for {@link #setProperty} that returns the current instance.
+ * Useful for method chaining and fluent-style use.
+ * @return this {@link MockPropertySource} instance
+ */
+ public MockPropertySource withProperty(String key, String value) {
+ this.setProperty(key, value);
+ return this;
+ }
+}
diff --git a/org.springframework.expression/src/test/java/org/springframework/mock/env/MockPropertySource.java b/org.springframework.expression/src/test/java/org/springframework/mock/env/MockPropertySource.java
new file mode 100644
index 00000000000..14f239e848e
--- /dev/null
+++ b/org.springframework.expression/src/test/java/org/springframework/mock/env/MockPropertySource.java
@@ -0,0 +1,103 @@
+/*
+ * 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.mock.env;
+
+import java.util.Properties;
+
+import org.springframework.core.env.PropertiesPropertySource;
+import org.springframework.core.env.PropertySource;
+
+/**
+ * Simple {@link PropertySource} implementation for use in testing. Accepts
+ * a user-provided {@link Properties} object, or if omitted during construction,
+ * the implementation will initialize its own.
+ *
+ * The {@link #setProperty} and {@link #withProperty} methods are exposed for
+ * convenience, for example:
+ *
+ * {@code
+ * PropertySource> source = new MockPropertySource().withProperty("foo", "bar");
+ * }
+ *
+ *
+ * @author Chris Beams
+ * @since 3.1
+ * @see MockEnvironment
+ */
+public class MockPropertySource extends PropertiesPropertySource {
+
+ /**
+ * {@value} is the default name for {@link MockPropertySource} instances not
+ * otherwise given an explicit name.
+ * @see #MockPropertySource()
+ * @see #MockPropertySource(String)
+ */
+ public static final String MOCK_PROPERTIES_PROPERTY_SOURCE_NAME = "mockProperties";
+
+ /**
+ * Create a new {@code MockPropertySource} named {@value #MOCK_PROPERTIES_PROPERTY_SOURCE_NAME}
+ * that will maintain its own internal {@link Properties} instance.
+ */
+ public MockPropertySource() {
+ this(new Properties());
+ }
+
+ /**
+ * Create a new {@code MockPropertySource} with the given name that will
+ * maintain its own internal {@link Properties} instance.
+ * @param name the {@linkplain #getName() name} of the property source
+ */
+ public MockPropertySource(String name) {
+ this(name, new Properties());
+ }
+
+ /**
+ * Create a new {@code MockPropertySource} named {@value #MOCK_PROPERTIES_PROPERTY_SOURCE_NAME}
+ * and backed by the given {@link Properties} object.
+ * @param properties the properties to use
+ */
+ public MockPropertySource(Properties properties) {
+ this(MOCK_PROPERTIES_PROPERTY_SOURCE_NAME, properties);
+ }
+
+ /**
+ * Create a new {@code MockPropertySource} with with the given name and backed by the given
+ * {@link Properties} object
+ * @param name the {@linkplain #getName() name} of the property source
+ * @param properties the properties to use
+ */
+ public MockPropertySource(String name, Properties properties) {
+ super(name, properties);
+ }
+
+ /**
+ * Set the given property on the underlying {@link Properties} object.
+ */
+ public void setProperty(String key, String value) {
+ this.source.put(key, value);
+ }
+
+ /**
+ * Convenient synonym for {@link #setProperty} that returns the current instance.
+ * Useful for method chaining and fluent-style use.
+ * @return this {@link MockPropertySource} instance
+ */
+ public MockPropertySource withProperty(String key, String value) {
+ this.setProperty(key, value);
+ return this;
+ }
+}
diff --git a/org.springframework.integration-tests/src/test/java/org/springframework/core/env/EnvironmentIntegrationTests.java b/org.springframework.integration-tests/src/test/java/org/springframework/core/env/EnvironmentIntegrationTests.java
index 5c3332ed2ad..68504d6e188 100644
--- a/org.springframework.integration-tests/src/test/java/org/springframework/core/env/EnvironmentIntegrationTests.java
+++ b/org.springframework.integration-tests/src/test/java/org/springframework/core/env/EnvironmentIntegrationTests.java
@@ -18,9 +18,7 @@ package org.springframework.core.env;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.isIn;
import static org.hamcrest.Matchers.lessThan;
-import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.Assert.assertThat;
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.rootBeanDefinition;
@@ -37,8 +35,6 @@ import static org.springframework.core.env.EnvironmentIntegrationTests.Constants
import java.io.File;
import java.io.IOException;
-import java.util.LinkedList;
-import java.util.Properties;
import org.junit.Before;
import org.junit.Test;
@@ -64,16 +60,19 @@ import org.springframework.core.io.ClassPathResource;
import org.springframework.jca.context.ResourceAdapterApplicationContext;
import org.springframework.jca.support.SimpleBootstrapContext;
import org.springframework.jca.work.SimpleTaskWorkManager;
+import org.springframework.mock.env.MockPropertySource;
import org.springframework.mock.web.MockServletConfig;
import org.springframework.mock.web.MockServletContext;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AbstractRefreshableWebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
+import org.springframework.web.context.support.DefaultWebEnvironment;
import org.springframework.web.context.support.GenericWebApplicationContext;
import org.springframework.web.context.support.StaticWebApplicationContext;
import org.springframework.web.context.support.XmlWebApplicationContext;
import org.springframework.web.portlet.context.AbstractRefreshablePortletApplicationContext;
+import org.springframework.web.portlet.context.DefaultPortletEnvironment;
import org.springframework.web.portlet.context.StaticPortletApplicationContext;
import org.springframework.web.portlet.context.XmlPortletApplicationContext;
@@ -433,34 +432,33 @@ public class EnvironmentIntegrationTests {
ConfigurableEnvironment environment = ctx.getEnvironment();
assertThat(environment, instanceOf(DefaultWebEnvironment.class));
- LinkedList> propertySources = environment.getPropertySources();
- assertThat(PropertySource.named(DefaultWebEnvironment.SERVLET_CONTEXT_PARAMS_PROPERTY_SOURCE_NAME), isIn(propertySources));
- assertThat(PropertySource.named(DefaultWebEnvironment.SERVLET_CONFIG_PARAMS_PROPERTY_SOURCE_NAME), isIn(propertySources));
+ MutablePropertySources propertySources = environment.getPropertySources();
+ PropertyResolver propertyResolver = environment.getPropertyResolver();
+ assertThat(propertySources.contains(DefaultWebEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME), is(true));
+ assertThat(propertySources.contains(DefaultWebEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME), is(true));
// ServletConfig gets precedence
- assertThat(environment.getProperty("pCommon"), is("pCommonConfigValue"));
- assertThat(propertySources.indexOf(PropertySource.named(DefaultWebEnvironment.SERVLET_CONFIG_PARAMS_PROPERTY_SOURCE_NAME)),
- lessThan(propertySources.indexOf(PropertySource.named(DefaultWebEnvironment.SERVLET_CONTEXT_PARAMS_PROPERTY_SOURCE_NAME))));
+ assertThat(propertyResolver.getProperty("pCommon"), is("pCommonConfigValue"));
+ assertThat(propertySources.asList().indexOf(PropertySource.named(DefaultWebEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME)),
+ lessThan(propertySources.asList().indexOf(PropertySource.named(DefaultWebEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME))));
// but all params are available
- assertThat(environment.getProperty("pContext1"), is("pContext1Value"));
- assertThat(environment.getProperty("pConfig1"), is("pConfig1Value"));
+ assertThat(propertyResolver.getProperty("pContext1"), is("pContext1Value"));
+ assertThat(propertyResolver.getProperty("pConfig1"), is("pConfig1Value"));
// Servlet* PropertySources have precedence over System* PropertySources
- assertThat(propertySources.indexOf(PropertySource.named(DefaultWebEnvironment.SERVLET_CONFIG_PARAMS_PROPERTY_SOURCE_NAME)),
- lessThan(propertySources.indexOf(PropertySource.named(DefaultEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME))));
+ assertThat(propertySources.asList().indexOf(PropertySource.named(DefaultWebEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME)),
+ lessThan(propertySources.asList().indexOf(PropertySource.named(DefaultEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME))));
// Replace system properties with a mock property source for convenience
MockPropertySource mockSystemProperties = new MockPropertySource(DefaultEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME);
mockSystemProperties.setProperty("pCommon", "pCommonSysPropsValue");
mockSystemProperties.setProperty("pSysProps1", "pSysProps1Value");
- propertySources.set(
- propertySources.indexOf(PropertySource.named(DefaultEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME)),
- mockSystemProperties);
+ propertySources.replace(DefaultEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, mockSystemProperties);
- // assert that servletconfig init params resolve with higher precedence than sysprops
- assertThat(environment.getProperty("pCommon"), is("pCommonConfigValue"));
- assertThat(environment.getProperty("pSysProps1"), is("pSysProps1Value"));
+ // assert that servletconfig params resolve with higher precedence than sysprops
+ assertThat(propertyResolver.getProperty("pCommon"), is("pCommonConfigValue"));
+ assertThat(propertyResolver.getProperty("pSysProps1"), is("pSysProps1Value"));
}
@Test
@@ -475,29 +473,27 @@ public class EnvironmentIntegrationTests {
ConfigurableEnvironment environment = ctx.getEnvironment();
assertThat(environment, instanceOf(DefaultWebEnvironment.class));
- LinkedList> propertySources = environment.getPropertySources();
- assertThat(PropertySource.named(DefaultWebEnvironment.SERVLET_CONTEXT_PARAMS_PROPERTY_SOURCE_NAME), isIn(propertySources));
- assertThat(PropertySource.named(DefaultWebEnvironment.SERVLET_CONFIG_PARAMS_PROPERTY_SOURCE_NAME), not(isIn(propertySources)));
+ MutablePropertySources propertySources = environment.getPropertySources();
+ PropertyResolver propertyResolver = environment.getPropertyResolver();
+ assertThat(propertySources.contains(DefaultWebEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME), is(true));
// ServletContext params are available
- assertThat(environment.getProperty("pCommon"), is("pCommonContextValue"));
- assertThat(environment.getProperty("pContext1"), is("pContext1Value"));
+ assertThat(propertyResolver.getProperty("pCommon"), is("pCommonContextValue"));
+ assertThat(propertyResolver.getProperty("pContext1"), is("pContext1Value"));
// Servlet* PropertySources have precedence over System* PropertySources
- assertThat(propertySources.indexOf(PropertySource.named(DefaultWebEnvironment.SERVLET_CONTEXT_PARAMS_PROPERTY_SOURCE_NAME)),
- lessThan(propertySources.indexOf(PropertySource.named(DefaultEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME))));
+ assertThat(propertySources.asList().indexOf(PropertySource.named(DefaultWebEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME)),
+ lessThan(propertySources.asList().indexOf(PropertySource.named(DefaultEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME))));
// Replace system properties with a mock property source for convenience
MockPropertySource mockSystemProperties = new MockPropertySource(DefaultEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME);
mockSystemProperties.setProperty("pCommon", "pCommonSysPropsValue");
mockSystemProperties.setProperty("pSysProps1", "pSysProps1Value");
- propertySources.set(
- propertySources.indexOf(PropertySource.named(DefaultEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME)),
- mockSystemProperties);
+ propertySources.replace(DefaultEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, mockSystemProperties);
// assert that servletcontext init params resolve with higher precedence than sysprops
- assertThat(environment.getProperty("pCommon"), is("pCommonContextValue"));
- assertThat(environment.getProperty("pSysProps1"), is("pSysProps1Value"));
+ assertThat(propertyResolver.getProperty("pCommon"), is("pCommonContextValue"));
+ assertThat(propertyResolver.getProperty("pSysProps1"), is("pSysProps1Value"));
}
@Test
@@ -515,48 +511,33 @@ public class EnvironmentIntegrationTests {
ctx.refresh();
ConfigurableEnvironment environment = ctx.getEnvironment();
- LinkedList> propertySources = environment.getPropertySources();
- assertThat(PropertySource.named(DefaultWebEnvironment.SERVLET_CONTEXT_PARAMS_PROPERTY_SOURCE_NAME), isIn(propertySources));
- assertThat(PropertySource.named(DefaultWebEnvironment.SERVLET_CONFIG_PARAMS_PROPERTY_SOURCE_NAME), isIn(propertySources));
+ MutablePropertySources propertySources = environment.getPropertySources();
+ PropertyResolver propertyResolver = environment.getPropertyResolver();
+ assertThat(propertySources.contains(DefaultWebEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME), is(true));
+ assertThat(propertySources.contains(DefaultWebEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME), is(true));
// ServletConfig gets precedence
- assertThat(environment.getProperty("pCommon"), is("pCommonConfigValue"));
- assertThat(propertySources.indexOf(PropertySource.named(DefaultWebEnvironment.SERVLET_CONFIG_PARAMS_PROPERTY_SOURCE_NAME)),
- lessThan(propertySources.indexOf(PropertySource.named(DefaultWebEnvironment.SERVLET_CONTEXT_PARAMS_PROPERTY_SOURCE_NAME))));
+ assertThat(propertyResolver.getProperty("pCommon"), is("pCommonConfigValue"));
+ assertThat(propertySources.asList().indexOf(PropertySource.named(DefaultWebEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME)),
+ lessThan(propertySources.asList().indexOf(PropertySource.named(DefaultWebEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME))));
// but all params are available
- assertThat(environment.getProperty("pContext1"), is("pContext1Value"));
- assertThat(environment.getProperty("pConfig1"), is("pConfig1Value"));
+ assertThat(propertyResolver.getProperty("pContext1"), is("pContext1Value"));
+ assertThat(propertyResolver.getProperty("pConfig1"), is("pConfig1Value"));
// Servlet* PropertySources have precedence over System* PropertySources
- assertThat(propertySources.indexOf(PropertySource.named(DefaultWebEnvironment.SERVLET_CONFIG_PARAMS_PROPERTY_SOURCE_NAME)),
- lessThan(propertySources.indexOf(PropertySource.named(DefaultEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME))));
+ assertThat(propertySources.asList().indexOf(PropertySource.named(DefaultWebEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME)),
+ lessThan(propertySources.asList().indexOf(PropertySource.named(DefaultEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME))));
// Replace system properties with a mock property source for convenience
MockPropertySource mockSystemProperties = new MockPropertySource(DefaultEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME);
mockSystemProperties.setProperty("pCommon", "pCommonSysPropsValue");
mockSystemProperties.setProperty("pSysProps1", "pSysProps1Value");
- propertySources.set(
- propertySources.indexOf(PropertySource.named(DefaultEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME)),
- mockSystemProperties);
+ propertySources.replace(DefaultEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, mockSystemProperties);
- // assert that servletconfig init params resolve with higher precedence than sysprops
- assertThat(environment.getProperty("pCommon"), is("pCommonConfigValue"));
- assertThat(environment.getProperty("pSysProps1"), is("pSysProps1Value"));
- }
-
- static class MockPropertySource extends PropertiesPropertySource {
- public MockPropertySource() {
- this("MockPropertySource");
- }
-
- public MockPropertySource(String name) {
- super(name, new Properties());
- }
-
- public void setProperty(String key, String value) {
- this.source.setProperty(key, value);
- }
+ // assert that servletconfig params resolve with higher precedence than sysprops
+ assertThat(propertyResolver.getProperty("pCommon"), is("pCommonConfigValue"));
+ assertThat(propertyResolver.getProperty("pSysProps1"), is("pSysProps1Value"));
}
@Test
@@ -580,8 +561,7 @@ public class EnvironmentIntegrationTests {
public void staticPortletApplicationContext() {
StaticPortletApplicationContext ctx = new StaticPortletApplicationContext();
- // TODO SPR-7508: should be a Portlet-specific environment?
- assertHasDefaultWebEnvironment(ctx);
+ assertHasDefaultPortletEnvironment(ctx);
registerEnvironmentBeanDefinition(ctx);
@@ -638,6 +618,12 @@ public class EnvironmentIntegrationTests {
assertThat(defaultEnv, instanceOf(DefaultWebEnvironment.class));
}
+ private void assertHasDefaultPortletEnvironment(WebApplicationContext ctx) {
+ Environment defaultEnv = ctx.getEnvironment();
+ assertThat(defaultEnv, notNullValue());
+ assertThat(defaultEnv, instanceOf(DefaultPortletEnvironment.class));
+ }
+
private void assertHasEnvironment(ApplicationContext ctx, Environment expectedEnv) {
// ensure the custom environment took
Environment actualEnv = ctx.getEnvironment();
diff --git a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/support/SQLErrorCodesFactory.java b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/support/SQLErrorCodesFactory.java
index 30f7a55dc8b..4b984e7f577 100644
--- a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/support/SQLErrorCodesFactory.java
+++ b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/support/SQLErrorCodesFactory.java
@@ -103,7 +103,6 @@ public class SQLErrorCodesFactory {
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
lbf.setBeanClassLoader(getClass().getClassLoader());
XmlBeanDefinitionReader bdr = new XmlBeanDefinitionReader(lbf);
- // TODO: SPR-7508 consider setEnvironment() here
// Load default SQL error codes.
Resource resource = loadResource(SQL_ERROR_CODE_DEFAULT_PATH);
diff --git a/org.springframework.test/src/main/java/org/springframework/mock/env/MockPropertySource.java b/org.springframework.test/src/main/java/org/springframework/mock/env/MockPropertySource.java
new file mode 100644
index 00000000000..14f239e848e
--- /dev/null
+++ b/org.springframework.test/src/main/java/org/springframework/mock/env/MockPropertySource.java
@@ -0,0 +1,103 @@
+/*
+ * 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.mock.env;
+
+import java.util.Properties;
+
+import org.springframework.core.env.PropertiesPropertySource;
+import org.springframework.core.env.PropertySource;
+
+/**
+ * Simple {@link PropertySource} implementation for use in testing. Accepts
+ * a user-provided {@link Properties} object, or if omitted during construction,
+ * the implementation will initialize its own.
+ *
+ * The {@link #setProperty} and {@link #withProperty} methods are exposed for
+ * convenience, for example:
+ *
+ * {@code
+ * PropertySource> source = new MockPropertySource().withProperty("foo", "bar");
+ * }
+ *
+ *
+ * @author Chris Beams
+ * @since 3.1
+ * @see MockEnvironment
+ */
+public class MockPropertySource extends PropertiesPropertySource {
+
+ /**
+ * {@value} is the default name for {@link MockPropertySource} instances not
+ * otherwise given an explicit name.
+ * @see #MockPropertySource()
+ * @see #MockPropertySource(String)
+ */
+ public static final String MOCK_PROPERTIES_PROPERTY_SOURCE_NAME = "mockProperties";
+
+ /**
+ * Create a new {@code MockPropertySource} named {@value #MOCK_PROPERTIES_PROPERTY_SOURCE_NAME}
+ * that will maintain its own internal {@link Properties} instance.
+ */
+ public MockPropertySource() {
+ this(new Properties());
+ }
+
+ /**
+ * Create a new {@code MockPropertySource} with the given name that will
+ * maintain its own internal {@link Properties} instance.
+ * @param name the {@linkplain #getName() name} of the property source
+ */
+ public MockPropertySource(String name) {
+ this(name, new Properties());
+ }
+
+ /**
+ * Create a new {@code MockPropertySource} named {@value #MOCK_PROPERTIES_PROPERTY_SOURCE_NAME}
+ * and backed by the given {@link Properties} object.
+ * @param properties the properties to use
+ */
+ public MockPropertySource(Properties properties) {
+ this(MOCK_PROPERTIES_PROPERTY_SOURCE_NAME, properties);
+ }
+
+ /**
+ * Create a new {@code MockPropertySource} with with the given name and backed by the given
+ * {@link Properties} object
+ * @param name the {@linkplain #getName() name} of the property source
+ * @param properties the properties to use
+ */
+ public MockPropertySource(String name, Properties properties) {
+ super(name, properties);
+ }
+
+ /**
+ * Set the given property on the underlying {@link Properties} object.
+ */
+ public void setProperty(String key, String value) {
+ this.source.put(key, value);
+ }
+
+ /**
+ * Convenient synonym for {@link #setProperty} that returns the current instance.
+ * Useful for method chaining and fluent-style use.
+ * @return this {@link MockPropertySource} instance
+ */
+ public MockPropertySource withProperty(String key, String value) {
+ this.setProperty(key, value);
+ return this;
+ }
+}
diff --git a/org.springframework.transaction/src/main/java/org/springframework/jca/context/SpringContextResourceAdapter.java b/org.springframework.transaction/src/main/java/org/springframework/jca/context/SpringContextResourceAdapter.java
index e9af714e540..ae041d3934e 100644
--- a/org.springframework.transaction/src/main/java/org/springframework/jca/context/SpringContextResourceAdapter.java
+++ b/org.springframework.transaction/src/main/java/org/springframework/jca/context/SpringContextResourceAdapter.java
@@ -166,7 +166,6 @@ public class SpringContextResourceAdapter implements ResourceAdapter {
new ResourceAdapterApplicationContext(bootstrapContext);
// Set ResourceAdapter's ClassLoader as bean class loader.
applicationContext.setClassLoader(getClass().getClassLoader());
- // TODO: SPR-7508 consider setEnvironment() here
// Extract individual config locations.
String[] configLocations =
StringUtils.tokenizeToStringArray(getContextConfigLocation(), CONFIG_LOCATION_DELIMITERS);
@@ -185,7 +184,6 @@ public class SpringContextResourceAdapter implements ResourceAdapter {
* @see #setContextConfigLocation
*/
protected void loadBeanDefinitions(BeanDefinitionRegistry registry, String[] configLocations) {
- // TODO: SPR-7508 consider setEnvironment() here
new XmlBeanDefinitionReader(registry).loadBeanDefinitions(configLocations);
}
diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/GenericPortletBean.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/GenericPortletBean.java
index f73ce1b6db4..7336e282ea6 100644
--- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/GenericPortletBean.java
+++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/GenericPortletBean.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2008 the original author or authors.
+ * 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.
@@ -33,12 +33,13 @@ import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.PropertyValues;
-import org.springframework.core.env.DefaultWebEnvironment;
+import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceEditor;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.StringUtils;
+import org.springframework.web.portlet.context.DefaultPortletEnvironment;
import org.springframework.web.portlet.context.PortletContextResourceLoader;
/**
@@ -64,7 +65,7 @@ import org.springframework.web.portlet.context.PortletContextResourceLoader;
* @see #processAction
* @see FrameworkPortlet
*/
-public abstract class GenericPortletBean extends GenericPortlet {
+public abstract class GenericPortletBean extends GenericPortlet implements EnvironmentAware {
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
@@ -75,10 +76,7 @@ public abstract class GenericPortletBean extends GenericPortlet {
*/
private final Set requiredProperties = new HashSet();
- /**
- * TODO SPR-7508: think about making this overridable {@link EnvironmentAware}?
- */
- private Environment environment = new DefaultWebEnvironment();
+ private Environment environment = new DefaultPortletEnvironment();
/**
@@ -167,6 +165,15 @@ public abstract class GenericPortletBean extends GenericPortlet {
protected void initPortletBean() throws PortletException {
}
+ /**
+ * {@inheritDoc}
+ * Any environment set here overrides the {@link DefaultPortletEnvironment}
+ * provided by default.
+ */
+ public void setEnvironment(Environment environment) {
+ this.environment = environment;
+ }
+
/**
* PropertyValues implementation created from PortletConfig init parameters.
diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/context/AbstractRefreshablePortletApplicationContext.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/context/AbstractRefreshablePortletApplicationContext.java
index 39ad0bdb735..c55cddaecd3 100644
--- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/context/AbstractRefreshablePortletApplicationContext.java
+++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/context/AbstractRefreshablePortletApplicationContext.java
@@ -24,6 +24,7 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractRefreshableConfigApplicationContext;
+import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.web.context.ServletContextAware;
@@ -150,6 +151,14 @@ public abstract class AbstractRefreshablePortletApplicationContext extends Abstr
beanFactory, this.servletContext, this.portletContext, this.portletConfig);
}
+ /**
+ * Create and return a new {@link DefaultPortletEnvironment}.
+ */
+ @Override
+ protected ConfigurableEnvironment createEnvironment() {
+ return new DefaultPortletEnvironment();
+ }
+
/**
* This implementation supports file paths beneath the root of the PortletContext.
* @see PortletContextResource
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
new file mode 100644
index 00000000000..3491c92ae09
--- /dev/null
+++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/context/DefaultPortletEnvironment.java
@@ -0,0 +1,79 @@
+/*
+ * 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.portlet.context;
+
+import javax.portlet.PortletConfig;
+import javax.portlet.PortletContext;
+import javax.servlet.ServletContext;
+
+import org.springframework.core.env.DefaultEnvironment;
+import org.springframework.core.env.Environment;
+import org.springframework.core.env.PropertySource;
+import org.springframework.core.env.PropertySource.StubPropertySource;
+import org.springframework.web.context.support.DefaultWebEnvironment;
+
+/**
+ * {@link Environment} implementation to be used by {@code Servlet}-based web
+ * applications. All Portlet-related {@code ApplicationContext} classes initialize an instance
+ * by default.
+ *
+ *
Contributes {@code ServletContext}-, {@code PortletContext}-, and {@code PortletConfig}-based
+ * {@link PropertySource} instances. See the {@link #DefaultPortletEnvironment()} constructor
+ * for details.
+ *
+ * @author Chris Beams
+ * @since 3.1
+ * @see DefaultEnvironment
+ * @see DefaultWebEnvironment
+ */
+public class DefaultPortletEnvironment extends DefaultEnvironment {
+
+ /** Portlet context init parameters property source name: {@value} */
+ public static final String PORTLET_CONTEXT_PROPERTY_SOURCE_NAME = "portletContextInitParams";
+
+ /** Portlet config init parameters property source name: {@value} */
+ 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:
+ *
+ * {@value #PORTLET_CONFIG_PROPERTY_SOURCE_NAME}
+ * {@value #PORTLET_CONTEXT_PROPERTY_SOURCE_NAME}
+ * {@linkplain DefaultWebEnvironment#SERVLET_CONTEXT_PROPERTY_SOURCE_NAME "servletContextInitParams"}
+ *
+ * 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.
+ *
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 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));
+ }
+}
diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/context/PortletApplicationContextUtils.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/context/PortletApplicationContextUtils.java
index 3d36f2d3a75..9a1ff5880d8 100644
--- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/context/PortletApplicationContextUtils.java
+++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/context/PortletApplicationContextUtils.java
@@ -21,6 +21,7 @@ import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
+
import javax.portlet.PortletConfig;
import javax.portlet.PortletContext;
import javax.portlet.PortletRequest;
@@ -30,6 +31,7 @@ import javax.servlet.ServletContext;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
+import org.springframework.core.env.MutablePropertySources;
import org.springframework.util.Assert;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestAttributes;
@@ -37,6 +39,7 @@ import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.RequestScope;
import org.springframework.web.context.request.SessionScope;
import org.springframework.web.context.request.WebRequest;
+import org.springframework.web.context.support.WebApplicationContextUtils;
/**
* Convenience methods for retrieving the root WebApplicationContext for a given
@@ -184,6 +187,29 @@ public abstract class PortletApplicationContextUtils {
}
}
+ /**
+ * Replace {@code Servlet}- and {@code Portlet}-based stub property sources
+ * with actual instances populated with the given context and config objects.
+ * @see org.springframework.core.env.PropertySource.StubPropertySource
+ * @see org.springframework.core.env.ConfigurableEnvironment#getPropertySources()
+ * @see org.springframework.web.context.support.WebApplicationContextUtils#initServletPropertySources(MutablePropertySources, ServletContext)
+ */
+ public static void initPortletPropertySources(MutablePropertySources propertySources, ServletContext servletContext,
+ PortletContext portletContext, PortletConfig portletConfig) {
+ Assert.notNull(propertySources, "propertySources must not be null");
+
+ WebApplicationContextUtils.initServletPropertySources(propertySources, servletContext);
+
+ if(portletContext != null && propertySources.contains(DefaultPortletEnvironment.PORTLET_CONTEXT_PROPERTY_SOURCE_NAME)) {
+ propertySources.replace(DefaultPortletEnvironment.PORTLET_CONTEXT_PROPERTY_SOURCE_NAME,
+ new PortletContextPropertySource(DefaultPortletEnvironment.PORTLET_CONTEXT_PROPERTY_SOURCE_NAME, portletContext));
+ }
+ if(portletConfig != null && propertySources.contains(DefaultPortletEnvironment.PORTLET_CONFIG_PROPERTY_SOURCE_NAME)) {
+ propertySources.replace(DefaultPortletEnvironment.PORTLET_CONFIG_PROPERTY_SOURCE_NAME,
+ new PortletConfigPropertySource(DefaultPortletEnvironment.PORTLET_CONFIG_PROPERTY_SOURCE_NAME, portletConfig));
+ }
+ }
+
/**
* Return the current RequestAttributes instance as PortletRequestAttributes.
* @see RequestContextHolder#currentRequestAttributes()
diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/context/PortletConfigPropertySource.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/context/PortletConfigPropertySource.java
new file mode 100644
index 00000000000..94b4142f8ce
--- /dev/null
+++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/context/PortletConfigPropertySource.java
@@ -0,0 +1,46 @@
+/*
+ * 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.portlet.context;
+
+import javax.portlet.PortletConfig;
+
+import org.springframework.core.env.PropertySource;
+import org.springframework.util.CollectionUtils;
+
+/**
+ * {@link PropertySource} that reads init parameters from a {@link PortletConfig} object.
+ *
+ * @author Chris Beams
+ * @since 3.1
+ * @see PortletContextPropertySource
+ */
+public class PortletConfigPropertySource extends PropertySource {
+
+ public PortletConfigPropertySource(String name, PortletConfig portletConfig) {
+ super(name, portletConfig);
+ }
+
+ @Override
+ public String[] getPropertyNames() {
+ return CollectionUtils.toArray(this.source.getInitParameterNames(), EMPTY_NAMES_ARRAY);
+ }
+
+ @Override
+ public String getProperty(String name) {
+ return this.source.getInitParameter(name);
+ }
+}
diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/context/PortletContextPropertySource.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/context/PortletContextPropertySource.java
new file mode 100644
index 00000000000..359960cac2c
--- /dev/null
+++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/context/PortletContextPropertySource.java
@@ -0,0 +1,46 @@
+/*
+ * 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.portlet.context;
+
+import javax.portlet.PortletContext;
+
+import org.springframework.core.env.PropertySource;
+import org.springframework.util.CollectionUtils;
+
+/**
+ * {@link PropertySource} that reads init parameters from a {@link PortletContext} object.
+ *
+ * @author Chris Beams
+ * @since 3.1
+ * @see PortletConfigPropertySource
+ */
+public class PortletContextPropertySource extends PropertySource {
+
+ public PortletContextPropertySource(String name, PortletContext portletContext) {
+ super(name, portletContext);
+ }
+
+ @Override
+ public String[] getPropertyNames() {
+ return CollectionUtils.toArray(this.source.getInitParameterNames(), EMPTY_NAMES_ARRAY);
+ }
+
+ @Override
+ public String getProperty(String name) {
+ return this.source.getInitParameter(name);
+ }
+}
diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/context/StaticPortletApplicationContext.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/context/StaticPortletApplicationContext.java
index 2746d8a1138..cac5932f5ff 100644
--- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/context/StaticPortletApplicationContext.java
+++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/context/StaticPortletApplicationContext.java
@@ -23,7 +23,7 @@ import javax.servlet.ServletContext;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.StaticApplicationContext;
-import org.springframework.core.env.DefaultWebEnvironment;
+import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.web.context.WebApplicationContext;
@@ -61,10 +61,29 @@ public class StaticPortletApplicationContext extends StaticApplicationContext
public StaticPortletApplicationContext() {
setDisplayName("Root Portlet ApplicationContext");
- setEnvironment(new DefaultWebEnvironment()); // TODO SPR-7508: create custom portlet env?
}
+ /**
+ * Return a new {@link DefaultPortletEnvironment}
+ */
+ @Override
+ protected ConfigurableEnvironment createEnvironment() {
+ return new DefaultPortletEnvironment();
+ }
+
+ /**
+ * {@inheritDoc}
+ * Replace {@code Portlet}- and {@code Servlet}-related property sources.
+ */
+ @Override
+ protected void initPropertySources() {
+ super.initPropertySources();
+ PortletApplicationContextUtils.initPortletPropertySources(
+ this.getEnvironment().getPropertySources(), this.servletContext,
+ this.portletContext, this.portletConfig);
+ }
+
@Override
public void setParent(ApplicationContext parent) {
super.setParent(parent);
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/HttpServletBean.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/HttpServletBean.java
index f166bdbc96b..b2bb312ea25 100644
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/HttpServletBean.java
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/HttpServletBean.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2008 the original author or authors.
+ * 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.
@@ -35,12 +35,12 @@ import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.PropertyValues;
import org.springframework.context.EnvironmentAware;
-import org.springframework.core.env.DefaultWebEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceEditor;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.StringUtils;
+import org.springframework.web.context.support.DefaultWebEnvironment;
import org.springframework.web.context.support.ServletContextResourceLoader;
/**
@@ -75,7 +75,8 @@ import org.springframework.web.context.support.ServletContextResourceLoader;
* @see #doGet
* @see #doPost
*/
-public abstract class HttpServletBean extends HttpServlet {
+@SuppressWarnings("serial")
+public abstract class HttpServletBean extends HttpServlet implements EnvironmentAware {
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
@@ -86,9 +87,6 @@ public abstract class HttpServletBean extends HttpServlet {
*/
private final Set requiredProperties = new HashSet();
- /**
- * TODO SPR-7508: think about making this overridable {@link EnvironmentAware}?
- */
private Environment environment = new DefaultWebEnvironment();
@@ -182,6 +180,15 @@ public abstract class HttpServletBean extends HttpServlet {
protected void initServletBean() throws ServletException {
}
+ /**
+ * {@inheritDoc}
+ * Any environment set here overrides the {@link DefaultWebEnvironment}
+ * provided by default.
+ */
+ public void setEnvironment(Environment environment) {
+ this.environment = environment;
+ }
+
/**
* PropertyValues implementation created from ServletConfig init parameters.
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java
index 7ed7cd4cd26..8540de9c2e0 100644
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java
@@ -35,9 +35,9 @@ import org.springframework.web.servlet.support.WebContentGenerator;
/**
* {@link HttpRequestHandler} that serves static resources optimized for superior browser performance
* (according to the guidelines of Page Speed, YSlow, etc.) by allowing for flexible cache settings
- * ({@link #setCacheSeconds "cacheSeconds" property}, last-modified support).
+ * ({@linkplain #setCacheSeconds "cacheSeconds" property}, last-modified support).
*
- *
The {@link #setLocations "locations" property takes a list of Spring {@link Resource} locations
+ *
The {@linkplain #setLocations "locations" property} takes a list of Spring {@link Resource} locations
* from which static resources are allowed to be served by this handler. For a given request, the
* list of locations will be consulted in order for the presence of the requested resource, and the
* first found match will be written to the response, with {@code Expires} and {@code Cache-Control}
@@ -54,7 +54,7 @@ import org.springframework.web.servlet.support.WebContentGenerator;
* using Spring EL. See the reference manual for further examples of this approach.
*
*
Rather than being directly configured as a bean, this handler will typically be configured
- * through use of the <mvc:resources/> XML configuration element.
+ * through use of the {@code } XML configuration element.
*
* @author Keith Donald
* @author Jeremy Grelle
diff --git a/org.springframework.web/src/main/java/org/springframework/web/context/support/AbstractRefreshableWebApplicationContext.java b/org.springframework.web/src/main/java/org/springframework/web/context/support/AbstractRefreshableWebApplicationContext.java
index 4a54674408c..377cca40154 100644
--- a/org.springframework.web/src/main/java/org/springframework/web/context/support/AbstractRefreshableWebApplicationContext.java
+++ b/org.springframework.web/src/main/java/org/springframework/web/context/support/AbstractRefreshableWebApplicationContext.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2010 the original author or authors.
+ * Copyright 2002-2009 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.
@@ -21,7 +21,7 @@ import javax.servlet.ServletContext;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.support.AbstractRefreshableConfigApplicationContext;
-import org.springframework.core.env.DefaultWebEnvironment;
+import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.ui.context.Theme;
@@ -93,14 +93,11 @@ public abstract class AbstractRefreshableWebApplicationContext extends AbstractR
public AbstractRefreshableWebApplicationContext() {
setDisplayName("Root WebApplicationContext");
- setEnvironment(new DefaultWebEnvironment());
}
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
- // TODO: SPR-7508 extract createEnvironment() method; do also in GWAC
- this.getEnvironment().getPropertySources().addFirst(new ServletContextPropertySource(this.servletContext));
}
public ServletContext getServletContext() {
@@ -112,8 +109,6 @@ public abstract class AbstractRefreshableWebApplicationContext extends AbstractR
if (servletConfig != null && this.servletContext == null) {
this.setServletContext(servletConfig.getServletContext());
}
- // TODO: SPR-7508 extract createEnvironment() method; do also in GWAC
- this.getEnvironment().getPropertySources().addFirst(new ServletConfigPropertySource(servletConfig));
}
public ServletConfig getServletConfig() {
@@ -136,6 +131,14 @@ public abstract class AbstractRefreshableWebApplicationContext extends AbstractR
return super.getConfigLocations();
}
+ /**
+ * Create and return a new {@link DefaultWebEnvironment}.
+ */
+ @Override
+ protected ConfigurableEnvironment createEnvironment() {
+ return new DefaultWebEnvironment();
+ }
+
/**
* Register request/session scopes, a {@link ServletContextAwareProcessor}, etc.
@@ -176,6 +179,18 @@ public abstract class AbstractRefreshableWebApplicationContext extends AbstractR
this.themeSource = UiApplicationContextUtils.initThemeSource(this);
}
+ /**
+ * {@inheritDoc}
+ *
Replace {@code Servlet}-related property sources.
+ */
+ @Override
+ protected void initPropertySources() {
+ super.initPropertySources();
+ WebApplicationContextUtils.initServletPropertySources(
+ this.getEnvironment().getPropertySources(), this.servletContext,
+ this.servletConfig);
+ }
+
public Theme getTheme(String themeName) {
return this.themeSource.getTheme(themeName);
}
diff --git a/org.springframework.web/src/main/java/org/springframework/web/context/support/AnnotationConfigWebApplicationContext.java b/org.springframework.web/src/main/java/org/springframework/web/context/support/AnnotationConfigWebApplicationContext.java
index b126cb3ec33..1e8c0db0dd0 100644
--- a/org.springframework.web/src/main/java/org/springframework/web/context/support/AnnotationConfigWebApplicationContext.java
+++ b/org.springframework.web/src/main/java/org/springframework/web/context/support/AnnotationConfigWebApplicationContext.java
@@ -28,7 +28,7 @@ import org.springframework.context.annotation.ScopeMetadataResolver;
* which accepts annotated classes as input - in particular
* {@link org.springframework.context.annotation.Configuration @Configuration}-annotated
* classes, but also plain {@link org.springframework.stereotype.Component @Components}
- * and JSR-330 compliant classes using {@literal javax.inject} annotations. Allows for
+ * and JSR-330 compliant classes using {@code javax.inject} annotations. Allows for
* registering classes one by one (specifying class names as config location) as well
* as for classpath scanning (specifying base packages as config location).
*
@@ -46,7 +46,7 @@ import org.springframework.context.annotation.ScopeMetadataResolver;
* FrameworkServlet. The param-value may contain both fully-qualified
* class names and base packages to scan for components.
*
- *
Note: In case of multiple {@literal @Configuration} classes, later {@literal @Bean}
+ *
Note: In case of multiple {@code @Configuration} classes, later {@code @Bean}
* definitions will override ones defined in earlier loaded files. This can be leveraged
* to deliberately override certain bean definitions via an extra Configuration class.
*
@@ -60,10 +60,10 @@ public class AnnotationConfigWebApplicationContext extends AbstractRefreshableWe
/**
* Register a {@link BeanDefinition} for each class specified by {@link #getConfigLocations()},
* or scan each specified package for annotated classes. Enables the default set of
- * annotation configuration post processors, such that {@literal @Autowired},
- * {@literal @Required}, and associated annotations can be used.
+ * annotation configuration post processors, such that {@code @Autowired},
+ * {@code @Required}, and associated annotations can be used.
*
Configuration class bean definitions are registered with generated bean definition
- * names unless the {@literal value} attribute is provided to the stereotype annotation.
+ * names unless the {@code value} attribute is provided to the stereotype annotation.
* @see #getConfigLocations()
* @see AnnotatedBeanDefinitionReader
* @see ClassPathBeanDefinitionScanner
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
new file mode 100644
index 00000000000..4e942bb05d0
--- /dev/null
+++ b/org.springframework.web/src/main/java/org/springframework/web/context/support/DefaultWebEnvironment.java
@@ -0,0 +1,71 @@
+/*
+ * 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 javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+
+import org.springframework.core.env.DefaultEnvironment;
+import org.springframework.core.env.PropertySource;
+import org.springframework.core.env.PropertySource.StubPropertySource;
+
+/**
+ * {@link Environment} implementation to be used by {@code Servlet}-based web
+ * applications. All web-related (servlet-based) {@code ApplicationContext} classes
+ * initialize an instance by default.
+ *
+ *
Contributes {@code ServletConfig}- and {@code ServletContext}-based {@link PropertySource}
+ * instances. See the {@link #DefaultWebEnvironment()} constructor for details.
+ *
+ * @author Chris Beams
+ * @since 3.1
+ * @see DefaultEnvironment
+ * @see DefaultPortletEnvironment
+ */
+public class DefaultWebEnvironment extends DefaultEnvironment {
+
+ /** Servlet context init parameters property source name: {@value} */
+ public static final String SERVLET_CONTEXT_PROPERTY_SOURCE_NAME = "servletContextInitParams";
+
+ /** Servlet config init parameters property source name: {@value} */
+ 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:
+ *
+ * {@value #SERVLET_CONFIG_PROPERTY_SOURCE_NAME}
+ * {@value #SERVLET_CONTEXT_PROPERTY_SOURCE_NAME}
+ *
+ * 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.
+ *
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.
+ * @see DefaultEnvironment#DefaultEnvironment
+ * @see ServletConfigPropertySource
+ * @see ServletContextPropertySource
+ * @see org.springframework.context.support.AbstractApplicationContext#initPropertySources
+ * @see WebApplicationContextUtils#initServletPropertySources
+ */
+ public DefaultWebEnvironment() {
+ this.getPropertySources().addFirst(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
+ this.getPropertySources().addFirst(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));
+ }
+}
diff --git a/org.springframework.web/src/main/java/org/springframework/web/context/support/GenericWebApplicationContext.java b/org.springframework.web/src/main/java/org/springframework/web/context/support/GenericWebApplicationContext.java
index 1604429071f..07afb7edf97 100644
--- a/org.springframework.web/src/main/java/org/springframework/web/context/support/GenericWebApplicationContext.java
+++ b/org.springframework.web/src/main/java/org/springframework/web/context/support/GenericWebApplicationContext.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2010 the original author or authors.
+ * Copyright 2002-2009 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.
@@ -21,7 +21,7 @@ import javax.servlet.ServletContext;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.support.GenericApplicationContext;
-import org.springframework.core.env.DefaultWebEnvironment;
+import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.ui.context.Theme;
@@ -61,12 +61,6 @@ public class GenericWebApplicationContext extends GenericApplicationContext
private ThemeSource themeSource;
- // override superclass definition of environment
- // TODO SPR-7508: polish
- {
- this.setEnvironment(new DefaultWebEnvironment());
- }
-
/**
* Create a new GenericWebApplicationContext.
* @see #setServletContext
@@ -123,6 +117,14 @@ public class GenericWebApplicationContext extends GenericApplicationContext
}
+ /**
+ * Create and return a new {@link DefaultWebEnvironment}.
+ */
+ @Override
+ protected ConfigurableEnvironment createEnvironment() {
+ return new DefaultWebEnvironment();
+ }
+
/**
* Register ServletContextAwareProcessor.
* @see ServletContextAwareProcessor
@@ -160,7 +162,17 @@ public class GenericWebApplicationContext extends GenericApplicationContext
@Override
protected void onRefresh() {
this.themeSource = UiApplicationContextUtils.initThemeSource(this);
- this.getEnvironment().getPropertySources().addFirst(new ServletContextPropertySource(servletContext));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
Replace {@code Servlet}-related property sources.
+ */
+ @Override
+ protected void initPropertySources() {
+ super.initPropertySources();
+ WebApplicationContextUtils.initServletPropertySources(
+ this.getEnvironment().getPropertySources(), this.servletContext);
}
public Theme getTheme(String themeName) {
diff --git a/org.springframework.web/src/main/java/org/springframework/web/context/support/ServletConfigPropertySource.java b/org.springframework.web/src/main/java/org/springframework/web/context/support/ServletConfigPropertySource.java
index 61d8658d200..2cc7afc405d 100644
--- a/org.springframework.web/src/main/java/org/springframework/web/context/support/ServletConfigPropertySource.java
+++ b/org.springframework.web/src/main/java/org/springframework/web/context/support/ServletConfigPropertySource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2010 the original author or authors.
+ * 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.
@@ -20,36 +20,27 @@ import java.util.Enumeration;
import javax.servlet.ServletConfig;
-import org.springframework.core.env.DefaultWebEnvironment;
import org.springframework.core.env.PropertySource;
-
+import org.springframework.util.CollectionUtils;
/**
- * TODO SPR-7508: document
- *
+ * {@link PropertySource} that reads init parameters from a {@link ServletConfig} object.
+ *
* @author Chris Beams
* @since 3.1
* @see ServletContextPropertySource
*/
public class ServletConfigPropertySource extends PropertySource {
- public ServletConfigPropertySource(ServletConfig servletConfig) {
- this(DefaultWebEnvironment.SERVLET_CONFIG_PARAMS_PROPERTY_SOURCE_NAME, servletConfig);
- }
-
public ServletConfigPropertySource(String name, ServletConfig servletConfig) {
super(name, servletConfig);
}
@Override
- public boolean containsProperty(String name) {
- Enumeration> initParamNames = this.source.getInitParameterNames();
- while (initParamNames.hasMoreElements()) {
- if (initParamNames.nextElement().equals(name)) {
- return true;
- }
- }
- return false;
+ @SuppressWarnings("unchecked")
+ public String[] getPropertyNames() {
+ return CollectionUtils.toArray(
+ (Enumeration)this.source.getInitParameterNames(), EMPTY_NAMES_ARRAY);
}
@Override
@@ -57,14 +48,4 @@ public class ServletConfigPropertySource extends PropertySource {
return this.source.getInitParameter(name);
}
- @Override
- public int size() {
- int size=0;
- Enumeration> initParamNames = this.source.getInitParameterNames();
- while (initParamNames.hasMoreElements()) {
- initParamNames.nextElement();
- size++;
- }
- return size;
- }
}
diff --git a/org.springframework.web/src/main/java/org/springframework/web/context/support/ServletContextPropertyPlaceholderConfigurer.java b/org.springframework.web/src/main/java/org/springframework/web/context/support/ServletContextPropertyPlaceholderConfigurer.java
index 3a35563168b..adfc6176261 100644
--- a/org.springframework.web/src/main/java/org/springframework/web/context/support/ServletContextPropertyPlaceholderConfigurer.java
+++ b/org.springframework.web/src/main/java/org/springframework/web/context/support/ServletContextPropertyPlaceholderConfigurer.java
@@ -21,12 +21,11 @@ import java.util.Properties;
import javax.servlet.ServletContext;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
-import org.springframework.context.support.EnvironmentAwarePropertyPlaceholderConfigurer;
import org.springframework.web.context.ServletContextAware;
/**
- * Subclass of PropertyPlaceholderConfigurer that resolves placeholders as
+ * Subclass of {@link PropertyPlaceholderConfigurer} that resolves placeholders as
* ServletContext init parameters (that is, web.xml context-param
* entries).
*
@@ -59,8 +58,8 @@ import org.springframework.web.context.ServletContextAware;
* @see #setSearchContextAttributes
* @see javax.servlet.ServletContext#getInitParameter(String)
* @see javax.servlet.ServletContext#getAttribute(String)
- * @deprecated in Spring 3.1 in favor of {@link EnvironmentAwarePropertyPlaceholderConfigurer}
- * in conjunction with {@link org.springframework.core.env.DefaultWebEnvironment}.
+ * @deprecated in Spring 3.1 in favor of {@link org.springframework.context.support.PropertySourcesPlaceholderConfigurer}
+ * in conjunction with {@link org.springframework.web.context.support.DefaultWebEnvironment}.
*/
@Deprecated
public class ServletContextPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer
diff --git a/org.springframework.web/src/main/java/org/springframework/web/context/support/ServletContextPropertySource.java b/org.springframework.web/src/main/java/org/springframework/web/context/support/ServletContextPropertySource.java
index 8bcad5ab2f8..cee78c5105c 100644
--- a/org.springframework.web/src/main/java/org/springframework/web/context/support/ServletContextPropertySource.java
+++ b/org.springframework.web/src/main/java/org/springframework/web/context/support/ServletContextPropertySource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2010 the original author or authors.
+ * 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.
@@ -20,12 +20,11 @@ import java.util.Enumeration;
import javax.servlet.ServletContext;
-import org.springframework.core.env.DefaultWebEnvironment;
import org.springframework.core.env.PropertySource;
-
+import org.springframework.util.CollectionUtils;
/**
- * TODO SPR-7508: document
+ * {@link PropertySource} that reads init parameters from a {@link ServletContext} object.
*
* @author Chris Beams
* @since 3.1
@@ -33,38 +32,19 @@ import org.springframework.core.env.PropertySource;
*/
public class ServletContextPropertySource extends PropertySource {
- public ServletContextPropertySource(ServletContext servletContext) {
- this(DefaultWebEnvironment.SERVLET_CONTEXT_PARAMS_PROPERTY_SOURCE_NAME, servletContext);
- }
-
public ServletContextPropertySource(String name, ServletContext servletContext) {
super(name, servletContext);
}
@Override
- public boolean containsProperty(String name) {
- Enumeration> initParamNames = this.source.getInitParameterNames();
- while (initParamNames.hasMoreElements()) {
- if (initParamNames.nextElement().equals(name)) {
- return true;
- }
- }
- return false;
+ @SuppressWarnings("unchecked")
+ public String[] getPropertyNames() {
+ return CollectionUtils.toArray(
+ (Enumeration)this.source.getInitParameterNames(), EMPTY_NAMES_ARRAY);
}
@Override
public String getProperty(String name) {
return this.source.getInitParameter(name);
}
-
- @Override
- public int size() {
- int size=0;
- Enumeration> initParamNames = this.source.getInitParameterNames();
- while (initParamNames.hasMoreElements()) {
- initParamNames.nextElement();
- size++;
- }
- return size;
- }
}
diff --git a/org.springframework.web/src/main/java/org/springframework/web/context/support/StaticWebApplicationContext.java b/org.springframework.web/src/main/java/org/springframework/web/context/support/StaticWebApplicationContext.java
index 395b50d1c2d..dfa8e8c37ce 100644
--- a/org.springframework.web/src/main/java/org/springframework/web/context/support/StaticWebApplicationContext.java
+++ b/org.springframework.web/src/main/java/org/springframework/web/context/support/StaticWebApplicationContext.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2010 the original author or authors.
+ * 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.
@@ -16,14 +16,12 @@
package org.springframework.web.context.support;
-import java.util.LinkedList;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.support.StaticApplicationContext;
-import org.springframework.core.env.DefaultWebEnvironment;
-import org.springframework.core.env.PropertySource;
+import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.ui.context.Theme;
@@ -69,7 +67,6 @@ public class StaticWebApplicationContext extends StaticApplicationContext
public StaticWebApplicationContext() {
setDisplayName("Root WebApplicationContext");
- setEnvironment(new DefaultWebEnvironment()); // TODO SPR-7508: see GenericWebApplicationContext, AbstractRefreshableWebApplicationContext
}
@@ -162,15 +159,27 @@ public class StaticWebApplicationContext extends StaticApplicationContext
return new ServletContextResourcePatternResolver(this);
}
+ /**
+ * Create and return a new {@link DefaultWebEnvironment}.
+ */
+ @Override
+ protected ConfigurableEnvironment createEnvironment() {
+ return new DefaultWebEnvironment();
+ }
+
/**
* Initialize the theme capability.
*/
@Override
protected void onRefresh() {
this.themeSource = UiApplicationContextUtils.initThemeSource(this);
- LinkedList> propertySources = this.getEnvironment().getPropertySources();
- propertySources.addFirst(new ServletContextPropertySource(servletContext));
- propertySources.addFirst(new ServletConfigPropertySource(servletConfig));
+ }
+
+ @Override
+ protected void initPropertySources() {
+ super.initPropertySources();
+ WebApplicationContextUtils.initServletPropertySources(
+ this.getEnvironment().getPropertySources(), this.servletContext, this.servletConfig);
}
public Theme getTheme(String themeName) {
diff --git a/org.springframework.web/src/main/java/org/springframework/web/context/support/WebApplicationContextUtils.java b/org.springframework.web/src/main/java/org/springframework/web/context/support/WebApplicationContextUtils.java
index 8e611e6b4d5..3be23bf2852 100644
--- a/org.springframework.web/src/main/java/org/springframework/web/context/support/WebApplicationContextUtils.java
+++ b/org.springframework.web/src/main/java/org/springframework/web/context/support/WebApplicationContextUtils.java
@@ -21,6 +21,7 @@ import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
+
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.servlet.ServletConfig;
@@ -30,6 +31,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.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.web.context.ConfigurableWebApplicationContext;
@@ -192,14 +194,14 @@ public abstract class WebApplicationContextUtils {
if (!bf.containsBean(WebApplicationContext.CONTEXT_PARAMETERS_BEAN_NAME)) {
Map parameterMap = new HashMap();
if (sc != null) {
- Enumeration paramNameEnum = sc.getInitParameterNames();
+ Enumeration> paramNameEnum = sc.getInitParameterNames();
while (paramNameEnum.hasMoreElements()) {
String paramName = (String) paramNameEnum.nextElement();
parameterMap.put(paramName, sc.getInitParameter(paramName));
}
}
if (config != null) {
- Enumeration paramNameEnum = config.getInitParameterNames();
+ Enumeration> paramNameEnum = config.getInitParameterNames();
while (paramNameEnum.hasMoreElements()) {
String paramName = (String) paramNameEnum.nextElement();
parameterMap.put(paramName, config.getInitParameter(paramName));
@@ -212,7 +214,7 @@ public abstract class WebApplicationContextUtils {
if (!bf.containsBean(WebApplicationContext.CONTEXT_ATTRIBUTES_BEAN_NAME)) {
Map attributeMap = new HashMap();
if (sc != null) {
- Enumeration attrNameEnum = sc.getAttributeNames();
+ Enumeration> attrNameEnum = sc.getAttributeNames();
while (attrNameEnum.hasMoreElements()) {
String attrName = (String) attrNameEnum.nextElement();
attributeMap.put(attrName, sc.getAttribute(attrName));
@@ -223,6 +225,38 @@ public abstract class WebApplicationContextUtils {
}
}
+ /**
+ * Replace {@code Servlet}-based stub property sources with actual instances
+ * populated with the given context object.
+ * @see org.springframework.core.env.PropertySource.StubPropertySource
+ * @see org.springframework.core.env.ConfigurableEnvironment#getPropertySources()
+ * @see org.springframework.web.context.support.WebApplicationContextUtils#initServletPropertySources(MutablePropertySources, ServletContext)
+ */
+ public static void initServletPropertySources(
+ MutablePropertySources propertySources, ServletContext servletContext) {
+ initServletPropertySources(propertySources, servletContext, null);
+ }
+
+ /**
+ * Replace {@code Servlet}-based stub property sources with actual instances
+ * populated with the given context and config objects.
+ * @see org.springframework.core.env.PropertySource.StubPropertySource
+ * @see org.springframework.web.context.support.WebApplicationContextUtils#initServletPropertySources(MutablePropertySources, ServletContext)
+ * @see org.springframework.core.env.ConfigurableEnvironment#getPropertySources()
+ */
+ public static void initServletPropertySources(
+ MutablePropertySources propertySources, ServletContext servletContext, ServletConfig servletConfig) {
+ Assert.notNull(propertySources, "propertySources must not be null");
+ if(servletContext != null && propertySources.contains(DefaultWebEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME)) {
+ propertySources.replace(DefaultWebEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME,
+ new ServletContextPropertySource(DefaultWebEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME, servletContext));
+ }
+ if(servletConfig != null && propertySources.contains(DefaultWebEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME)) {
+ propertySources.replace(DefaultWebEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME,
+ new ServletConfigPropertySource(DefaultWebEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME, servletConfig));
+ }
+ }
+
/**
* Return the current RequestAttributes instance as ServletRequestAttributes.
* @see RequestContextHolder#currentRequestAttributes()
@@ -239,6 +273,7 @@ public abstract class WebApplicationContextUtils {
/**
* Factory that exposes the current request object on demand.
*/
+ @SuppressWarnings("serial")
private static class RequestObjectFactory implements ObjectFactory, Serializable {
public ServletRequest getObject() {
@@ -255,6 +290,7 @@ public abstract class WebApplicationContextUtils {
/**
* Factory that exposes the current session object on demand.
*/
+ @SuppressWarnings("serial")
private static class SessionObjectFactory implements ObjectFactory, Serializable {
public HttpSession getObject() {
@@ -271,6 +307,7 @@ public abstract class WebApplicationContextUtils {
/**
* Factory that exposes the current WebRequest object on demand.
*/
+ @SuppressWarnings("serial")
private static class WebRequestObjectFactory implements ObjectFactory, Serializable {
public WebRequest getObject() {
diff --git a/org.springframework.web/src/main/java/org/springframework/web/filter/GenericFilterBean.java b/org.springframework.web/src/main/java/org/springframework/web/filter/GenericFilterBean.java
index 288532600b8..5ead00a4d9f 100644
--- a/org.springframework.web/src/main/java/org/springframework/web/filter/GenericFilterBean.java
+++ b/org.springframework.web/src/main/java/org/springframework/web/filter/GenericFilterBean.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2008 the original author or authors.
+ * 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.
@@ -38,7 +38,6 @@ import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.EnvironmentAware;
-import org.springframework.core.env.DefaultWebEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceEditor;
@@ -46,6 +45,7 @@ import org.springframework.core.io.ResourceLoader;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.context.ServletContextAware;
+import org.springframework.web.context.support.DefaultWebEnvironment;
import org.springframework.web.context.support.ServletContextResourceLoader;
import org.springframework.web.util.NestedServletException;
@@ -88,16 +88,10 @@ public abstract class GenericFilterBean implements
*/
private final Set requiredProperties = new HashSet();
- /* The FilterConfig of this filter */
private FilterConfig filterConfig;
private String beanName;
- /**
- * TODO SPR-7508: document
- * Defaults to {@link DefaultWebEnvironment}; can be overriden if deployed
- * as a spring bean by {@link #setEnvironment(Environment)}
- */
private Environment environment = new DefaultWebEnvironment();
private ServletContext servletContext;
@@ -115,7 +109,9 @@ public abstract class GenericFilterBean implements
}
/**
- * TODO SPR-7508: document
+ * {@inheritDoc}
+ * Any environment set here overrides the {@link DefaultWebEnvironment}
+ * provided by default.
*/
public void setEnvironment(Environment environment) {
this.environment = environment;
@@ -282,6 +278,7 @@ public abstract class GenericFilterBean implements
/**
* PropertyValues implementation created from FilterConfig init parameters.
*/
+ @SuppressWarnings("serial")
private static class FilterConfigPropertyValues extends MutablePropertyValues {
/**
@@ -292,12 +289,12 @@ public abstract class GenericFilterBean implements
* @throws ServletException if any required properties are missing
*/
public FilterConfigPropertyValues(FilterConfig config, Set requiredProperties)
- throws ServletException {
+ throws ServletException {
Set missingProps = (requiredProperties != null && !requiredProperties.isEmpty()) ?
new HashSet(requiredProperties) : null;
- Enumeration en = config.getInitParameterNames();
+ Enumeration> en = config.getInitParameterNames();
while (en.hasMoreElements()) {
String property = (String) en.nextElement();
Object value = config.getInitParameter(property);
diff --git a/org.springframework.web/src/test/java/org/springframework/web/context/support/DefaultWebEnvironmentTests.java b/org.springframework.web/src/test/java/org/springframework/web/context/support/DefaultWebEnvironmentTests.java
new file mode 100644
index 00000000000..fdfb9ac9fba
--- /dev/null
+++ b/org.springframework.web/src/test/java/org/springframework/web/context/support/DefaultWebEnvironmentTests.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2002-2010 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.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import java.util.List;
+
+import org.junit.Test;
+import org.springframework.core.env.ConfigurableEnvironment;
+import org.springframework.core.env.DefaultEnvironment;
+import org.springframework.core.env.PropertySource;
+
+
+public class DefaultWebEnvironmentTests {
+
+ @Test
+ public void propertySourceOrder() {
+ ConfigurableEnvironment env = new DefaultWebEnvironment();
+ List> sources = env.getPropertySources().asList();
+ assertThat(sources.size(), is(4));
+ assertThat(sources.get(0).getName(), equalTo(DefaultWebEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME));
+ assertThat(sources.get(1).getName(), equalTo(DefaultWebEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
+ assertThat(sources.get(2).getName(), equalTo(DefaultEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME));
+ assertThat(sources.get(3).getName(), equalTo(DefaultEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME));
+ }
+}