M1 cut of environment, profiles and property work (SPR-7508)

Decomposed Environment interface into PropertySources, PropertyResolver
objects

    Environment interface and implementations are still present, but
    simpler.

    PropertySources container aggregates PropertySource objects;
    PropertyResolver provides search, conversion, placeholder
    replacement. Single implementation for now is
    PropertySourcesPlaceholderResolver

Renamed EnvironmentAwarePropertyPlaceholderConfigurer to
PropertySourcesPlaceholderConfigurer

    <context:property-placeholder/> now registers PSPC by default, else
    PPC if systemPropertiesMode* settings are involved

Refined configuration and behavior of default profiles

    See Environment interface Javadoc for details

Added Portlet implementations of relevant interfaces:

    * DefaultPortletEnvironment
    * PortletConfigPropertySource, PortletContextPropertySource
    * Integrated each appropriately throughout Portlet app contexts

Added protected 'createEnvironment()' method to AbstractApplicationContext

    Subclasses can override at will to supply a custom Environment
    implementation.  In practice throughout the framework, this is how
    Web- and Portlet-related ApplicationContexts override use of the
    DefaultEnvironment and swap in DefaultWebEnvironment or
    DefaultPortletEnvironment as appropriate.

Introduced "stub-and-replace" behavior for Servlet- and Portlet-based
PropertySource implementations

    Allows for early registration and ordering of the stub, then
    replacement with actual backing object at refresh() time.

    Added AbstractApplicationContext.initPropertySources() method to
    support stub-and-replace behavior. Called from within existing
    prepareRefresh() method so as to avoid impact with
    ApplicationContext implementations that copy and modify AAC's
    refresh() method (e.g.: Spring DM).

    Added methods to WebApplicationContextUtils and
    PortletApplicationContextUtils to support stub-and-replace behavior

Added comprehensive Javadoc for all new or modified types and members

Added XSD documentation for all new or modified elements and attributes

    Including nested <beans>, <beans profile="..."/>, and changes for
    certain attributes type from xsd:IDREF to xsd:string

Improved fix for detecting non-file based Resources in
PropertiesLoaderSupport (SPR-7547, SPR-7552)

    Technically unrelated to environment work, but grouped in with
    this changeset for convenience.

Deprecated (removed) context:property-placeholder
'system-properties-mode' attribute from spring-context-3.1.xsd

    Functionality is preserved for those using schemas up to and including
    spring-context-3.0.  For 3.1, system-properties-mode is no longer
    supported as it conflicts with the idea of managing a set of property
    sources within the context's Environment object. See Javadoc in
    PropertyPlaceholderConfigurer, AbstractPropertyPlaceholderConfigurer
    and PropertySourcesPlaceholderConfigurer for details.

Introduced CollectionUtils.toArray(Enumeration<E>, A[])

Work items remaining for 3.1 M2:

    Consider repackaging PropertySource* types; eliminate internal use
    of SystemPropertyUtils and deprecate

    Further work on composition of Environment interface; consider
    repurposing existing PlaceholderResolver interface to obviate need
    for resolve[Required]Placeholder() methods currently in Environment.

    Ensure configurability of placeholder prefix, suffix, and value
    separator when working against an AbstractPropertyResolver

    Add JNDI-based Environment / PropertySource implementatinos

    Consider support for @Profile at the @Bean level

    Provide consistent logging for the entire property resolution
    lifecycle; consider issuing all such messages against a dedicated
    logger with a single category.

    Add reference documentation to cover the featureset.
This commit is contained in:
Chris Beams 2011-01-03 09:04:34 +00:00
parent b130a36af7
commit b3ff9be78f
111 changed files with 3439 additions and 1715 deletions

18
.gitignore vendored
View File

@ -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

View File

@ -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.
* <p>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,

View File

