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
(cherry picked from commit 8543b91)
pull/465/head
Juergen Hoeller 12 years ago
parent
commit
866c7847e3
  1. 34
      spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java
  2. 3
      spring-beans/src/main/java/org/springframework/beans/factory/config/PropertyPlaceholderConfigurer.java
  3. 130
      spring-core/src/main/java/org/springframework/core/SpringProperties.java
  4. 23
      spring-core/src/main/java/org/springframework/core/env/AbstractEnvironment.java
  5. 64
      spring-core/src/test/java/org/springframework/core/env/StandardEnvironmentTests.java

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

@ -33,6 +33,7 @@ import java.util.WeakHashMap; @@ -33,6 +33,7 @@ import java.util.WeakHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.SpringProperties;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
@ -114,17 +115,7 @@ public class CachedIntrospectionResults { @@ -114,17 +115,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);
}
@ -293,16 +284,19 @@ public class CachedIntrospectionResults { @@ -293,16 +284,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() + "]");

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;
@ -369,15 +370,15 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment { @@ -369,15 +370,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;
}
@ -396,15 +397,7 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment { @@ -396,15 +397,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);
}
@SuppressWarnings("unchecked")
@ -415,15 +408,15 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment { @@ -415,15 +408,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;
}

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

@ -17,19 +17,17 @@ @@ -17,19 +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.mock.env.MockPropertySource;
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.*;
@ -38,6 +36,7 @@ import static org.springframework.core.env.AbstractEnvironment.*; @@ -38,6 +36,7 @@ import static org.springframework.core.env.AbstractEnvironment.*;
* Unit tests for {@link StandardEnvironment}.
*
* @author Chris Beams
* @author Juergen Hoeller
*/
public class StandardEnvironmentTests {
@ -60,15 +59,15 @@ public class StandardEnvironmentTests { @@ -60,15 +59,15 @@ public class StandardEnvironmentTests {
child.setActiveProfiles("c1", "c2");
child.getPropertySources().addLast(
new MockPropertySource("childMock")
.withProperty("childKey", "childVal")
.withProperty("bothKey", "childBothVal"));
.withProperty("childKey", "childVal")
.withProperty("bothKey", "childBothVal"));
ConfigurableEnvironment parent = new StandardEnvironment();
parent.setActiveProfiles("p1", "p2");
parent.getPropertySources().addLast(
new MockPropertySource("parentMock")
.withProperty("parentKey", "parentVal")
.withProperty("bothKey", "parentBothVal"));
.withProperty("parentKey", "parentVal")
.withProperty("bothKey", "parentBothVal"));
assertThat(child.getProperty("childKey"), is("childVal"));
assertThat(child.getProperty("parentKey"), nullValue());
@ -334,12 +333,27 @@ public class StandardEnvironmentTests { @@ -334,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);
@ -371,7 +385,7 @@ public class StandardEnvironmentTests { @@ -371,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
@ -402,7 +416,8 @@ public class StandardEnvironmentTests { @@ -402,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
}
}
@ -436,7 +451,7 @@ public class StandardEnvironmentTests { @@ -436,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));
}
}
};
@ -469,7 +484,8 @@ public class StandardEnvironmentTests { @@ -469,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);
}
}
@ -479,8 +495,9 @@ public class StandardEnvironmentTests { @@ -479,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 {
@ -488,10 +505,12 @@ public class StandardEnvironmentTests { @@ -488,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 {
@ -499,12 +518,15 @@ public class StandardEnvironmentTests { @@ -499,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