Skip to content

Commit b40263e

Browse files
committed
PropertySourcesPlaceholderConfigurer's "ignoreUnresolvablePlaceholders" setting reliably applies to nested placeholders as well
Issue: SPR-10549 (cherry picked from commit 127b91f)
1 parent 2f8dfb3 commit b40263e

File tree

8 files changed

+132
-57
lines changed

8 files changed

+132
-57
lines changed

spring-context/src/main/java/org/springframework/context/support/PropertySourcesPlaceholderConfigurer.java

Lines changed: 5 additions & 5 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.
@@ -108,8 +108,8 @@ public void setEnvironment(Environment environment) {
108108
* <p>Processing occurs by replacing ${...} placeholders in bean definitions by resolving each
109109
* against this configurer's set of {@link PropertySources}, which includes:
110110
* <ul>
111-
* <li>all {@linkplain org.springframework.core.env.ConfigurableEnvironment#getPropertySources environment property sources}, if an
112-
* {@code Environment} {@linkplain #setEnvironment is present}
111+
* <li>all {@linkplain org.springframework.core.env.ConfigurableEnvironment#getPropertySources
112+
* environment property sources}, if an {@code Environment} {@linkplain #setEnvironment is present}
113113
* <li>{@linkplain #mergeProperties merged local properties}, if {@linkplain #setLocation any}
114114
* {@linkplain #setLocations have} {@linkplain #setProperties been}
115115
* {@linkplain #setPropertiesArray specified}
@@ -135,7 +135,7 @@ public String getProperty(String key) {
135135
}
136136
try {
137137
PropertySource<?> localPropertySource =
138-
new PropertiesPropertySource(LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME, this.mergeProperties());
138+
new PropertiesPropertySource(LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME, mergeProperties());
139139
if (this.localOverride) {
140140
this.propertySources.addFirst(localPropertySource);
141141
}
@@ -148,7 +148,7 @@ public String getProperty(String key) {
148148
}
149149
}
150150

151-
this.processProperties(beanFactory, new PropertySourcesPropertyResolver(this.propertySources));
151+
processProperties(beanFactory, new PropertySourcesPropertyResolver(this.propertySources));
152152
}
153153

154154
/**

spring-context/src/test/java/org/springframework/context/support/PropertyResourceConfigurerIntegrationTests.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,25 @@ public void testPropertyPlaceholderConfigurerWithNestedCircularReference() {
153153
}
154154
}
155155

156+
@Test
157+
public void testPropertyPlaceholderConfigurerWithNestedUnresolvableReference() {
158+
StaticApplicationContext ac = new StaticApplicationContext();
159+
MutablePropertyValues pvs = new MutablePropertyValues();
160+
pvs.add("name", "name${var}");
161+
ac.registerSingleton("tb1", TestBean.class, pvs);
162+
pvs = new MutablePropertyValues();
163+
pvs.add("properties", "var=${m}var\nm=${var2}\nvar2=${m2}");
164+
ac.registerSingleton("configurer1", PropertyPlaceholderConfigurer.class, pvs);
165+
try {
166+
ac.refresh();
167+
fail("Should have thrown BeanDefinitionStoreException");
168+
}
169+
catch (BeanDefinitionStoreException ex) {
170+
// expected
171+
ex.printStackTrace();
172+
}
173+
}
174+
156175
@Ignore // this test was breaking after the 3.0 repackaging
157176
@Test
158177
public void testPropertyPlaceholderConfigurerWithAutowireByType() {

spring-context/src/test/java/org/springframework/context/support/PropertySourcesPlaceholderConfigurerTests.java

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,6 @@
3939
import org.springframework.tests.sample.beans.TestBean;
4040

4141
/**
42-
* Unit tests for {@link PropertySourcesPlaceholderConfigurer}.
43-
*
4442
* @author Chris Beams
4543
* @since 3.1
4644
*/
@@ -139,7 +137,9 @@ public void explicitPropertySourcesExcludesLocalProperties() {
139137

140138
PropertySourcesPlaceholderConfigurer pc = new PropertySourcesPlaceholderConfigurer();
141139
pc.setPropertySources(propertySources);
142-
pc.setProperties(new Properties() {{ put("my.name", "local"); }});
140+
pc.setProperties(new Properties() {{
141+
put("my.name", "local");
142+
}});
143143
pc.setIgnoreUnresolvablePlaceholders(true);
144144
pc.postProcessBeanFactory(bf);
145145
assertThat(bf.getBean(TestBean.class).getName(), equalTo("${my.name}"));
@@ -172,6 +172,38 @@ public void ignoreUnresolvablePlaceholders_true() {
172172
assertThat(bf.getBean(TestBean.class).getName(), equalTo("${my.name}"));
173173
}
174174

175+
@Test(expected=BeanDefinitionStoreException.class)
176+
public void nestedUnresolvablePlaceholder() {
177+
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
178+
bf.registerBeanDefinition("testBean",
179+
genericBeanDefinition(TestBean.class)
180+
.addPropertyValue("name", "${my.name}")
181+
.getBeanDefinition());
182+
183+
PropertySourcesPlaceholderConfigurer pc = new PropertySourcesPlaceholderConfigurer();
184+
pc.setProperties(new Properties() {{
185+
put("my.name", "${bogus}");
186+
}});
187+
pc.postProcessBeanFactory(bf); // should throw
188+
}
189+
190+
@Test
191+
public void ignoredNestedUnresolvablePlaceholder() {
192+
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
193+
bf.registerBeanDefinition("testBean",
194+
genericBeanDefinition(TestBean.class)
195+
.addPropertyValue("name", "${my.name}")
196+
.getBeanDefinition());
197+
198+
PropertySourcesPlaceholderConfigurer pc = new PropertySourcesPlaceholderConfigurer();
199+
pc.setProperties(new Properties() {{
200+
put("my.name", "${bogus}");
201+
}});
202+
pc.setIgnoreUnresolvablePlaceholders(true);
203+
pc.postProcessBeanFactory(bf);
204+
assertThat(bf.getBean(TestBean.class).getName(), equalTo("${bogus}"));
205+
}
206+
175207
@Test
176208
public void withNonEnumerablePropertySource() {
177209
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
@@ -213,7 +245,8 @@ private void localPropertiesOverride(boolean override) {
213245
ppc.postProcessBeanFactory(bf);
214246
if (override) {
215247
assertThat(bf.getBean(TestBean.class).getName(), equalTo("local"));
216-
} else {
248+
}
249+
else {
217250
assertThat(bf.getBean(TestBean.class).getName(), equalTo("enclosing"));
218251
}
219252
}

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

Lines changed: 40 additions & 26 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.
@@ -16,25 +16,22 @@
1616

1717
package org.springframework.core.env;
1818

19-
import static java.lang.String.format;
20-
import static org.springframework.util.SystemPropertyUtils.PLACEHOLDER_PREFIX;
21-
import static org.springframework.util.SystemPropertyUtils.PLACEHOLDER_SUFFIX;
22-
import static org.springframework.util.SystemPropertyUtils.VALUE_SEPARATOR;
23-
2419
import java.util.LinkedHashSet;
2520
import java.util.Set;
2621

2722
import org.apache.commons.logging.Log;
2823
import org.apache.commons.logging.LogFactory;
24+
2925
import org.springframework.core.convert.support.ConfigurableConversionService;
3026
import org.springframework.core.convert.support.DefaultConversionService;
3127
import org.springframework.util.PropertyPlaceholderHelper;
32-
import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver;
28+
import org.springframework.util.SystemPropertyUtils;
3329

3430
/**
3531
* Abstract base class for resolving properties against any underlying source.
3632
*
3733
* @author Chris Beams
34+
* @author Juergen Hoeller
3835
* @since 3.1
3936
*/
4037
public abstract class AbstractPropertyResolver implements ConfigurablePropertyResolver {
@@ -44,15 +41,20 @@ public abstract class AbstractPropertyResolver implements ConfigurablePropertyRe
4441
protected ConfigurableConversionService conversionService = new DefaultConversionService();
4542

4643
private PropertyPlaceholderHelper nonStrictHelper;
44+
4745
private PropertyPlaceholderHelper strictHelper;
46+
4847
private boolean ignoreUnresolvableNestedPlaceholders = false;
4948

50-
private String placeholderPrefix = PLACEHOLDER_PREFIX;
51-
private String placeholderSuffix = PLACEHOLDER_SUFFIX;
52-
private String valueSeparator = VALUE_SEPARATOR;
49+
private String placeholderPrefix = SystemPropertyUtils.PLACEHOLDER_PREFIX;
50+
51+
private String placeholderSuffix = SystemPropertyUtils.PLACEHOLDER_SUFFIX;
52+
53+
private String valueSeparator = SystemPropertyUtils.VALUE_SEPARATOR;
5354

5455
private final Set<String> requiredProperties = new LinkedHashSet<String>();
5556

57+
5658
public ConfigurableConversionService getConversionService() {
5759
return this.conversionService;
5860
}
@@ -63,12 +65,12 @@ public void setConversionService(ConfigurableConversionService conversionService
6365

6466
public String getProperty(String key, String defaultValue) {
6567
String value = getProperty(key);
66-
return value == null ? defaultValue : value;
68+
return (value != null ? value : defaultValue);
6769
}
6870

6971
public <T> T getProperty(String key, Class<T> targetType, T defaultValue) {
7072
T value = getProperty(key, targetType);
71-
return value == null ? defaultValue : value;
73+
return (value != null ? value : defaultValue);
7274
}
7375

7476
public void setRequiredProperties(String... requiredProperties) {
@@ -92,15 +94,15 @@ public void validateRequiredProperties() {
9294
public String getRequiredProperty(String key) throws IllegalStateException {
9395
String value = getProperty(key);
9496
if (value == null) {
95-
throw new IllegalStateException(format("required key [%s] not found", key));
97+
throw new IllegalStateException(String.format("required key [%s] not found", key));
9698
}
9799
return value;
98100
}
99101

100102
public <T> T getRequiredProperty(String key, Class<T> valueType) throws IllegalStateException {
101103
T value = getProperty(key, valueType);
102104
if (value == null) {
103-
throw new IllegalStateException(format("required key [%s] not found", key));
105+
throw new IllegalStateException(String.format("required key [%s] not found", key));
104106
}
105107
return value;
106108
}
@@ -130,17 +132,17 @@ public void setValueSeparator(String valueSeparator) {
130132
}
131133

132134
public String resolvePlaceholders(String text) {
133-
if (nonStrictHelper == null) {
134-
nonStrictHelper = createPlaceholderHelper(true);
135+
if (this.nonStrictHelper == null) {
136+
this.nonStrictHelper = createPlaceholderHelper(true);
135137
}
136-
return doResolvePlaceholders(text, nonStrictHelper);
138+
return doResolvePlaceholders(text, this.nonStrictHelper);
137139
}
138140

139141
public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
140-
if (strictHelper == null) {
141-
strictHelper = createPlaceholderHelper(false);
142+
if (this.strictHelper == null) {
143+
this.strictHelper = createPlaceholderHelper(false);
142144
}
143-
return doResolvePlaceholders(text, strictHelper);
145+
return doResolvePlaceholders(text, this.strictHelper);
144146
}
145147

146148
/**
@@ -154,15 +156,19 @@ public void setIgnoreUnresolvableNestedPlaceholders(boolean ignoreUnresolvableNe
154156

155157
/**
156158
* Resolve placeholders within the given string, deferring to the value of
157-
* {@link #setIgnoreUnresolvableNestedPlaceholders(boolean)} to determine whether any
159+
* {@link #setIgnoreUnresolvableNestedPlaceholders} to determine whether any
158160
* unresolvable placeholders should raise an exception or be ignored.
161+
* <p>Invoked from {@link #getProperty} and its variants, implicitly resolving
162+
* nested placeholders. In contrast, {@link #resolvePlaceholders} and
163+
* {@link #resolveRequiredPlaceholders} do <emphasis>not</emphasis> delegate
164+
* to this method but rather perform their own handling of unresolvable
165+
* placeholders, as specified by each of those methods.
159166
* @since 3.2
160-
* @see #setIgnoreUnresolvableNestedPlaceholders(boolean)
167+
* @see #setIgnoreUnresolvableNestedPlaceholders
161168
*/
162169
protected String resolveNestedPlaceholders(String value) {
163170
return this.ignoreUnresolvableNestedPlaceholders ?
164-
this.resolvePlaceholders(value) :
165-
this.resolveRequiredPlaceholders(value);
171+
resolvePlaceholders(value) : resolveRequiredPlaceholders(value);
166172
}
167173

168174
private PropertyPlaceholderHelper createPlaceholderHelper(boolean ignoreUnresolvablePlaceholders) {
@@ -171,11 +177,19 @@ private PropertyPlaceholderHelper createPlaceholderHelper(boolean ignoreUnresolv
171177
}
172178

173179
private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
174-
return helper.replacePlaceholders(text, new PlaceholderResolver() {
180+
return helper.replacePlaceholders(text, new PropertyPlaceholderHelper.PlaceholderResolver() {
175181
public String resolvePlaceholder(String placeholderName) {
176-
return getProperty(placeholderName);
182+
return getPropertyAsRawString(placeholderName);
177183
}
178184
});
179185
}
180186

187+
/**
188+
* Retrieve the specified property as a raw String,
189+
* i.e. without resolution of nested placeholders.
190+
* @param key the property name to resolve
191+
* @return the property value or {@code null} if none found
192+
*/
193+
protected abstract String getPropertyAsRawString(String key);
194+
181195
}

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

Lines changed: 6 additions & 4 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.
@@ -74,8 +74,9 @@ public interface PropertyResolver {
7474
/**
7575
* Convert the property value associated with the given key to a {@code Class}
7676
* of type {@code T} or {@code null} if the key cannot be resolved.
77-
* @throws ConversionException if class specified by property value cannot be found
78-
* or loaded or if targetType is not assignable from class specified by property value
77+
* @throws org.springframework.core.convert.ConversionException if class specified
78+
* by property value cannot be found or loaded or if targetType is not assignable
79+
* from class specified by property value
7980
* @see #getProperty(String, Class)
8081
*/
8182
<T> Class<T> getPropertyAsClass(String key, Class<T> targetType);
@@ -113,8 +114,9 @@ public interface PropertyResolver {
113114
* no default value will cause an IllegalArgumentException to be thrown.
114115
* @return the resolved String (never {@code null})
115116
* @throws IllegalArgumentException if given text is {@code null}
116-
* @throws IllegalArgumentException if any placeholders are unresolvable
117+
* or if any placeholders are unresolvable
117118
* @see org.springframework.util.SystemPropertyUtils#resolvePlaceholders(String, boolean)
118119
*/
119120
String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;
121+
120122
}

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

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,20 @@ public boolean containsProperty(String key) {
5757

5858
@Override
5959
public String getProperty(String key) {
60-
return getProperty(key, String.class);
60+
return getProperty(key, String.class, true);
6161
}
6262

6363
@Override
6464
public <T> T getProperty(String key, Class<T> targetValueType) {
65+
return getProperty(key, targetValueType, true);
66+
}
67+
68+
@Override
69+
protected String getPropertyAsRawString(String key) {
70+
return getProperty(key, String.class, false);
71+
}
72+
73+
protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
6574
boolean debugEnabled = logger.isDebugEnabled();
6675
if (logger.isTraceEnabled()) {
6776
logger.trace(String.format("getProperty(\"%s\", %s)", key, targetValueType.getSimpleName()));
@@ -74,8 +83,8 @@ public <T> T getProperty(String key, Class<T> targetValueType) {
7483
Object value;
7584
if ((value = propertySource.getProperty(key)) != null) {
7685
Class<?> valueType = value.getClass();
77-
if (String.class.equals(valueType)) {
78-
value = this.resolveNestedPlaceholders((String) value);
86+
if (resolveNestedPlaceholders && value instanceof String) {
87+
value = resolveNestedPlaceholders((String) value);
7988
}
8089
if (debugEnabled) {
8190
logger.debug(String.format("Found key '%s' in [%s] with type [%s] and value '%s'",
@@ -86,7 +95,7 @@ public <T> T getProperty(String key, Class<T> targetValueType) {
8695
"Cannot convert value [%s] from source type [%s] to target type [%s]",
8796
value, valueType.getSimpleName(), targetValueType.getSimpleName()));
8897
}
89-
return conversionService.convert(value, targetValueType);
98+
return this.conversionService.convert(value, targetValueType);
9099
}
91100
}
92101
}
@@ -115,10 +124,10 @@ public <T> Class<T> getPropertyAsClass(String key, Class<T> targetValueType) {
115124
Class<?> clazz;
116125
if (value instanceof String) {
117126
try {
118-
clazz = ClassUtils.forName((String)value, null);
127+
clazz = ClassUtils.forName((String) value, null);
119128
}
120129
catch (Exception ex) {
121-
throw new ClassConversionException((String)value, targetValueType, ex);
130+
throw new ClassConversionException((String) value, targetValueType, ex);
122131
}
123132
}
124133
else if (value instanceof Class) {

0 commit comments

Comments
 (0)