@ -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 {

View File

@ -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 <em>pull</em> values from a
* properties file or other {@linkplain org.springframework.core.env.PropertySource
* property source} into bean definitions.
*
* <p>The default placeholder syntax follows the Ant / Log4J / JSP EL style:
*
*<pre class="code">${...}</pre>
*
* Example XML bean definition:
*
*<pre class="code">{@code
*<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"/>
* <property name="driverClassName" value="}${driver}{@code"/>
* <property name="url" value="jdbc:}${dbname}{@code"/>
*</bean>
*}</pre>
*
* Example properties file:
*
* <pre class="code"> driver=com.mysql.jdbc.Driver
* dbname=mysql:mydb</pre>
*
* Annotated bean definitions may take advantage of property replacement using
* the {@link org.springframework.beans.factory.annotation.Value @Value} annotation:
*
*<pre class="code">@Value("${person.age}")</pre>
*
* Implementations check simple property values, lists, maps, props, and bean names
* in bean references. Furthermore, placeholder values can also cross-reference
* other placeholders, like:
*
*<pre class="code">rootPath=myrootdir
*subPath=${rootPath}/subdir</pre>
*
* In contrast to {@link PropertyOverrideConfigurer}, subclasses of this type allow
* filling in of explicit placeholders in bean definitions.
*
* <p>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 <em>own</em> placeholder syntax. Use {@link
* #ignoreUnresolvablePlaceholders} to intentionally suppress throwing an exception if a
* placeholder cannot be resolved.
*
* <p>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)}.
*
* <p>Example XML property with default value:
*
*<pre class="code">{@code
* <property name="url" value="jdbc:}${dbname:defaultdb}{@code"/>
*}</pre>
*
* @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 <code>null</code> 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 <code>null</code> when
* Set a value that should be treated as {@code null} when
* resolved as a placeholder value: e.g. "" (empty String) or "null".
* <p>Note that this will only apply to full property values,
* not to parts of concatenated values.
* <p>By default, no such null value is defined. This means that
* there is no way to express <code>null</code> 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 {

View File

@ -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 <i>pulls</i> 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.
*
* <p>The default placeholder syntax follows the Ant / Log4J / JSP EL style:
* <p>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.
*
* <pre class="code">${...}</pre>
* <p>{@link PropertyPlaceholderConfigurer} is still appropriate for use when:
* <ul>
* <li>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}).
* <li>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}.
* </ul>
*
* Example XML context definition:
*
* <pre class="code">&lt;bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"&gt;
* &lt;property name="driverClassName"&gt;&lt;value&gt;${driver}&lt;/value&gt;&lt;/property&gt;
* &lt;property name="url"&gt;&lt;value&gt;jdbc:${dbname}&lt;/value&gt;&lt;/property&gt;
* &lt;/bean&gt;</pre>
*
* Example properties file:
*
* <pre class="code">driver=com.mysql.jdbc.Driver
* dbname=mysql:mydb</pre>
*
* PropertyPlaceholderConfigurer checks simple property values, lists, maps,
* props, and bean names in bean references. Furthermore, placeholder values can
* also cross-reference other placeholders, like:
*
* <pre class="code">rootPath=myrootdir
* subPath=${rootPath}/subdir</pre>
*
* 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.
*
* <p>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 <i>own</i> placeholder syntax.
*
* <p>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".
*
* <p>Note that the context definition <i>is</i> 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).
*
* <p>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.
* <p>Prior to Spring 3.1, the {@code <context:property-placeholder/>} 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 {

View File

@ -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);

View File

@ -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;
}

View File

@ -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<Object> prototypesCurrentlyInCreation =
new NamedThreadLocal<Object>("Prototype beans currently in creation");
private ConfigurableEnvironment environment = new DefaultEnvironment();
/**
* Create a new AbstractBeanFactory.

View File

@ -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<String, Object> beans = new HashMap<String, Object>();
/** TODO SPR-7508: document */
private ConfigurableEnvironment environment = new DefaultEnvironment();
/**
* Add a new singleton bean.

View File

@ -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 <beans/>} document/element should
* be included or omitted.
*/
void setEnvironment(Environment environment);
}

View File

