Skip to content

Commit 8543b91

Browse files
committed
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
1 parent 6151e6d commit 8543b91

6 files changed

Lines changed: 203 additions & 61 deletions

File tree

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2013 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -492,20 +492,21 @@ private Object convertIfNecessary(String propertyName, Object oldValue, Object n
492492
public Object convertForProperty(Object value, String propertyName) throws TypeMismatchException {
493493
CachedIntrospectionResults cachedIntrospectionResults = getCachedIntrospectionResults();
494494
PropertyDescriptor pd = cachedIntrospectionResults.getPropertyDescriptor(propertyName);
495-
TypeDescriptor td = cachedIntrospectionResults.getTypeDescriptor(pd);
496495
if (pd == null) {
497496
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
498497
"No property '" + propertyName + "' found");
499498
}
499+
TypeDescriptor td = cachedIntrospectionResults.getTypeDescriptor(pd);
500500
if (td == null) {
501501
td = new TypeDescriptor(property(pd));
502-
cachedIntrospectionResults.putTypeDescriptor(pd, td);
502+
cachedIntrospectionResults.addTypeDescriptor(pd, td);
503503
}
504504
return convertForProperty(propertyName, null, value, pd, td);
505505
}
506506

507507
private Object convertForProperty(String propertyName, Object oldValue, Object newValue, PropertyDescriptor pd, TypeDescriptor td)
508508
throws TypeMismatchException {
509+
509510
return convertIfNecessary(propertyName, oldValue, newValue, pd.getPropertyType(), td);
510511
}
511512

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

Lines changed: 20 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,19 @@
2222
import java.beans.PropertyDescriptor;
2323
import java.lang.ref.Reference;
2424
import java.lang.ref.WeakReference;
25-
import java.util.HashMap;
2625
import java.util.HashSet;
2726
import java.util.Iterator;
2827
import java.util.LinkedHashMap;
2928
import java.util.List;
3029
import java.util.Map;
3130
import java.util.Set;
3231
import java.util.WeakHashMap;
32+
import java.util.concurrent.ConcurrentHashMap;
3333

3434
import org.apache.commons.logging.Log;
3535
import org.apache.commons.logging.LogFactory;
3636

