Merge 3.1.0 development branch into trunk

Branch in question is 'env' branch from git://git.springsource.org/sandbox/cbeams.git; merged into
git-svn repository with:

    git merge -s recursive -Xtheirs --no-commit env

No merge conflicts, but did need to

    git rm spring-build

prior to committing.

With this change, Spring 3.1.0 development is now happening on SVN
trunk. Further commits to the 3.0.x line will happen in an as-yet
uncreated SVN branch.  3.1.0 snapshots will be available
per the usual nightly CI build from trunk.
This commit is contained in:
Chris Beams
2010-10-25 19:48:20 +00:00
parent b0ea2b13d2
commit f480333d31
211 changed files with 9876 additions and 623 deletions

View File

@@ -0,0 +1,284 @@
/*
* 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.StringUtils.commaDelimitedListToSet;
import static org.springframework.util.StringUtils.trimAllWhitespace;
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 java.security.AccessControlException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
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;
/**
* TODO SPR-7508: document
*
* @author Chris Beams
* @since 3.1
*/
public abstract class AbstractEnvironment implements ConfigurableEnvironment {
public static final String SPRING_PROFILES_PROPERTY_NAME = "springProfiles";
protected final Log logger = LogFactory.getLog(getClass());
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 Set<String> activeProfiles = new LinkedHashSet<String>();
private LinkedList<PropertySource<?>> propertySources = new LinkedList<PropertySource<?>>();
private ConversionService conversionService = ConversionServiceFactory.createDefaultConversionService();
private boolean explicitlySetProfiles;
public void addPropertySource(PropertySource<?> propertySource) {
propertySources.push(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();
Iterator<PropertySource<?>> descendingIterator = getPropertySources().descendingIterator();
while (descendingIterator.hasNext()) {
PropertySource<?> propertySource = descendingIterator.next();
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() {
doGetProfiles();
return Collections.unmodifiableSet(activeProfiles);
}
private void doGetProfiles() {
if (explicitlySetProfiles)
return;
String profiles = getProperty(SPRING_PROFILES_PROPERTY_NAME);
if (profiles == null || profiles.equals("")) {
return;
}
this.activeProfiles = commaDelimitedListToSet(trimAllWhitespace(profiles));
}
public void setActiveProfiles(String... profiles) {
explicitlySetProfiles = true;
this.activeProfiles.clear();
this.activeProfiles.addAll(Arrays.asList(profiles));
}
public Map<String, String> getSystemEnvironment() {
Map<String,String> systemEnvironment;
try {
systemEnvironment = System.getenv();
}
catch (AccessControlException ex) {
systemEnvironment = new ReadOnlySystemAttributesMap() {
@Override
protected String getSystemAttribute(String variableName) {
try {
return System.getenv(variableName);
}
catch (AccessControlException ex) {
if (logger.isInfoEnabled()) {
logger.info(format("Caught AccessControlException when accessing system environment variable " +
"[%s]; its value will be returned [null]. Reason: %s", variableName, ex.getMessage()));
}
return null;
}
}
};
}
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;
try {
systemProperties = System.getProperties();
}
catch (AccessControlException ex) {
systemProperties = new ReadOnlySystemAttributesMap() {
@Override
protected String getSystemAttribute(String propertyName) {
try {
return System.getProperty(propertyName);
}
catch (AccessControlException ex) {
if (logger.isInfoEnabled()) {
logger.info(format("Caught AccessControlException when accessing system property " +
"[%s]; its value will be returned [null]. Reason: %s", propertyName, ex.getMessage()));
}
return null;
}
}
};
}
return systemProperties;
}
public String resolvePlaceholders(String text) {
return doResolvePlaceholders(text, nonStrictHelper);
}
public String resolveRequiredPlaceholders(String text) {
return doResolvePlaceholders(text, strictHelper);
}
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 getClass().getSimpleName() + " [activeProfiles=" + activeProfiles
+ ", propertySources=" + propertySources + "]";
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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;
/**
* TODO SPR-7508: document
*
* @author Chris Beams
* @since 3.1
*/
public interface ConfigurableEnvironment extends Environment, PropertySourceAggregator {
/**
* TODO SPR-7508: document
*/
void setActiveProfiles(String... profiles);
}

View File

@@ -0,0 +1,39 @@
/*
* 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;
/**
* TODO SPR-7508: document
*
* Explain why the default ordering of property sources is the way it is.
*
* @author Chris Beams
* @since 3.1
*/
public class DefaultEnvironment extends AbstractEnvironment {
public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";
public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";
public DefaultEnvironment() {
addPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment());
addPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties());
}
}

View File

@@ -0,0 +1,31 @@
/*
* 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;
/**
* 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";
}

View File

@@ -0,0 +1,101 @@
/*
* 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.Map;
import java.util.Properties;
import java.util.Set;
/**
* TODO SPR-7508: document
* TODO: Consider extracting a PropertyResolutionService interface
*
* @author Chris Beams
* @since 3.1
*/
public interface Environment {
/**
* TODO SPR-7508: document
*/
Set<String> getActiveProfiles();
/**
* TODO SPR-7508: document
*/
boolean containsProperty(String key);
/**
* TODO SPR-7508: document
*/
String getProperty(String key);
/**
* 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.
*/
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()}
*/
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

@@ -0,0 +1,32 @@
/*
* 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;
/**
* TODO SPR-7508: document
*
* @author Chris Beams
* @since 3.1
* @see Environment
* @see ConfigurableEnvironmentCapable
*/
public interface EnvironmentCapable {
Environment getEnvironment();
}

View File

@@ -0,0 +1,53 @@
/*
* 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.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.
*
* @author Chris Beams
* @since 3.1
*/
public class MapPropertySource extends PropertySource<Map<String, String>> {
protected MapPropertySource(String name, Map<String, String> source) {
super(name, source);
}
public boolean containsProperty(String key) {
return source.containsKey(key);
}
public String getProperty(String key) {
return source.get(key);
}
@Override
public int size() {
return source.size();
}
}

View File

@@ -0,0 +1,54 @@
/*
* 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;
/**
* 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.
*
* Consider adding a TypeConvertingPropertiesPropertySource to accommodate
* non-string keys and values (such as is technically possible with
* System.getProperties())
*
* @author Chris Beams
* @since 3.1
*/
public class PropertiesPropertySource extends PropertySource<Properties> {
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();
}
}

View File

@@ -0,0 +1,139 @@
/*
* 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.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public abstract class PropertySource<T> {
protected final Log logger = LogFactory.getLog(this.getClass());
protected final String name;
protected final T source;
public PropertySource(String name, T source) {
this.name = name;
this.source = source;
}
public String getName() {
return name;
}
public T getSource() {
return source;
}
public abstract boolean containsProperty(String key);
public abstract String getProperty(String key);
public abstract int size();
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof PropertySource))
return false;
PropertySource<?> other = (PropertySource<?>) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
/**
* Produce concise output (type, name, and number of properties) if the current log level does
* not include debug. If debug is enabled, produce verbose output including hashcode of the
* PropertySource instance and every key/value property pair.
*
* This variable verbosity is useful as a property source such as system properties
* or environment variables may contain an arbitrary number of property pairs, potentially
* leading to difficult to read exception and log messages.
*
* @see Log#isDebugEnabled()
*/
@Override
public String toString() {
if (logger.isDebugEnabled()) {
return String.format("%s@%s [name='%s', properties=%s]",
getClass().getSimpleName(), System.identityHashCode(this), name, source);
}
return String.format("%s [name='%s', propertyCount=%d]",
getClass().getSimpleName(), name, this.size());
}
/**
* For collection comparison purposes
* TODO SPR-7508: document
*/
public static PropertySource<?> named(String name) {
return new ComparisonPropertySource(name);
}
/**
* TODO: SPR-7508: document
*/
public static class ComparisonPropertySource extends PropertySource<Void>{
private static final String USAGE_ERROR =
"ComparisonPropertySource instances are for collection comparison " +
"use only";
public ComparisonPropertySource(String name) {
super(name, null);
}
@Override
public Void getSource() {
throw new UnsupportedOperationException(USAGE_ERROR);
}
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);
}
}
}