@ -1476,7 +1476,7 @@ public class BeanDefinitionParserDelegate {
/**
* Determine whether the name of the supplied node is equal to the supplied name.
* <p>The default implementation checks the supplied desired name against both
* {@link Node#getNodeName()) and {@link Node#getLocalName()}.
* {@link Node#getNodeName()} and {@link Node#getLocalName()}.
* <p>Subclasses may override the default implementation to provide a different
* mechanism for comparing node names.
* @param node the node to compare

View File

@ -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}
* <p>Default value is {@code null}; property is required for parsing any
* {@code <beans/>} 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}
* <p>This implementation parses bean definitions according to the "spring-beans" XSD
* (or DTD, historically).
* <p>Opens a DOM Document; then initializes the default settings
* specified at <code>&lt;beans&gt;</code> level; then parses
* the contained bean definitions.
* specified at the {@code <beans/>} 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 <beans/>} element.
* @throws IllegalStateException if {@code <beans profile="..."} attribute is present
* and Environment property has not been set
* @see #setEnvironment
*/
protected void doRegisterBeanDefinitions(Element root) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
Assert.state(this.environment != null, "environment property must not be null");
String[] specifiedProfiles = commaDelimitedListToStringArray(trimAllWhitespace(profileSpec));
if (!this.environment.acceptsProfiles(specifiedProfiles)) {
// TODO SPR-7508: log that this bean is being rejected on profile mismatch
return;
}
if (!this.environment.acceptsProfiles(specifiedProfiles)) {
// TODO SPR-7508: log that this bean is being rejected on profile mismatch
return;
}
}
// any nested <beans> elements will cause recursion in this method. in
// any nested <beans> elements will cause recursion in this method. In
// order to propagate and preserve <beans> 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<Resource> actualResources = new LinkedHashSet<Resource>(4);

View File

@ -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;

View File

