Browse Source

Introduced SpringProperties class and optional "spring.properties" file

This in particular allows for specifying "spring.getenv.ignore" and "spring.beaninfo.ignore" in a local way within the application, in case that JVM-level system properties are locked.

Issue: SPR-9014
Issue: SPR-11297
pull/445/merge
Juergen Hoeller 12 years ago
parent
commit
8543b91c50
  1. 7
      spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java
  2. 46
      spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java
  3. 3
      spring-beans/src/main/java/org/springframework/beans/factory/config/PropertyPlaceholderConfigurer.java
  4. 130
      spring-core/src/main/java/org/springframework/core/SpringProperties.java
  5. 23
      spring-core/src/main/java/org/springframework/core/env/AbstractEnvironment.java
  6. 55
      spring-core/src/test/java/org/springframework/core/env/StandardEnvironmentTests.java

7
spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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.
@ -492,20 +492,21 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra @@ -492,20 +492,21 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
public Object convertForProperty(Object value, String propertyName) throws TypeMismatchException {
CachedIntrospectionResults cachedIntrospectionResults = getCachedIntrospectionResults();
PropertyDescriptor pd = cachedIntrospectionResults.getPropertyDescriptor(propertyName);
TypeDescriptor td = cachedIntrospectionResults.getTypeDescriptor(pd);
if (pd == null) {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"No property '" + propertyName + "' found");
}
TypeDescriptor td = cachedIntrospectionResults.getTypeDescriptor(pd);
if (td == null) {
td = new TypeDescriptor(property(pd));
cachedIntrospectionResults.putTypeDescriptor(pd, td);
cachedIntrospectionResults.addTypeDescriptor(pd, td);
}
return convertForProperty(propertyName, null, value, pd, td);
}
private Object convertForProperty(String propertyName, Object oldValue, Object newValue, PropertyDescriptor pd, TypeDescriptor td)
throws TypeMismatchException {
return convertIfNecessary(propertyName, oldValue, newValue, pd.getPropertyType(), td);
}

46
spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java