37+
import org.springframework.core.SpringProperties;
3738
import org.springframework.core.convert.TypeDescriptor;
3839
import org.springframework.core.io.support.SpringFactoriesLoader;
3940
import org.springframework.util.ClassUtils;
@@ -116,17 +117,7 @@ public class CachedIntrospectionResults {
116117

117118

118119
static {
119-
boolean ignoreValue;
120-
try {
121-
ignoreValue = "true".equalsIgnoreCase(System.getProperty(IGNORE_BEANINFO_PROPERTY_NAME));
122-
}
123-
catch (Throwable ex) {
124-
if (logger.isDebugEnabled()) {
125-
logger.debug("Could not obtain system property '" + IGNORE_BEANINFO_PROPERTY_NAME + "': " + ex);
126-
}
127-
ignoreValue = false;
128-
}
129-
shouldIntrospectorIgnoreBeaninfoClasses = ignoreValue;
120+
shouldIntrospectorIgnoreBeaninfoClasses = SpringProperties.getFlag(IGNORE_BEANINFO_PROPERTY_NAME);
130121
}
131122

132123

@@ -298,16 +289,19 @@ private CachedIntrospectionResults(Class<?> beanClass) throws BeansException {
298289
}
299290
this.beanInfo = beanInfo;
300291

301-
// Immediately remove class from Introspector cache, to allow for proper
302-
// garbage collection on class loader shutdown - we cache it here anyway,
303-
// in a GC-friendly manner. In contrast to CachedIntrospectionResults,
304-
// Introspector does not use WeakReferences as values of its WeakHashMap!
305-
Class<?> classToFlush = beanClass;
306-
do {
307-
Introspector.flushFromCaches(classToFlush);
308-
classToFlush = classToFlush.getSuperclass();
292+
// Only bother with flushFromCaches if the Introspector actually cached...
293+
if (!shouldIntrospectorIgnoreBeaninfoClasses) {
294+
// Immediately remove class from Introspector cache, to allow for proper
295+
// garbage collection on class loader shutdown - we cache it here anyway,
296+
// in a GC-friendly manner. In contrast to CachedIntrospectionResults,
297+
// Introspector does not use WeakReferences as values of its WeakHashMap!
298+
Class<?> classToFlush = beanClass;
299+
do {
300+
Introspector.flushFromCaches(classToFlush);
301+
classToFlush = classToFlush.getSuperclass();
302+
}
303+
while (classToFlush != null);
309304
}
310-
while (classToFlush != null);
311305

312306
if (logger.isTraceEnabled()) {
313307
logger.trace("Caching PropertyDescriptors for class [" + beanClass.getName() + "]");
@@ -332,7 +326,7 @@ private CachedIntrospectionResults(Class<?> beanClass) throws BeansException {
332326
this.propertyDescriptorCache.put(pd.getName(), pd);
333327
}
334328

335-
this.typeDescriptorCache = new HashMap<PropertyDescriptor, TypeDescriptor>();
329+
this.typeDescriptorCache = new ConcurrentHashMap<PropertyDescriptor, TypeDescriptor>();
336330
}
337331
catch (IntrospectionException ex) {
338332
throw new FatalBeanException("Failed to obtain BeanInfo for class [" + beanClass.getName() + "]", ex);
@@ -381,12 +375,12 @@ private PropertyDescriptor buildGenericTypeAwarePropertyDescriptor(Class<?> bean
381375
}
382376
}
383377

384-
TypeDescriptor getTypeDescriptor(PropertyDescriptor pd) {
385-
return this.typeDescriptorCache.get(pd);
378+
void addTypeDescriptor(PropertyDescriptor pd, TypeDescriptor td) {
379+
this.typeDescriptorCache.put(pd, td);
386380
}
387381

388-
void putTypeDescriptor(PropertyDescriptor pd, TypeDescriptor td) {
389-
this.typeDescriptorCache.put(pd, td);
382+
TypeDescriptor getTypeDescriptor(PropertyDescriptor pd) {
383+
return this.typeDescriptorCache.get(pd);
390384
}
391385

392386
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import org.springframework.beans.BeansException;
2323
import org.springframework.core.Constants;
24+
import org.springframework.core.SpringProperties;
2425
import org.springframework.core.env.AbstractEnvironment;
2526
import org.springframework.util.PropertyPlaceholderHelper;
2627
import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver;
@@ -84,7 +85,7 @@ public class PropertyPlaceholderConfigurer extends PlaceholderConfigurerSupport
8485
private int systemPropertiesMode = SYSTEM_PROPERTIES_MODE_FALLBACK;
8586

8687
private boolean searchSystemEnvironment =
87-
!"true".equalsIgnoreCase(System.getProperty(AbstractEnvironment.IGNORE_GETENV_PROPERTY_NAME));
88+
!SpringProperties.getFlag(AbstractEnvironment.IGNORE_GETENV_PROPERTY_NAME);
8889

8990

9091
/**
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/*
2+
* Copyright 2002-2013 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.core;
18+
19+
import java.io.IOException;
20+
import java.io.InputStream;
21+
import java.net.URL;
22+
import java.util.Properties;
23+
24+
import org.apache.commons.logging.Log;
25+
import org.apache.commons.logging.LogFactory;
26+
27+
/**
28+
* Static holder for local Spring properties, i.e. defined at the Spring library level.
29+
*
30+
* <p>Reads a {@code spring.properties} file from the root of the Spring library classpath,
31+
* and also allows for programmatically setting properties through {@link #setProperty}.
32+
* When checking a property, local entries are being checked first, then falling back
33+
* to JVM-level system properties through a {@link System#getProperty} check.
34+
*
35+
* <p>This is an alternative way to set Spring-related system properties such as
36+
* "spring.getenv.ignore" and "spring.beaninfo.ignore", in particular for scenarios
37+
* where JVM system properties are locked on the target platform (e.g. WebSphere).
38+
* See {@link #setFlag} for a convenient way to locally set such flags to "true".
39+
*
40+
* @author Juergen Hoeller
41+
* @since 3.2.7
42+
* @see org.springframework.core.env.AbstractEnvironment#IGNORE_GETENV_PROPERTY_NAME
43+
* @see org.springframework.beans.CachedIntrospectionResults#IGNORE_BEANINFO_PROPERTY_NAME
44+
*/
45+
public abstract class SpringProperties {
46+
47+
private static final Log logger = LogFactory.getLog(SpringProperties.class);
48+
49+
private static final Properties localProperties = new Properties();
50+
51+
52+
static {
53+
try {
54+
ClassLoader cl = SpringProperties.class.getClassLoader();
55+
URL url = cl.getResource("spring.properties");
56+
if (url != null) {
57+
logger.info("Found 'spring.properties' file in local classpath");
58+
InputStream is = url.openStream();
59+
try {
60+
localProperties.load(is);
61+
}
62+
finally {
63+
is.close();
64+
}
65+
}
66+
}
67+
catch (IOException ex) {
68+
if (logger.isInfoEnabled()) {
69+
logger.info("Could not load 'spring.properties' file from local classpath: " + ex);
70+
}
71+
}
72+
}
73+
74+
75+
/**
76+
* Programmatically set a local property, overriding an entry in the
77+
* {@code spring.properties} file (if any).
78+
* @param key the property key
79+
* @param value the associated property value, or {@code null} to reset it
80+
*/
81+
public static void setProperty(String key, String value) {
82+
if (value != null) {
83+
localProperties.setProperty(key, value);
84+
}
85+
else {
86+
localProperties.remove(key);
87+
}
88+
}
89+
90+
/**
91+
* Retrieve the property value for the given key, checking local Spring
92+
* properties first and falling back to JVM-level system properties.
93+
* @param key the property key
94+
* @return the associated property value, or {@code null} if none found
95+
*/
96+
public static String getProperty(String key) {
97+
String value = localProperties.getProperty(key);
98+
if (value == null) {
99+
try {
100+
value = System.getProperty(key);
101+
}
102+
catch (Throwable ex) {
103+
if (logger.isDebugEnabled()) {
104+
logger.debug("Could not retrieve system property '" + key + "': " + ex);
105+
}
106+
}
107+
}
108+
return value;
109+
}
110+
111+
/**
112+
* Programmatically set a local flag to "true", overriding an
113+
* entry in the {@code spring.properties} file (if any).
114+
* @param key the property key
115+
*/
116+
public static void setFlag(String key) {
117+
localProperties.put(key, Boolean.TRUE.toString());
118+
}
119+
120+
/**
121+
* Retrieve the flag for the given property key.
122+
* @param key the property key
123+
* @return {@code true} if the property is set to "true",
124+
* {@code} false otherwise
125+
*/
126+
public static boolean getFlag(String key) {
127+
return Boolean.parseBoolean(getProperty(key));
128+
}
129+
130+
}

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

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.apache.commons.logging.Log;
2626
import org.apache.commons.logging.LogFactory;
2727

28+
import org.springframework.core.SpringProperties;
2829
import org.springframework.core.convert.support.ConfigurableConversionService;
2930
import org.springframework.util.Assert;
3031
import org.springframework.util.StringUtils;
@@ -377,15 +378,15 @@ public Map<String, Object> getSystemEnvironment() {
377378
catch (AccessControlException ex) {
378379
return (Map) new ReadOnlySystemAttributesMap() {
379380
@Override
380-
protected String getSystemAttribute(String variableName) {
381+
protected String getSystemAttribute(String attributeName) {
381382
try {
382-
return System.getenv(variableName);
383+
return System.getenv(attributeName);
383384
}
384385
catch (AccessControlException ex) {
385386
if (logger.isInfoEnabled()) {
386387
logger.info(format("Caught AccessControlException when accessing system " +
387388
"environment variable [%s]; its value will be returned [null]. Reason: %s",
388-
variableName, ex.getMessage()));
389+
attributeName, ex.getMessage()));
389390
}
390391
return null;
391392
}
@@ -404,15 +405,7 @@ protected String getSystemAttribute(String variableName) {
404405
* returning {@code true} if its value equals "true" in any case.
405406
*/
406407
protected boolean suppressGetenvAccess() {
407-
try {
408-
return "true".equalsIgnoreCase(System.getProperty(IGNORE_GETENV_PROPERTY_NAME));
409-
}
410-
catch (Throwable ex) {
411-
if (logger.isDebugEnabled()) {
412-
logger.debug("Could not obtain system property '" + IGNORE_GETENV_PROPERTY_NAME + "': " + ex);
413-
}
414-
return false;
415-
}
408+
return SpringProperties.getFlag(IGNORE_GETENV_PROPERTY_NAME);
416409
}
417410

418411
@Override
@@ -424,15 +417,15 @@ public Map<String, Object> getSystemProperties() {
424417
catch (AccessControlException ex) {
425418
return (Map) new ReadOnlySystemAttributesMap() {
426419
@Override
427-
protected String getSystemAttribute(String propertyName) {
420+
protected String getSystemAttribute(String attributeName) {
428421
try {
429-
return System.getProperty(propertyName);
422+
return System.getProperty(attributeName);
430423
}
431424
catch (AccessControlException ex) {
432425
if (logger.isInfoEnabled()) {
433426
logger.info(format("Caught AccessControlException when accessing system " +
434427
"property [%s]; its value will be returned [null]. Reason: %s",
435-
propertyName, ex.getMessage()));
428+
attributeName, ex.getMessage()));
436429
}
437430
return null;
438431
}

0 commit comments

Comments
 (0)