@ -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;
* <p>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.
*
* <p><b>For advanced needs, consider using a {@link DefaultListableBeanFactory} with
* an {@link XmlBeanDefinitionReader}.</b> 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);
}
/**

View File

@ -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.
]]></xsd:documentation>
</xsd:annotation>
@ -63,8 +61,11 @@
<xsd:element name="beans">
<xsd:annotation>
<xsd:documentation><![CDATA[
The top level (typically root) element. Allows the definition
of default values for all nested bean definitions.
Container for <bean> 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 <beans> element
must be declared as the last element in the document.
]]></xsd:documentation>
</xsd:annotation>
<xsd:complexType>
@ -81,16 +82,20 @@
<xsd:attribute name="profile" use="optional" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
TODO:SPR-7508: Document profile annotation:
* may be comma-delimited
* empty profile means beans 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
The set of profiles for which this <beans> 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 <beans> element will be parsed, and all of its <bean> elements registered,
&lt;import&gt; 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
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
@ -115,7 +120,7 @@
<xsd:annotation>
<xsd:documentation><![CDATA[
The default 'autowire' value; see the documentation for the
'autowire' attribute of the 'bean' element. The default is 'no'.
'autowire' attribute of the 'bean' element. The default is 'default'.
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>

View File

@ -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<String, String> getModifiableSystemEnvironment() {

View File

@ -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",

View File

@ -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);

View File

@ -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);
}

View File

@ -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.
* <p>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 {
* <p>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 {
* <p>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;
}

View File

@ -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}
* <p>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);
}

View File

@ -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 <bean/>} element in the Spring XML schema.
* to those of the {@code <bean/>} element in the Spring XML schema.
*
* <p>Note that the <code>@Bean</code> annotation does not provide attributes for scope,
* primary or lazy. Rather, it should be used in conjunction with {@link Scope &#064;Scope},
* {@link Primary &#064;Primary}, and {@link Lazy &#064;Lazy} annotations to achieve
* <p>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.
* <p>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

View File

@ -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.
* <p>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;

View File

@ -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
* &lt;context:component-scan&gt; element.
* {@code <context:component-scan>} element.
*
* TODO SPR-7508: complete documentation.
*

View File

@ -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();

View File

@ -49,8 +49,8 @@ import org.springframework.util.ClassUtils;
* {@link BeanFactoryPostProcessor} used for bootstrapping processing of
* {@link Configuration @Configuration} classes.
*
* <p>Registered by default when using {@literal <context:annotation-config/>} or
* {@literal <context:component-scan/>}. Otherwise, may be declared manually as
* <p>Registered by default when using {@code <context:annotation-config/>} or
* {@code <context:component-scan/>}. Otherwise, may be declared manually as
* with any other BeanFactoryPostProcessor.
*
* <p>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.
* <p>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.
* <p>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");

View File

@ -38,7 +38,7 @@ import java.lang.annotation.Documented;
* <p>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 <bean depends-on="..."/>} is respected instead.
* {@code <bean depends-on="..."/>} is respected instead.
*
* @author Juergen Hoeller
* @since 3.0

View File

@ -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.
*
* <p>Provides functionality equivalent to the {@literal <import/>} element in Spring XML.
* Only supported for actual {@literal @Configuration}-annotated classes.
* <p>Provides functionality equivalent to the {@code <import/>} element in Spring XML.
* Only supported for actual {@code @Configuration}-annotated classes.
*
* <p>{@literal @Bean} definitions declared in imported {@literal @Configuration} classes
* <p>{@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.
*
* <p>If XML or other non-{@literal @Configuration} bean definition resources need to be
* <p>If XML or other non-{@code @Configuration} bean definition resources need to be
* imported, use {@link ImportResource @ImportResource}
*
* @author Chris Beams

View File

@ -30,14 +30,14 @@ import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
* Indicates one or more resources containing bean definitions to import.
*
* <p>Like {@link Import @Import}, this annotation provides functionality similar to the
* {@literal <import/>} element in Spring XML. It is typically used when
* {@code <import/>} 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.
*
* <p>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 <beans/>} XML files. Optionally, the {@link #reader()} attribute may be
* {@code <beans/>} 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();

View File

@ -25,25 +25,25 @@ import java.lang.annotation.Inherited;
/**
* Indicates whether a bean is to be lazily initialized.
*
*
* <p>May be used on any class directly or indirectly annotated with
* {@link org.springframework.stereotype.Component} or on methods annotated with
* {@link Bean}.
*
*
* <p>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.
*
* <p>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.
*
*
* <p>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

View File

@ -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.
*
*
* <p>May be used on any class directly or indirectly annotated with
* {@link org.springframework.stereotype.Component} or on methods annotated
* with {@link Bean}.
*
*
* <p>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 <bean primary="true|false"/>} is respected instead.
*
* {@code <bean primary="true|false"/>} is respected instead.
*
* @author Chris Beams
* @since 3.0
* @see Lazy

View File

@ -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.
*
* <p>A <em>profile</em> 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}.
*
* <p>The {@code @Profile} annotation may be used in any of the following ways:
* <ul>
* <li>as a type-level annotation on any class directly or indirectly annotated with
* {@code @Component}, including {@link Configuration @Configuration} classes
* <li>as a meta-annotation, for the purpose of composing custom stereotype annotations
* </ul>
*
* <p>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 <beans profile="p1,p2">}, 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 <code>@Profile({"p1", "p2"})</code>, that class will
* not be registered/processed unless profiles 'p1' and/or 'p2' have been activated.
*
* <p>If the {@code @Profile} annotation is omitted, registration will occur, regardless of which,
* if any, profiles are active.
*
* <p>When defining Spring beans via XML, the {@code "profile"} attribute of the {@code <beans>}
* 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");
}
}
}

View File

@ -35,7 +35,7 @@ import org.springframework.stereotype.Component;
* the instance returned from the method.
*
* <p>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.
* <p>Defaults to {@link ScopedProxyMode#NO}, indicating that no scoped
* proxy should be created.
* <p>Analogous to {@literal <aop:scoped-proxy/>} support in Spring XML.
* <p>Analogous to {@code <aop:scoped-proxy/>} support in Spring XML.
*/
ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;

