diff --git a/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java b/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java index eb2cd89b9a3..3a080b582e3 100644 --- a/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java +++ b/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java @@ -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 { 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 { } 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() + "]"); diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/PropertyPlaceholderConfigurer.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/PropertyPlaceholderConfigurer.java index b631968085e..57e6800ac76 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/PropertyPlaceholderConfigurer.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/PropertyPlaceholderConfigurer.java @@ -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 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); /** diff --git a/spring-core/src/main/java/org/springframework/core/SpringProperties.java b/spring-core/src/main/java/org/springframework/core/SpringProperties.java new file mode 100644 index 00000000000..fe6def84bea --- /dev/null +++ b/spring-core/src/main/java/org/springframework/core/SpringProperties.java @@ -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. + * + *
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. + * + *
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));
+ }
+
+}
diff --git a/spring-core/src/main/java/org/springframework/core/env/AbstractEnvironment.java b/spring-core/src/main/java/org/springframework/core/env/AbstractEnvironment.java
index a8a5458f575..197bff8e0c3 100644
--- a/spring-core/src/main/java/org/springframework/core/env/AbstractEnvironment.java
+++ b/spring-core/src/main/java/org/springframework/core/env/AbstractEnvironment.java
@@ -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 {
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 {
* 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 {
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;
}
diff --git a/spring-core/src/test/java/org/springframework/core/env/StandardEnvironmentTests.java b/spring-core/src/test/java/org/springframework/core/env/StandardEnvironmentTests.java
index 131917c8ccf..964ba389875 100644
--- a/spring-core/src/test/java/org/springframework/core/env/StandardEnvironmentTests.java
+++ b/spring-core/src/test/java/org/springframework/core/env/StandardEnvironmentTests.java
@@ -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.*;
* Unit tests for {@link StandardEnvironment}.
*
* @author Chris Beams
+ * @author Juergen Hoeller
*/
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 {
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 {
// 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 {
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 {
//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 {
if (obj != null && obj.getClass().getName().equals("java.lang.ProcessEnvironment$StringEnvironment")) {
return (Map