@ -22,7 +22,6 @@ import java.beans.Introspector; @@ -22,7 +22,6 @@ import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
@ -30,10 +29,12 @@ import java.util.List; @@ -30,10 +29,12 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.SpringProperties;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.util.ClassUtils;
@ -116,17 +117,7 @@ public class CachedIntrospectionResults { @@ -116,17 +117,7 @@ public class CachedIntrospectionResults {
static {
boolean ignoreValue;
try {
ignoreValue = "true".equalsIgnoreCase(System.getProperty(IGNORE_BEANINFO_PROPERTY_NAME));
}
catch (Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("Could not obtain system property '" + IGNORE_BEANINFO_PROPERTY_NAME + "': " + ex);
}
ignoreValue = false;
}
shouldIntrospectorIgnoreBeaninfoClasses = ignoreValue;
shouldIntrospectorIgnoreBeaninfoClasses = SpringProperties.getFlag(IGNORE_BEANINFO_PROPERTY_NAME);
}
@ -298,16 +289,19 @@ public class CachedIntrospectionResults { @@ -298,16 +289,19 @@ public class CachedIntrospectionResults {
}
this.beanInfo = beanInfo;
// Immediately remove class from Introspector cache, to allow for proper
// garbage collection on class loader shutdown - we cache it here anyway,
// in a GC-friendly manner. In contrast to CachedIntrospectionResults,
// Introspector does not use WeakReferences as values of its WeakHashMap!
Class<?> classToFlush = beanClass;
do {
Introspector.flushFromCaches(classToFlush);
classToFlush = classToFlush.getSuperclass();
// Only bother with flushFromCaches if the Introspector actually cached...
if (!shouldIntrospectorIgnoreBeaninfoClasses) {
// Immediately remove class from Introspector cache, to allow for proper
// garbage collection on class loader shutdown - we cache it here anyway,
// in a GC-friendly manner. In contrast to CachedIntrospectionResults,
// Introspector does not use WeakReferences as values of its WeakHashMap!
Class<?> classToFlush = beanClass;
do {
Introspector.flushFromCaches(classToFlush);
classToFlush = classToFlush.getSuperclass();
}
while (classToFlush != null);
}
while (classToFlush != null);
if (logger.isTraceEnabled()) {
logger.trace("Caching PropertyDescriptors for class [" + beanClass.getName() + "]");
@ -332,7 +326,7 @@ public class CachedIntrospectionResults { @@ -332,7 +326,7 @@ public class CachedIntrospectionResults {
this.propertyDescriptorCache.put(pd.getName(), pd);
}
this.typeDescriptorCache = new HashMap<PropertyDescriptor, TypeDescriptor>();
this.typeDescriptorCache = new ConcurrentHashMap<PropertyDescriptor, TypeDescriptor>();
}
catch (IntrospectionException ex) {
throw new FatalBeanException("Failed to obtain BeanInfo for class [" + beanClass.getName() + "]", ex);
@ -381,12 +375,12 @@ public class CachedIntrospectionResults { @@ -381,12 +375,12 @@ public class CachedIntrospectionResults {
}
}
TypeDescriptor getTypeDescriptor(PropertyDescriptor pd) {
return this.typeDescriptorCache.get(pd);
void addTypeDescriptor(PropertyDescriptor pd, TypeDescriptor td) {
this.typeDescriptorCache.put(pd, td);
}
void putTypeDescriptor(PropertyDescriptor pd, TypeDescriptor td) {
this.typeDescriptorCache.put(pd, td);
TypeDescriptor getTypeDescriptor(PropertyDescriptor pd) {
return this.typeDescriptorCache.get(pd);
}
}

3
spring-beans/src/main/java/org/springframework/beans/factory/config/PropertyPlaceholderConfigurer.java

@ -21,6 +21,7 @@ import java.util.Set; @@ -21,6 +21,7 @@ import java.util.Set;
import org.springframework.beans.BeansException;
import org.springframework.core.Constants;
import org.springframework.core.SpringProperties;
import org.springframework.core.env.AbstractEnvironment;
import org.springframework.util.PropertyPlaceholderHelper;
import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver;
@ -84,7 +85,7 @@ public class PropertyPlaceholderConfigurer extends PlaceholderConfigurerSupport @@ -84,7 +85,7 @@ public class PropertyPlaceholderConfigurer extends PlaceholderConfigurerSupport
private int systemPropertiesMode = SYSTEM_PROPERTIES_MODE_FALLBACK;
private boolean searchSystemEnvironment =
!"true".equalsIgnoreCase(System.getProperty(AbstractEnvironment.IGNORE_GETENV_PROPERTY_NAME));
!SpringProperties.getFlag(AbstractEnvironment.IGNORE_GETENV_PROPERTY_NAME);
/**

130
spring-core/src/main/java/org/springframework/core/SpringProperties.java

@ -0,0 +1,130 @@ @@ -0,0 +1,130 @@
/*
* Copyright 2002-2013 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;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Static holder for local Spring properties, i.e. defined at the Spring library level.
*
* <p>Reads a {@code spring.properties} file from the root of the Spring library classpath,
* and also allows for programmatically setting properties through {@link #setProperty}.
* When checking a property, local entries are being checked first, then falling back
* to JVM-level system properties through a {@link System#getProperty} check.
*
* <p>This is an alternative way to set Spring-related system properties such as
* "spring.getenv.ignore" and "spring.beaninfo.ignore", in particular for scenarios
* where JVM system properties are locked on the target platform (e.g. WebSphere).
* See {@link #setFlag} for a convenient way to locally set such flags to "true".
*
* @author Juergen Hoeller
* @since 3.2.7
* @see org.springframework.core.env.AbstractEnvironment#IGNORE_GETENV_PROPERTY_NAME
* @see org.springframework.beans.CachedIntrospectionResults#IGNORE_BEANINFO_PROPERTY_NAME
*/
public abstract class SpringProperties {
private static final Log logger = LogFactory.getLog(SpringProperties.class);
private static final Properties localProperties = new Properties();
static {
try {
ClassLoader cl = SpringProperties.class.getClassLoader();
URL url = cl.getResource("spring.properties");
if (url != null) {
logger.info("Found 'spring.properties' file in local classpath");
InputStream is = url.openStream();
try {
localProperties.load(is);
}
finally {
is.close();
}
}
}
catch (IOException ex) {
if (logger.isInfoEnabled()) {
logger.info("Could not load 'spring.properties' file from local classpath: " + ex);
}
}
}
/**
* Programmatically set a local property, overriding an entry in the
* {@code spring.properties} file (if any).
* @param key the property key
* @param value the associated property value, or {@code null} to reset it
*/
public static void setProperty(String key, String value) {
if (value != null) {
localProperties.setProperty(key, value);
}
else {
localProperties.remove(key);
}
}
/**
* Retrieve the property value for the given key, checking local Spring
* properties first and falling back to JVM-level system properties.
* @param key the property key
* @return the associated property value, or {@code null} if none found
*/
public static String getProperty(String key) {
String value = localProperties.getProperty(key);
if (value == null) {
try {
value = System.getProperty(key);
}
catch (Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("Could not retrieve system property '" + key + "': " + ex);
}
}
}
return value;
}
/**
* Programmatically set a local flag to "true", overriding an
* entry in the {@code spring.properties} file (if any).
* @param key the property key
*/
public static void setFlag(String key) {
localProperties.put(key, Boolean.TRUE.toString());
}
/**
* Retrieve the flag for the given property key.
* @param key the property key
* @return {@code true} if the property is set to "true",
* {@code} false otherwise
*/
public static boolean getFlag(String key) {
return Boolean.parseBoolean(getProperty(key));
}
}

23
spring-core/src/main/java/org/springframework/core/env/AbstractEnvironment.java vendored

@ -25,6 +25,7 @@ import java.util.Set; @@ -25,6 +25,7 @@ import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.SpringProperties;
import org.springframework.core.convert.support.ConfigurableConversionService;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@ -377,15 +378,15 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment { @@ -377,15 +378,15 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment {
catch (AccessControlException ex) {
return (Map) new ReadOnlySystemAttributesMap() {
@Override
protected String getSystemAttribute(String variableName) {
protected String getSystemAttribute(String attributeName) {
try {
return System.getenv(variableName);
return System.getenv(attributeName);
}
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()));
attributeName, ex.getMessage()));
}
return null;
}
@ -404,15 +405,7 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment { @@ -404,15 +405,7 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment {
* returning {@code true} if its value equals "true" in any case.
*/
protected boolean suppressGetenvAccess() {
try {
return "true".equalsIgnoreCase(System.getProperty(IGNORE_GETENV_PROPERTY_NAME));
}
catch (Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("Could not obtain system property '" + IGNORE_GETENV_PROPERTY_NAME + "': " + ex);
}
return false;
}
return SpringProperties.getFlag(IGNORE_GETENV_PROPERTY_NAME);
}
@Override
@ -424,15 +417,15 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment { @@ -424,15 +417,15 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment {
catch (AccessControlException ex) {
return (Map) new ReadOnlySystemAttributesMap() {
@Override
protected String getSystemAttribute(String propertyName) {
protected String getSystemAttribute(String attributeName) {
try {
return System.getProperty(propertyName);
return System.getProperty(attributeName);
}
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()));
attributeName, ex.getMessage()));
}
return null;
}

55
spring-core/src/test/java/org/springframework/core/env/StandardEnvironmentTests.java vendored

@ -17,18 +17,17 @@ @@ -17,18 +17,17 @@
package org.springframework.core.env;
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.core.SpringProperties;
import org.springframework.mock.env.MockPropertySource;
import static java.lang.String.*;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.springframework.core.env.AbstractEnvironment.*;
@ -37,6 +36,7 @@ import static org.springframework.core.env.AbstractEnvironment.*; @@ -37,6 +36,7 @@ import static org.springframework.core.env.AbstractEnvironment.*;
* Unit tests for {@link StandardEnvironment}.
*
* @author Chris Beams
* @author Juergen Hoeller
*/
public class StandardEnvironmentTests {
@ -333,12 +333,27 @@ public class StandardEnvironmentTests { @@ -333,12 +333,27 @@ public class StandardEnvironmentTests {
try {
env.addActiveProfile("invalid-profile");
fail("expected validation exception");
} catch (IllegalArgumentException ex) {
}
catch (IllegalArgumentException ex) {
assertThat(ex.getMessage(),
equalTo("Invalid profile [invalid-profile]: must not contain dash character"));
}
}
@Test
public void suppressGetenvAccessThroughSystemProperty() {
System.setProperty("spring.getenv.ignore", "true");
assertTrue(environment.getSystemEnvironment().isEmpty());
System.clearProperty("spring.getenv.ignore");
}
@Test
public void suppressGetenvAccessThroughSpringProperty() {
SpringProperties.setProperty("spring.getenv.ignore", "true");
assertTrue(environment.getSystemEnvironment().isEmpty());
SpringProperties.setProperty("spring.getenv.ignore", null);
}
@Test
public void getSystemProperties_withAndWithoutSecurityManager() {
System.setProperty(ALLOWED_PROPERTY_NAME, ALLOWED_PROPERTY_VALUE);
@ -370,7 +385,7 @@ public class StandardEnvironmentTests { @@ -370,7 +385,7 @@ public class StandardEnvironmentTests {
// 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));
String.format("Accessing the system property [%s] is disallowed", DISALLOWED_PROPERTY_NAME));
}
}
@Override
@ -401,7 +416,8 @@ public class StandardEnvironmentTests { @@ -401,7 +416,8 @@ public class StandardEnvironmentTests {
try {
systemProperties.get(NON_STRING_PROPERTY_NAME);
fail("Expected IllegalArgumentException when searching with non-string key against ReadOnlySystemAttributesMap");
} catch (IllegalArgumentException ex) {
}
catch (IllegalArgumentException ex) {
// expected
}
}
@ -435,7 +451,7 @@ public class StandardEnvironmentTests { @@ -435,7 +451,7 @@ public class StandardEnvironmentTests {
//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));
String.format("Accessing the system environment variable [%s] is disallowed", DISALLOWED_PROPERTY_NAME));
}
}
};
@ -468,7 +484,8 @@ public class StandardEnvironmentTests { @@ -468,7 +484,8 @@ public class StandardEnvironmentTests {
if (obj != null && obj.getClass().getName().equals("java.lang.ProcessEnvironment$StringEnvironment")) {
return (Map<String, String>) obj;
}
} catch (Exception ex) {
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
}
@ -478,8 +495,9 @@ public class StandardEnvironmentTests { @@ -478,8 +495,9 @@ public class StandardEnvironmentTests {
Class<?> processEnvironmentClass;
try {
processEnvironmentClass = Class.forName("java.lang.ProcessEnvironment");
} catch (Exception e) {
throw new RuntimeException(e);
}
catch (Exception ex) {
throw new IllegalStateException(ex);
}
try {
@ -487,10 +505,12 @@ public class StandardEnvironmentTests { @@ -487,10 +505,12 @@ public class StandardEnvironmentTests {
theCaseInsensitiveEnvironmentField.setAccessible(true);
Object obj = theCaseInsensitiveEnvironmentField.get(null);
return (Map<String, String>) obj;
} catch (NoSuchFieldException e) {
}
catch (NoSuchFieldException ex) {
// do nothing
} catch (Exception e) {
throw new RuntimeException(e);
}
catch (Exception ex) {
throw new IllegalStateException(ex);
}
try {
@ -498,12 +518,15 @@ public class StandardEnvironmentTests { @@ -498,12 +518,15 @@ public class StandardEnvironmentTests {
theEnvironmentField.setAccessible(true);
Object obj = theEnvironmentField.get(null);
return (Map<String, String>) obj;
} catch (NoSuchFieldException e) {
}
catch (NoSuchFieldException ex) {
// do nothing
} catch (Exception e) {
throw new RuntimeException(e);
}
catch (Exception ex) {
throw new IllegalStateException(ex);
}
throw new IllegalStateException();
}
}

Loading…
Cancel
Save