View File

@ -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;

View File

@ -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);
}
}
}

View File

@ -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));
}
/**

View File

@ -206,8 +206,8 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
/** Statically specified listeners */
private Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<ApplicationListener<?>>();
/** 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}
* <p>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 <em>before</em> {@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();
}
/**
* <p>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.
}
/**

View File

@ -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.
* <p>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.
* <p>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

View File

@ -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);
}

View File

@ -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<PropertySource<?>> 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<Environment> {
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();
}
}
}

View File

@ -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;

View File

@ -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}
* <p>Delegates the given environment to underlying {@link XmlBeanDefinitionReader}.
* Should be called before any call to {@link #load}.
*/
@Override
public void setEnvironment(ConfigurableEnvironment environment) {

View File

@ -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}
*
* <p>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}
* <p>{@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}
* <p>Processing occurs by replacing ${...} placeholders in bean definitions by resolving each
* against this configurer's set of {@link PropertySources}, which includes:
* <ul>
* <li>all {@linkplain Environment#getPropertySources environment property sources}, if an
* {@code Environment} {@linkplain #setEnvironment is present}
* <li>{@linkplain #mergeProperties merged local properties}, if {@linkplain #setLocation any}
* {@linkplain #setLocations have} {@linkplain #setProperties been}
* {@linkplain #setPropertiesArray specified}
* <li>any property sources set by calling {@link #setPropertySources}
* </ul>
* <p>If {@link #setPropertySources} is called, <strong>environment and local properties will be
* ignored</strong>. 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());
}
}

View File

@ -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<String, Class> resourceTypes = new HashMap<String, Class>();
/** TODO SPR-7508: should be JNDI-specific environment */
private ConfigurableEnvironment environment = new DefaultEnvironment();
public SimpleJndiBeanFactory() {
setResourceRef(true);

View File

@ -90,42 +90,17 @@
<xsd:annotation>
<xsd:documentation><![CDATA[
Activates replacement of ${...} placeholders, resolved against the specified properties file or
Properties object (if any). Defines an EnvironmentAwarePropertyPlaceholderConfigurer within the context.
Properties object (if any). Defines an PropertySourcesPlaceholderConfigurer within the context.
For backward compatibility with versions earlier than Spring 3.1, a PropertyPlaceholderConfigurer will be
registered if the 'system-properties-mode' attribute has been explicitly assigned a value.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:exports
type="org.springframework.beans.factory.config.AbstractPropertyPlaceholderConfigurer" />
type="org.springframework.context.support.PropertySourcesPlaceholderConfigurer" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="propertyPlaceholder">
<xsd:attribute name="system-properties-mode" default="DELEGATE">
<xsd:annotation>
<xsd:documentation><![CDATA[
If set to any value other than DELEGATE, register a PropertyPlaceholderConfigurer bean and call
its setSystemPropertiesModeName() method with the value. If set to DELEGATE (the default), register
an EnvironmentAwarePropertyPlaceholderConfigurer and ignore the setting altogether, instead delegating
to the enclosing application context's Environment object and its collection of PropertySource objects
which may or may not include system properties and environment variables.
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="DELEGATE"/>
<xsd:enumeration value="NEVER"/>
<xsd:enumeration value="FALLBACK"/>
<xsd:enumeration value="OVERRIDE"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="property-override">

View File

@ -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 "";
}

View File

@ -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";
}

View File

@ -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();

View File

@ -40,7 +40,7 @@ import test.beans.TestBean;
* particularly convenient syntax requiring no extra artifact for the aspect.
*
* <p>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 <aop:aspectj-autoproxy/>} to enable
* annotation-config or component-scan), and thus will also use {@code <aop:aspectj-autoproxy/>} to enable
* processing of the Aspect annotation.
*
* @author Chris Beams

View File

@ -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()));

View File

@ -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"));

View File

@ -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"));
}
}

View File

@ -9,7 +9,7 @@ import org.springframework.util.ClassUtils;
/**
* Unit tests for {@link GenericXmlApplicationContext}.
*
*
* See SPR-7530.
*
* @author Chris Beams

View File

@ -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"));
}
}
}

View File

@ -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;
}
}

View File

@ -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:
* <pre>
* {@code
* PropertySource<?> source = new MockPropertySource().withProperty("foo", "bar");
* }
* </pre>
*
* @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;
}
}

View File

@ -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">
<util:properties id="placeholderProps">

View File

@ -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<String> activeProfiles = new LinkedHashSet<String>();
private Set<String> defaultProfiles = new LinkedHashSet<String>();
private LinkedList<PropertySource<?>> propertySources = new LinkedList<PropertySource<?>>();
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<String, String> propertiesMap) {
addPropertySource(new MapPropertySource(name, propertiesMap));
}
public LinkedList<PropertySource<?>> 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> T getProperty(String key, Class<T> 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> T getRequiredProperty(String key, Class<T> 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<String> getActiveProfiles() {
protected Set<String> 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<String> getDefaultProfiles() {
public String[] getDefaultProfiles() {
return this.doGetDefaultProfiles().toArray(new String[]{});
}
protected Set<String> 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<String> activeProfiles = this.doGetActiveProfiles();
Set<String> 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<String, String> getSystemEnvironment() {
Map<String,String> 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<String, String> 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<String> activeProfiles = this.getActiveProfiles();
Set<String> 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);
}
}

View File

@ -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> T getRequiredProperty(String key, Class<T> 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);
}
});
}
}

View File

@ -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<String, String> 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<PropertySource<?>> getPropertySources();
ConfigurablePropertyResolver getPropertyResolver();
}

View File

@ -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);
}

View File

@ -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.
* <p>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:
* <ol>
* <li>{@linkplain AbstractEnvironment#getSystemProperties() system properties}
* <li>{@linkplain AbstractEnvironment#getSystemEnvironment() system environment variables}
* </ol>
*
* 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.
*
* <p>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()}.
*
* <h4>Example: adding a new property source with highest search priority</h4>
* <pre class="code">
* ConfigurableEnvironment environment = new DefaultEnvironment();
* MutablePropertySources propertySources = environment.getPropertySources();
* Map<String, String> myMap = new HashMap<String, String>();
* myMap.put("xyz", "myValue");
* propertySources.addFirst(new MapPropertySource("MY_MAP", myMap));
* </pre>
*
* <h4>Example: removing the default system properties property source</h4>
* <pre class="code">
* MutablePropertySources propertySources = environment.getPropertySources();
* propertySources.remove(DefaultEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME)
* </pre>
*
* <h4>Example: mocking the system environment for testing purposes</h4>
* <pre class="code">
* MutablePropertySources propertySources = environment.getPropertySources();
* MockPropertySource mockEnvVars = new MockPropertySource().withProperty("xyz", "myValue");
* propertySources.replace(DefaultEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, mockEnvVars);
* </pre>
*
* When an {@link Environment} is being used by an ApplicationContext, it is important
* that any such PropertySource manipulations be performed <em>before</em> the context's {@link
* org.springframework.context.support.AbstractApplicationContext#refresh() refresh()} method is
* called. This ensures that all 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:
* <ul>
* <li>{@value #SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME}
* <li>{@value #SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME}
* </ul>
*
* <p>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()));
}
}

View File

@ -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:
* <ol>
* <li>profiles</li>
* <li>properties</li>
* </ol>
*
* A <em>profile</em> is a named, logical group of bean definitions to be registered with the
* container only if the given profile is <em>active</em>. 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}.
*
* <p><em>Properties</em> 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.
*
* <p>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.
*
* <p>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 <context:property-placeholder/>}.
*
* <p>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...)}.
*
* <p>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<String> 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<String> 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> T getProperty(String key, Class<T> targetType);
/**
* TODO SPR-7508: document
*/
String getRequiredProperty(String key);
/**
* TODO SPR-7508: document
*/
<T> T getRequiredProperty(String key, Class<T> 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)}.
*
* <p>Note that most {@link Environment} implementations will include this system environment map as
* a default {@link PropertySource} to be searched. Therefore, it is recommended that this method not be
* used directly unless bypassing other property sources is expressly intended.
*
* <p>Calls to {@link Map#get(Object)} on the Map returned will never throw {@link IllegalAccessException};
* in cases where the SecurityManager forbids access to a property, {@code null} will be returned and an
* INFO-level log message will be issued noting the exception.
*/
Map<String, String> 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)}.
*
* <p>Note that most {@code Environment} implementations will include this system properties map as a
* default {@link PropertySource} to be searched. Therefore, it is recommended that this method not be
* used directly unless bypassing other property sources is expressly intended.
*
* <p>Calls to {@link Map#get(Object)} on the Map returned will never throw {@link IllegalAccessException};
* in cases where the SecurityManager forbids access to a property, {@code null} will be returned and an
* INFO-level log message will be issued noting the exception.
*/
Map<String, String> 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);
}