View File

@@ -0,0 +1,51 @@
/*
* 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.LinkedList;
import java.util.Map;
import java.util.Properties;
/**
* TODO: SPR-7508 document
*
* @author Chris Beams
* @since 3.1
*/
public interface PropertySourceAggregator {
void addPropertySource(PropertySource<?> propertySource);
void addPropertySource(String name, Properties properties);
void addPropertySource(String name, Map<String, String> propertiesMap);
/**
* 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.
*/
LinkedList<PropertySource<?>> getPropertySources();
}

View File

@@ -0,0 +1,118 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* 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.Collection;
import java.util.Map;
import java.util.Set;
/**
* Read-only {@code Map<String, String>} implementation that is backed by system properties or environment
* variables.
*
* <p>Used by {@link AbstractApplicationContext} when a {@link SecurityManager} prohibits access to {@link
* System#getProperties()} or {@link System#getenv()}.
*
* @author Arjen Poutsma
* @author Chris Beams
* @since 3.0
*/
abstract class ReadOnlySystemAttributesMap implements Map<String, String> {
public boolean containsKey(Object key) {
return get(key) != null;
}
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");
}
}
public boolean isEmpty() {
return false;
}
/**
* Template method that returns the underlying system attribute.
*
* <p>Implementations typically call {@link System#getProperty(String)} or {@link System#getenv(String)} here.
*/
protected abstract String getSystemAttribute(String attributeName);
// Unsupported
public int size() {
throw new UnsupportedOperationException();
}
public String put(String key, String value) {
throw new UnsupportedOperationException();
}
public boolean containsValue(Object value) {
throw new UnsupportedOperationException();
}
public String remove(Object key) {
throw new UnsupportedOperationException();
}
public void clear() {
throw new UnsupportedOperationException();
}
public Set<String> keySet() {
throw new UnsupportedOperationException();
}
public void putAll(Map<? extends String, ? extends String> m) {
throw new UnsupportedOperationException();
}
public Collection<String> values() {
throw new UnsupportedOperationException();
}
public Set<Entry<String, String>> entrySet() {
throw new UnsupportedOperationException();
}
}

