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 9366080ef6..4fd571b519 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 @@ -464,6 +464,10 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment { return this.propertyResolver.resolveRequiredPlaceholders(text); } + public void setIgnoreUnresolvableNestedPlaceholders(boolean ignoreUnresolvableNestedPlaceholders) { + this.propertyResolver.setIgnoreUnresolvableNestedPlaceholders(ignoreUnresolvableNestedPlaceholders); + } + public void setConversionService(ConfigurableConversionService conversionService) { this.propertyResolver.setConversionService(conversionService); } diff --git a/spring-core/src/main/java/org/springframework/core/env/AbstractPropertyResolver.java b/spring-core/src/main/java/org/springframework/core/env/AbstractPropertyResolver.java index 8538c6c229..9d61649648 100644 --- a/spring-core/src/main/java/org/springframework/core/env/AbstractPropertyResolver.java +++ b/spring-core/src/main/java/org/springframework/core/env/AbstractPropertyResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -45,6 +45,7 @@ public abstract class AbstractPropertyResolver implements ConfigurablePropertyRe private PropertyPlaceholderHelper nonStrictHelper; private PropertyPlaceholderHelper strictHelper; + private boolean ignoreUnresolvableNestedPlaceholders = false; private String placeholderPrefix = PLACEHOLDER_PREFIX; private String placeholderSuffix = PLACEHOLDER_SUFFIX; @@ -142,6 +143,28 @@ public abstract class AbstractPropertyResolver implements ConfigurablePropertyRe return doResolvePlaceholders(text, strictHelper); } + /** + * {@inheritDoc} + *

The default value for this implementation is {@code false}. + * @since 3.2 + */ + public void setIgnoreUnresolvableNestedPlaceholders(boolean ignoreUnresolvableNestedPlaceholders) { + this.ignoreUnresolvableNestedPlaceholders = ignoreUnresolvableNestedPlaceholders; + } + + /** + * Resolve placeholders within the given string, deferring to the value of + * {@link #setIgnoreUnresolvableNestedPlaceholders(boolean)} to determine whether any + * unresolvable placeholders should raise an exception or be ignored. + * @since 3.2 + * @see #setIgnoreUnresolvableNestedPlaceholders(boolean) + */ + protected String resolveNestedPlaceholders(String value) { + return this.ignoreUnresolvableNestedPlaceholders ? + this.resolvePlaceholders(value) : + this.resolveRequiredPlaceholders(value); + } + private PropertyPlaceholderHelper createPlaceholderHelper(boolean ignoreUnresolvablePlaceholders) { return new PropertyPlaceholderHelper(this.placeholderPrefix, this.placeholderSuffix, this.valueSeparator, ignoreUnresolvablePlaceholders); diff --git a/spring-core/src/main/java/org/springframework/core/env/ConfigurablePropertyResolver.java b/spring-core/src/main/java/org/springframework/core/env/ConfigurablePropertyResolver.java index ab5aa541dd..823ae43863 100644 --- a/spring-core/src/main/java/org/springframework/core/env/ConfigurablePropertyResolver.java +++ b/spring-core/src/main/java/org/springframework/core/env/ConfigurablePropertyResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -87,4 +87,17 @@ public interface ConfigurablePropertyResolver extends PropertyResolver { * properties are not resolvable. */ void validateRequiredProperties() throws MissingRequiredPropertiesException; + + /** + * Set whether to throw an exception when encountering an unresolvable placeholder + * nested within the value of a given property. A {@code false} value indicates strict + * resolution, i.e. that an exception will be thrown. A {@code true} value indicates + * that unresolvable nested placeholders should be passed through in their unresolved + * ${...} form. + *

Implementations of {@link #getProperty(String)} and its variants must inspect + * the value set here to determine correct behavior when property values contain + * unresolvable placeholders. + * @since 3.2 + */ + void setIgnoreUnresolvableNestedPlaceholders(boolean ignoreUnresolvableNestedPlaceholders); } diff --git a/spring-core/src/main/java/org/springframework/core/env/PropertySourcesPropertyResolver.java b/spring-core/src/main/java/org/springframework/core/env/PropertySourcesPropertyResolver.java index cee4db51a6..f761527437 100644 --- a/spring-core/src/main/java/org/springframework/core/env/PropertySourcesPropertyResolver.java +++ b/spring-core/src/main/java/org/springframework/core/env/PropertySourcesPropertyResolver.java @@ -73,7 +73,7 @@ public class PropertySourcesPropertyResolver extends AbstractPropertyResolver { if ((value = propertySource.getProperty(key)) != null) { Class valueType = value.getClass(); if (String.class.equals(valueType)) { - value = this.resolveRequiredPlaceholders((String) value); + value = this.resolveNestedPlaceholders((String) value); } if (debugEnabled) { logger.debug( diff --git a/spring-core/src/test/java/org/springframework/core/env/PropertySourcesPropertyResolverTests.java b/spring-core/src/test/java/org/springframework/core/env/PropertySourcesPropertyResolverTests.java index 09481c169c..3e35391aa6 100644 --- a/spring-core/src/test/java/org/springframework/core/env/PropertySourcesPropertyResolverTests.java +++ b/spring-core/src/test/java/org/springframework/core/env/PropertySourcesPropertyResolverTests.java @@ -20,8 +20,6 @@ import java.util.HashMap; import java.util.Map; import java.util.Properties; -import org.hamcrest.CoreMatchers; - import org.junit.Before; import org.junit.Test; @@ -364,7 +362,7 @@ public class PropertySourcesPropertyResolverTests { .withProperty("pL", "${pR}") // cyclic reference left .withProperty("pR", "${pL}") // cyclic reference right ); - PropertySourcesPropertyResolver pr = new PropertySourcesPropertyResolver(ps); + ConfigurablePropertyResolver pr = new PropertySourcesPropertyResolver(ps); assertThat(pr.getProperty("p1"), equalTo("v1")); assertThat(pr.getProperty("p2"), equalTo("v2")); assertThat(pr.getProperty("p3"), equalTo("v1:v2")); @@ -383,6 +381,45 @@ public class PropertySourcesPropertyResolverTests { } } + @Test + public void ignoreUnresolvableNestedPlaceholdersIsConfigurable() { + MutablePropertySources ps = new MutablePropertySources(); + ps.addFirst(new MockPropertySource() + .withProperty("p1", "v1") + .withProperty("p2", "v2") + .withProperty("p3", "${p1}:${p2}:${bogus:def}") // unresolvable w/ default + .withProperty("p4", "${p1}:${p2}:${bogus}") // unresolvable placeholder + ); + ConfigurablePropertyResolver pr = new PropertySourcesPropertyResolver(ps); + assertThat(pr.getProperty("p1"), equalTo("v1")); + assertThat(pr.getProperty("p2"), equalTo("v2")); + assertThat(pr.getProperty("p3"), equalTo("v1:v2:def")); + + // placeholders nested within the value of "p4" are unresolvable and cause an + // exception by default + try { + pr.getProperty("p4"); + } catch (IllegalArgumentException ex) { + assertThat(ex.getMessage(), containsString( + "Could not resolve placeholder 'bogus' in string value [${p1}:${p2}:${bogus}]")); + } + + // relax the treatment of unresolvable nested placeholders + pr.setIgnoreUnresolvableNestedPlaceholders(true); + // and observe they now pass through unresolved + assertThat(pr.getProperty("p4"), equalTo("v1:v2:${bogus}")); + + // resolve[Nested]Placeholders methods behave as usual regardless the value of + // ignoreUnresolvableNestedPlaceholders + assertThat(pr.resolvePlaceholders("${p1}:${p2}:${bogus}"), equalTo("v1:v2:${bogus}")); + try { + pr.resolveRequiredPlaceholders("${p1}:${p2}:${bogus}"); + } catch (IllegalArgumentException ex) { + assertThat(ex.getMessage(), containsString( + "Could not resolve placeholder 'bogus' in string value [${p1}:${p2}:${bogus}]")); + } + } + static interface SomeType { } static class SpecificType implements SomeType { }