View File

@ -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.
*
* <p>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.
*
* <p>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();
}

View File

@ -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<String,String>} object.
*
* @author Chris Beams
* @since 3.1
* @see PropertiesPropertySource
*/
public class MapPropertySource extends PropertySource<Map<String, String>> {
@ -37,17 +31,14 @@ public class MapPropertySource extends PropertySource<Map<String, String>> {
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);
}
}

View File

@ -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<PropertySource<?>> propertySourceList = new LinkedList<PropertySource<?>>();
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<PropertySource<?>> asList() {
return Collections.unmodifiableList(this.propertySourceList);
}
public PropertySource<?> get(String propertySourceName) {
return propertySourceList.get(propertySourceList.indexOf(PropertySource.named(propertySourceName)));
}
}

View File

@ -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())
* <p>Note that because a {@code Properties} object is technically an {@code <Object, Object>}
* {@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<Properties> {
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);
}
}

View File

@ -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> T getProperty(String key, Class<T> 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> T getRequiredProperty(String key, Class<T> 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;
}

View File

@ -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.
*
* <p>{@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}.
*
* <p>{@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<T> {
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:
* <ul>
* <li>they are the same instance
* <li>the {@code name} properties for both objects are equal
* </ul>
*
* <P>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<T> {
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<T> {
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.
*
* <p>Primarily for internal use, but given a collection of {@code PropertySource} objects, may be
* used as follows:
* <pre class="code">
* {@code
* List<PropertySource<?>> sources = new ArrayList<PropertySource<?>>();
* 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"));
* }
* </pre>
*
* <p>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<T> {
/**
* 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<Void>{
public static class StubPropertySource extends PropertySource<Object> {
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);
}
}
}

View File

@ -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<PropertySource<?>> asList();
int size();
boolean contains(String propertySourceName);
}

View File

@ -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> T getProperty(String key, Class<T> 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<PropertySource<?>> 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;
}
}

View File

@ -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<String, String>} implementation that is backed by system properties or environment
@ -38,32 +39,16 @@ abstract class ReadOnlySystemAttributesMap implements Map<String, String> {
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() {

View File

@ -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 <code>String</code> locations
* e.g. <code>"file:C:/myfile.txt"</code> or
* <code>"classpath:myfile.txt"</code>) to <code>Resource</code>
* properties instead of using a <code>String</code> 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.
*
* <p>The path may contain <code>${...}</code> placeholders, to be
* resolved as {@link Environment} properties: e.g. <code>${user.dir}</code>.
@ -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);
}

View File

@ -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);
}
}

View File

@ -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,E extends A> A[] toArray(Enumeration<E> enumeration, A[] array) {
ArrayList<A> elements = new ArrayList<A>();
while (enumeration.hasMoreElements()) {
elements.add(enumeration.nextElement());
}
return elements.toArray(array);
}
/**
* Adapt an enumeration to an iterator.
* @param enumeration the enumeration
* @return the iterator
*/

View File

@ -25,12 +25,6 @@ import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver;
* <code>${user.dir}</code>. 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

View File

@ -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<PropertySource<?>> 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<PropertySource<?>> 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<String, String> nullableProperties = new HashMap<String, String>();
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<String> 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<String, String> 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<String, String> 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<Collection<?>> isEmpty() {
return new TypeSafeMatcher<Collection<?>>() {
@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<String, String> getModifiableSystemEnvironment() throws Exception {
Class<?>[] classes = Collections.class.getDeclaredClasses();
Map<String, String> 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<String, String>) obj;
}
}
throw new IllegalStateException();
public void propertySourceOrder() {
ConfigurableEnvironment env = new DefaultEnvironment();
List<PropertySource<?>> 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));
}
}

View File

@ -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
}
}

View File

@ -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<String, String> 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<String, String> getModifiableSystemEnvironment() throws Exception {
Class<?>[] classes = Collections.class.getDeclaredClasses();
Map<String, String> 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<String, String>) obj;
}
}
throw new IllegalStateException();
}
}