View File

@@ -19,9 +19,11 @@ package org.springframework.core.io;
import java.beans.PropertyEditorSupport;
import java.io.IOException;
import org.springframework.core.env.DefaultEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.util.SystemPropertyUtils;
/**
* {@link java.beans.PropertyEditor Editor} for {@link Resource}
@@ -30,24 +32,26 @@ import org.springframework.util.SystemPropertyUtils;
* <code>"classpath:myfile.txt"</code>) to <code>Resource</code>
* properties instead of using a <code>String</code> location property.
*
* <p>The path may contain <code>${...}</code> placeholders,
* to be resolved as system properties: e.g. <code>${user.dir}</code>.
* Unresolvable placeholder are ignored by default.
* <p>The path may contain <code>${...}</code> placeholders, to be
* resolved as {@link Environment} properties: e.g. <code>${user.dir}</code>.
* Unresolvable placeholders are ignored by default.
*
* <p>Delegates to a {@link ResourceLoader} to do the heavy lifting,
* by default using a {@link DefaultResourceLoader}.
*
* @author Juergen Hoeller
* @author Dave Syer
* @author Chris Beams
* @since 28.12.2003
* @see Resource
* @see ResourceLoader
* @see DefaultResourceLoader
* @see org.springframework.util.SystemPropertyUtils#resolvePlaceholders
* @see System#getProperty(String)
* @see org.springframework.env.Environment#resolvePlaceholders
*/
public class ResourceEditor extends PropertyEditorSupport {
private final Environment environment;
private final ResourceLoader resourceLoader;
private final boolean ignoreUnresolvablePlaceholders;
@@ -55,31 +59,60 @@ public class ResourceEditor extends PropertyEditorSupport {
/**
* Create a new instance of the {@link ResourceEditor} class
* using a {@link DefaultResourceLoader}.
* using a {@link DefaultResourceLoader} and {@link DefaultEnvironment}.
*/
public ResourceEditor() {
this(new DefaultResourceLoader());
this(new DefaultResourceLoader(), new DefaultEnvironment());
}
/**
* Create a new instance of the {@link ResourceEditor} class
* using the given {@link ResourceLoader}.
* using the given {@link ResourceLoader} and a {@link DefaultEnvironment}.
* @param resourceLoader the <code>ResourceLoader</code> to use
* @deprecated as of Spring 3.1 in favor of
* {@link #ResourceEditor(ResourceLoader, Environment)}
*/
@Deprecated
public ResourceEditor(ResourceLoader resourceLoader) {
this(resourceLoader, new DefaultEnvironment(), true);
}
/**
* Create a new instance of the {@link ResourceEditor} class
* using the given {@link ResourceLoader} and {@link Environment}.
* @param resourceLoader the <code>ResourceLoader</code> to use
*/
public ResourceEditor(ResourceLoader resourceLoader) {
this(resourceLoader, true);
public ResourceEditor(ResourceLoader resourceLoader, Environment environment) {
this(resourceLoader, environment, true);
}
/**
* Create a new instance of the {@link ResourceEditor} class
* using the given {@link ResourceLoader}.
* using the given {@link ResourceLoader} and a {@link DefaultEnvironment}.
* @param resourceLoader the <code>ResourceLoader</code> to use
* @param ignoreUnresolvablePlaceholders whether to ignore unresolvable placeholders
* if no corresponding system property could be found
* if no corresponding property could be found in the DefaultEnvironment
* @deprecated as of Spring 3.1 in favor of
* {@link #ResourceEditor(ResourceLoader, Environment, boolean)}
*/
@Deprecated
public ResourceEditor(ResourceLoader resourceLoader, boolean ignoreUnresolvablePlaceholders) {
this(resourceLoader, new DefaultEnvironment(), ignoreUnresolvablePlaceholders);
}
/**
* Create a new instance of the {@link ResourceEditor} class
* using the given {@link ResourceLoader}.
* @param resourceLoader the <code>ResourceLoader</code> to use
* @param environment the <code>Environment</code> to use
* @param ignoreUnresolvablePlaceholders whether to ignore unresolvable placeholders
* if no corresponding property could be found in the given <code>environment</code>
*/
public ResourceEditor(ResourceLoader resourceLoader, Environment environment, boolean ignoreUnresolvablePlaceholders) {
Assert.notNull(resourceLoader, "ResourceLoader must not be null");
Assert.notNull(environment, "Environment must not be null");
this.resourceLoader = resourceLoader;
this.environment = environment;
this.ignoreUnresolvablePlaceholders = ignoreUnresolvablePlaceholders;
}
@@ -96,14 +129,17 @@ public class ResourceEditor extends PropertyEditorSupport {
}
/**
* Resolve the given path, replacing placeholders with
* corresponding system property values if necessary.
* Resolve the given path, replacing placeholders with corresponding
* property values from the <code>environment</code> if necessary.
* @param path the original file path
* @return the resolved file path
* @see org.springframework.util.SystemPropertyUtils#resolvePlaceholders
* @see Environment#resolvePlaceholders
* @see Environment#resolveRequiredPlaceholders
*/
protected String resolvePath(String path) {
return SystemPropertyUtils.resolvePlaceholders(path, this.ignoreUnresolvablePlaceholders);
return this.ignoreUnresolvablePlaceholders ?
environment.resolvePlaceholders(path) :
environment.resolveRequiredPlaceholders(path);
}

View File

@@ -23,7 +23,6 @@ import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.Resource;
import org.springframework.util.CollectionUtils;
import org.springframework.util.DefaultPropertiesPersister;
@@ -45,12 +44,12 @@ public abstract class PropertiesLoaderSupport {
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
private Properties[] localProperties;
protected Properties[] localProperties;
protected boolean localOverride = false;
private Resource[] locations;
private boolean localOverride = false;
private boolean ignoreResourceNotFound = false;
private String fileEncoding;

View File

@@ -25,9 +25,9 @@ import java.util.List;
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.io.Resource;
import org.springframework.util.SystemPropertyUtils;
/**
* Editor for {@link org.springframework.core.io.Resource} arrays, to
@@ -44,17 +44,19 @@ import org.springframework.util.SystemPropertyUtils;
* by default using a {@link PathMatchingResourcePatternResolver}.
*
* @author Juergen Hoeller
* @author Chris Beams
* @since 1.1.2
* @see org.springframework.core.io.Resource
* @see ResourcePatternResolver
* @see PathMatchingResourcePatternResolver
* @see org.springframework.util.SystemPropertyUtils#resolvePlaceholders
* @see System#getProperty(String)
* @see org.springframework.env.Environment#resolvePlaceholders
*/
public class ResourceArrayPropertyEditor extends PropertyEditorSupport {
private static final Log logger = LogFactory.getLog(ResourceArrayPropertyEditor.class);
private final Environment environment;
private final ResourcePatternResolver resourcePatternResolver;
private final boolean ignoreUnresolvablePlaceholders;
@@ -62,29 +64,59 @@ public class ResourceArrayPropertyEditor extends PropertyEditorSupport {
/**
* Create a new ResourceArrayPropertyEditor with a default
* PathMatchingResourcePatternResolver.
* {@link PathMatchingResourcePatternResolver} and {@link DefaultEnvironment}.
* @see PathMatchingResourcePatternResolver
* @see Environment
*/
public ResourceArrayPropertyEditor() {
this(new PathMatchingResourcePatternResolver());
this(new PathMatchingResourcePatternResolver(), new DefaultEnvironment(), true);
}
/**
* Create a new ResourceArrayPropertyEditor with the given ResourcePatternResolver.
* Create a new ResourceArrayPropertyEditor with the given {@link ResourcePatternResolver}
* and a {@link DefaultEnvironment}.
* @param resourcePatternResolver the ResourcePatternResolver to use
* @deprecated as of 3.1 in favor of {@link #ResourceArrayPropertyEditor(ResourcePatternResolver, Environment)}
*/
@Deprecated
public ResourceArrayPropertyEditor(ResourcePatternResolver resourcePatternResolver) {
this(resourcePatternResolver, true);
this(resourcePatternResolver, new DefaultEnvironment(), true);
}
/**
* Create a new ResourceArrayPropertyEditor with the given ResourcePatternResolver.
* Create a new ResourceArrayPropertyEditor with the given {@link ResourcePatternResolver}
* and {@link Environment}.
* @param resourcePatternResolver the ResourcePatternResolver to use
* @param environment the Environment to use
*/
public ResourceArrayPropertyEditor(ResourcePatternResolver resourcePatternResolver, Environment environment) {
this(resourcePatternResolver, environment, true);
}
/**
* Create a new ResourceArrayPropertyEditor with the given {@link ResourcePatternResolver}
* and a {@link DefaultEnvironment}.
* @param resourcePatternResolver the ResourcePatternResolver to use
* @param ignoreUnresolvablePlaceholders whether to ignore unresolvable placeholders
* if no corresponding system property could be found
* @deprecated as of 3.1 in favor of {@link #ResourceArrayPropertyEditor(ResourcePatternResolver, Environment, boolean)}
*/
@Deprecated
public ResourceArrayPropertyEditor(ResourcePatternResolver resourcePatternResolver, boolean ignoreUnresolvablePlaceholders) {
this(resourcePatternResolver, new DefaultEnvironment(), ignoreUnresolvablePlaceholders);
}
/**
* Create a new ResourceArrayPropertyEditor with the given {@link ResourcePatternResolver}
* and {@link Environment}.
* @param resourcePatternResolver the ResourcePatternResolver to use
* @param environment the Environment to use
* @param ignoreUnresolvablePlaceholders whether to ignore unresolvable placeholders
* if no corresponding system property could be found
*/
public ResourceArrayPropertyEditor(ResourcePatternResolver resourcePatternResolver, Environment environment, boolean ignoreUnresolvablePlaceholders) {
this.resourcePatternResolver = resourcePatternResolver;
this.environment = environment;
this.ignoreUnresolvablePlaceholders = ignoreUnresolvablePlaceholders;
}
@@ -160,10 +192,13 @@ public class ResourceArrayPropertyEditor extends PropertyEditorSupport {
* corresponding system property values if necessary.
* @param path the original file path
* @return the resolved file path
* @see org.springframework.util.SystemPropertyUtils#resolvePlaceholders
* @see Environment#resolvePlaceholders
* @see Environment#resolveRequiredPlaceholders
*/
protected String resolvePath(String path) {
return SystemPropertyUtils.resolvePlaceholders(path, this.ignoreUnresolvablePlaceholders);
return this.ignoreUnresolvablePlaceholders ?
environment.resolvePlaceholders(path) :
environment.resolveRequiredPlaceholders(path);
}
}

View File

@@ -25,6 +25,12 @@ 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