Skip to content

Commit

Permalink
PropertySourcesPlaceholderConfigurer's "ignoreUnresolvablePlaceholder…
Browse files Browse the repository at this point in the history
…s" setting reliably applies to nested placeholders as well

Issue: SPR-10549
  • Loading branch information
jhoeller committed Jul 31, 2013
1 parent 803779d commit 127b91f
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@ public void setEnvironment(Environment environment) {
* <p>Processing occurs by replacing ${...} placeholders in bean definitions by resolving each
* against this configurer's set of {@link PropertySources}, which includes:
* <ul>
* <li>all {@linkplain org.springframework.core.env.ConfigurableEnvironment#getPropertySources environment property sources}, if an
* {@code Environment} {@linkplain #setEnvironment is present}
* <li>all {@linkplain org.springframework.core.env.ConfigurableEnvironment#getPropertySources
* environment property sources}, if an {@code Environment} {@linkplain #setEnvironment is present}
* <li>{@linkplain #mergeProperties merged local properties}, if {@linkplain #setLocation any}
* {@linkplain #setLocations have} {@linkplain #setProperties been}
* {@linkplain #setPropertiesArray specified}
Expand All @@ -139,7 +139,7 @@ public String getProperty(String key) {
}
try {
PropertySource<?> localPropertySource =
new PropertiesPropertySource(LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME, this.mergeProperties());
new PropertiesPropertySource(LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME, mergeProperties());
if (this.localOverride) {
this.propertySources.addFirst(localPropertySource);
}
Expand All @@ -152,7 +152,7 @@ public String getProperty(String key) {
}
}

this.processProperties(beanFactory, new PropertySourcesPropertyResolver(this.propertySources));
processProperties(beanFactory, new PropertySourcesPropertyResolver(this.propertySources));
this.appliedPropertySources = this.propertySources;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,25 @@ public void testPropertyPlaceholderConfigurerWithNestedCircularReference() {
}
}

@Test
public void testPropertyPlaceholderConfigurerWithNestedUnresolvableReference() {
StaticApplicationContext ac = new StaticApplicationContext();
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("name", "name${var}");
ac.registerSingleton("tb1", TestBean.class, pvs);
pvs = new MutablePropertyValues();
pvs.add("properties", "var=${m}var\nm=${var2}\nvar2=${m2}");
ac.registerSingleton("configurer1", PropertyPlaceholderConfigurer.class, pvs);
try {
ac.refresh();
fail("Should have thrown BeanDefinitionStoreException");
}
catch (BeanDefinitionStoreException ex) {
// expected
ex.printStackTrace();
}
}

@Ignore // this test was breaking after the 3.0 repackaging
@Test
public void testPropertyPlaceholderConfigurerWithAutowireByType() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.*;

/**
* Unit tests for {@link PropertySourcesPlaceholderConfigurer}.
*
* @author Chris Beams
* @since 3.1
*/
Expand Down Expand Up @@ -143,7 +141,9 @@ public void explicitPropertySourcesExcludesLocalProperties() {

PropertySourcesPlaceholderConfigurer pc = new PropertySourcesPlaceholderConfigurer();
pc.setPropertySources(propertySources);
pc.setProperties(new Properties() {{ put("my.name", "local"); }});
pc.setProperties(new Properties() {{
put("my.name", "local");
}});
pc.setIgnoreUnresolvablePlaceholders(true);
pc.postProcessBeanFactory(bf);
assertThat(bf.getBean(TestBean.class).getName(), equalTo("${my.name}"));
Expand Down Expand Up @@ -176,6 +176,38 @@ public void ignoreUnresolvablePlaceholders_true() {
assertThat(bf.getBean(TestBean.class).getName(), equalTo("${my.name}"));
}

@Test(expected=BeanDefinitionStoreException.class)
public void nestedUnresolvablePlaceholder() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.registerBeanDefinition("testBean",
genericBeanDefinition(TestBean.class)
.addPropertyValue("name", "${my.name}")
.getBeanDefinition());

PropertySourcesPlaceholderConfigurer pc = new PropertySourcesPlaceholderConfigurer();
pc.setProperties(new Properties() {{
put("my.name", "${bogus}");
}});
pc.postProcessBeanFactory(bf); // should throw
}

@Test
public void ignoredNestedUnresolvablePlaceholder() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.registerBeanDefinition("testBean",
genericBeanDefinition(TestBean.class)
.addPropertyValue("name", "${my.name}")
.getBeanDefinition());

PropertySourcesPlaceholderConfigurer pc = new PropertySourcesPlaceholderConfigurer();
pc.setProperties(new Properties() {{
put("my.name", "${bogus}");
}});
pc.setIgnoreUnresolvablePlaceholders(true);
pc.postProcessBeanFactory(bf);
assertThat(bf.getBean(TestBean.class).getName(), equalTo("${bogus}"));
}

@Test
public void withNonEnumerablePropertySource() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
Expand Down Expand Up @@ -217,7 +249,8 @@ private void localPropertiesOverride(boolean override) {
ppc.postProcessBeanFactory(bf);
if (override) {
assertThat(bf.getBean(TestBean.class).getName(), equalTo("local"));
} else {
}
else {
assertThat(bf.getBean(TestBean.class).getName(), equalTo("enclosing"));
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -16,25 +16,22 @@

package org.springframework.core.env;

import static java.lang.String.format;
import static org.springframework.util.SystemPropertyUtils.PLACEHOLDER_PREFIX;
import static org.springframework.util.SystemPropertyUtils.PLACEHOLDER_SUFFIX;
import static org.springframework.util.SystemPropertyUtils.VALUE_SEPARATOR;

import java.util.LinkedHashSet;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.core.convert.support.ConfigurableConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.util.PropertyPlaceholderHelper;
import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver;
import org.springframework.util.SystemPropertyUtils;

/**
* Abstract base class for resolving properties against any underlying source.
*
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.1
*/
public abstract class AbstractPropertyResolver implements ConfigurablePropertyResolver {
Expand All @@ -44,15 +41,20 @@ public abstract class AbstractPropertyResolver implements ConfigurablePropertyRe
protected ConfigurableConversionService conversionService = new DefaultConversionService();

private PropertyPlaceholderHelper nonStrictHelper;

private PropertyPlaceholderHelper strictHelper;

private boolean ignoreUnresolvableNestedPlaceholders = false;

private String placeholderPrefix = PLACEHOLDER_PREFIX;
private String placeholderSuffix = PLACEHOLDER_SUFFIX;
private String valueSeparator = VALUE_SEPARATOR;
private String placeholderPrefix = SystemPropertyUtils.PLACEHOLDER_PREFIX;

private String placeholderSuffix = SystemPropertyUtils.PLACEHOLDER_SUFFIX;

private String valueSeparator = SystemPropertyUtils.VALUE_SEPARATOR;

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


@Override
public ConfigurableConversionService getConversionService() {
return this.conversionService;
Expand All @@ -66,13 +68,13 @@ public void setConversionService(ConfigurableConversionService conversionService
@Override
public String getProperty(String key, String defaultValue) {
String value = getProperty(key);
return value == null ? defaultValue : value;
return (value != null ? value : defaultValue);
}

@Override
public <T> T getProperty(String key, Class<T> targetType, T defaultValue) {
T value = getProperty(key, targetType);
return value == null ? defaultValue : value;
return (value != null ? value : defaultValue);
}

@Override
Expand All @@ -99,7 +101,7 @@ public void validateRequiredProperties() {
public String getRequiredProperty(String key) throws IllegalStateException {
String value = getProperty(key);
if (value == null) {
throw new IllegalStateException(format("required key [%s] not found", key));
throw new IllegalStateException(String.format("required key [%s] not found", key));
}
return value;
}
Expand All @@ -108,7 +110,7 @@ public String getRequiredProperty(String key) throws IllegalStateException {
public <T> T getRequiredProperty(String key, Class<T> valueType) throws IllegalStateException {
T value = getProperty(key, valueType);
if (value == null) {
throw new IllegalStateException(format("required key [%s] not found", key));
throw new IllegalStateException(String.format("required key [%s] not found", key));
}
return value;
}
Expand Down Expand Up @@ -142,18 +144,18 @@ public void setValueSeparator(String valueSeparator) {

@Override
public String resolvePlaceholders(String text) {
if (nonStrictHelper == null) {
nonStrictHelper = createPlaceholderHelper(true);
if (this.nonStrictHelper == null) {
this.nonStrictHelper = createPlaceholderHelper(true);
}
return doResolvePlaceholders(text, nonStrictHelper);
return doResolvePlaceholders(text, this.nonStrictHelper);
}

@Override
public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
if (strictHelper == null) {
strictHelper = createPlaceholderHelper(false);
if (this.strictHelper == null) {
this.strictHelper = createPlaceholderHelper(false);
}
return doResolvePlaceholders(text, strictHelper);
return doResolvePlaceholders(text, this.strictHelper);
}

/**
Expand All @@ -168,15 +170,19 @@ public void setIgnoreUnresolvableNestedPlaceholders(boolean ignoreUnresolvableNe

/**
* Resolve placeholders within the given string, deferring to the value of
* {@link #setIgnoreUnresolvableNestedPlaceholders(boolean)} to determine whether any
* {@link #setIgnoreUnresolvableNestedPlaceholders} to determine whether any
* unresolvable placeholders should raise an exception or be ignored.
* <p>Invoked from {@link #getProperty} and its variants, implicitly resolving
* nested placeholders. In contrast, {@link #resolvePlaceholders} and
* {@link #resolveRequiredPlaceholders} do <emphasis>not</emphasis> delegate
* to this method but rather perform their own handling of unresolvable
* placeholders, as specified by each of those methods.
* @since 3.2
* @see #setIgnoreUnresolvableNestedPlaceholders(boolean)
* @see #setIgnoreUnresolvableNestedPlaceholders
*/
protected String resolveNestedPlaceholders(String value) {
return this.ignoreUnresolvableNestedPlaceholders ?
this.resolvePlaceholders(value) :
this.resolveRequiredPlaceholders(value);
resolvePlaceholders(value) : resolveRequiredPlaceholders(value);
}

private PropertyPlaceholderHelper createPlaceholderHelper(boolean ignoreUnresolvablePlaceholders) {
Expand All @@ -185,12 +191,20 @@ private PropertyPlaceholderHelper createPlaceholderHelper(boolean ignoreUnresolv
}

private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
return helper.replacePlaceholders(text, new PlaceholderResolver() {
return helper.replacePlaceholders(text, new PropertyPlaceholderHelper.PlaceholderResolver() {
@Override
public String resolvePlaceholder(String placeholderName) {
return getProperty(placeholderName);
return getPropertyAsRawString(placeholderName);
}
});
}

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

}
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -74,8 +74,9 @@ public interface PropertyResolver {
/**
* Convert the property value associated with the given key to a {@code Class}
* of type {@code T} or {@code null} if the key cannot be resolved.
* @throws ConversionException if class specified by property value cannot be found
* or loaded or if targetType is not assignable from class specified by property value
* @throws org.springframework.core.convert.ConversionException if class specified
* by property value cannot be found or loaded or if targetType is not assignable
* from class specified by property value
* @see #getProperty(String, Class)
*/
<T> Class<T> getPropertyAsClass(String key, Class<T> targetType);
Expand Down Expand Up @@ -113,8 +114,9 @@ public interface PropertyResolver {
* no default value will cause an IllegalArgumentException to be thrown.
* @return the resolved String (never {@code null})
* @throws IllegalArgumentException if given text is {@code null}
* @throws IllegalArgumentException if any placeholders are unresolvable
* or if any placeholders are unresolvable
* @see org.springframework.util.SystemPropertyUtils#resolvePlaceholders(String, boolean)
*/
String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;

}
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,20 @@ public boolean containsProperty(String key) {

@Override
public String getProperty(String key) {
return getProperty(key, String.class);
return getProperty(key, String.class, true);
}

@Override
public <T> T getProperty(String key, Class<T> targetValueType) {
return getProperty(key, targetValueType, true);
}

@Override
protected String getPropertyAsRawString(String key) {
return getProperty(key, String.class, false);
}

protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
boolean debugEnabled = logger.isDebugEnabled();
if (logger.isTraceEnabled()) {
logger.trace(String.format("getProperty(\"%s\", %s)", key, targetValueType.getSimpleName()));
Expand All @@ -74,8 +83,8 @@ public <T> T getProperty(String key, Class<T> targetValueType) {
Object value;
if ((value = propertySource.getProperty(key)) != null) {
Class<?> valueType = value.getClass();
if (String.class.equals(valueType)) {
value = this.resolveNestedPlaceholders((String) value);
if (resolveNestedPlaceholders && value instanceof String) {
value = resolveNestedPlaceholders((String) value);
}
if (debugEnabled) {
logger.debug(String.format("Found key '%s' in [%s] with type [%s] and value '%s'",
Expand All @@ -86,7 +95,7 @@ public <T> T getProperty(String key, Class<T> targetValueType) {
"Cannot convert value [%s] from source type [%s] to target type [%s]",
value, valueType.getSimpleName(), targetValueType.getSimpleName()));
}
return conversionService.convert(value, targetValueType);
return this.conversionService.convert(value, targetValueType);
}
}
}
Expand Down Expand Up @@ -115,10 +124,10 @@ public <T> Class<T> getPropertyAsClass(String key, Class<T> targetValueType) {
Class<?> clazz;
if (value instanceof String) {
try {
clazz = ClassUtils.forName((String)value, null);
clazz = ClassUtils.forName((String) value, null);
}
catch (Exception ex) {
throw new ClassConversionException((String)value, targetValueType, ex);
throw new ClassConversionException((String) value, targetValueType, ex);
}
}
else if (value instanceof Class) {
Expand Down
Loading

0 comments on commit 127b91f

Please sign in to comment.