View File

@ -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<String, String> 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<String, String> 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<String, String> getModifiableSystemEnvironment() throws Exception {
Class<?>[] classes = Collections.class.getDeclaredClasses();
Map<String, String> 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<String, String>) obj;
}
}
throw new IllegalStateException();
}
}

View File

@ -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<String, String> nullableProperties = new HashMap<String, String>();
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<String, String> map = new HashMap<String, String>();
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<String, String> map = new HashMap<String, String>();
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<Foo> {
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);
}
}

View File

@ -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<PropertySource<?>> propertySources = new LinkedList<PropertySource<?>>();
List<PropertySource<?>> propertySources = new ArrayList<PropertySource<?>>();
assertThat(propertySources.add(ps1), equalTo(true));
assertThat(propertySources.contains(ps1), is(true));
assertThat(propertySources.contains(PropertySource.named("ps1")), is(true));

View File

@ -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")));
}
}
}

View File

@ -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:
* <pre>
* {@code
* PropertySource<?> source = new MockPropertySource().withProperty("foo", "bar");
* }
* </pre>
*
* @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;
}
}

View File

@ -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:
* <pre>
* {@code
* PropertySource<?> source = new MockPropertySource().withProperty("foo", "bar");
* }
* </pre>
*
* @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;
}
}

View File

@ -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<PropertySource<?>> 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<PropertySource<?>> 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<PropertySource<?>> 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();

View File

@ -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);

View File

@ -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:
* <pre>
* {@code
* PropertySource<?> source = new MockPropertySource().withProperty("foo", "bar");
* }
* </pre>
*
* @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;
}
}

View File

@ -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);
}

View File

@ -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<String> requiredProperties = new HashSet<String>();
/**
* 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}
* <p>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.

View File

@ -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

View File

@ -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.
*
* <p>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:
* <ul>
* <li>{@value #PORTLET_CONFIG_PROPERTY_SOURCE_NAME}
* <li>{@value #PORTLET_CONTEXT_PROPERTY_SOURCE_NAME}
* <li>{@linkplain DefaultWebEnvironment#SERVLET_CONTEXT_PROPERTY_SOURCE_NAME "servletContextInitParams"}
* </ul>
* <p>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.
* <p>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));
}
}

View File

@ -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()

View File

@ -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<PortletConfig> {
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);
}
}

View File

@ -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<PortletContext> {
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);
}
}

View File

@ -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}
* <p>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);

View File

@ -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<String> requiredProperties = new HashSet<String>();
/**
* 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}
* <p>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.

View File

@ -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).
*
* <p>The {@link #setLocations "locations" property takes a list of Spring {@link Resource} locations
* <p>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.
*
* <p>Rather than being directly configured as a bean, this handler will typically be configured
* through use of the <code>&lt;mvc:resources/&gt;</code> XML configuration element.
* through use of the {@code <mvc:resources/>} XML configuration element.
*
* @author Keith Donald
* @author Jeremy Grelle

Some files were not shown because too many files have changed in this diff Show More