From 26a90c1d2470134b8ae88aaaaf31b19ddf1a3a60 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Tue, 20 Sep 2016 10:13:50 +0100 Subject: [PATCH] Add flag to PropertiesConfigurationFactory to switch off placeholder resolution The default behaviour is unchanged, but it is useful for some applications to be able to bind without placeholder resolution (e.g. to prevent exposing system environment variables, if the bound object is being sent over the wire). --- .../bind/PropertiesConfigurationFactory.java | 15 +++++++++- .../bind/PropertySourcesPropertyValues.java | 27 +++++++++++++---- .../PropertiesConfigurationFactoryTests.java | 29 +++++++++++++++++++ 3 files changed, 65 insertions(+), 6 deletions(-) diff --git a/spring-boot/src/main/java/org/springframework/boot/bind/PropertiesConfigurationFactory.java b/spring-boot/src/main/java/org/springframework/boot/bind/PropertiesConfigurationFactory.java index cb41a2268b8..8a160634bec 100644 --- a/spring-boot/src/main/java/org/springframework/boot/bind/PropertiesConfigurationFactory.java +++ b/spring-boot/src/main/java/org/springframework/boot/bind/PropertiesConfigurationFactory.java @@ -86,6 +86,8 @@ public class PropertiesConfigurationFactory private ConversionService conversionService; + private boolean resolvePlaceholders = true; + /** * Create a new {@link PropertiesConfigurationFactory} instance. * @param target the target object to bind too @@ -203,6 +205,16 @@ public class PropertiesConfigurationFactory this.exceptionIfInvalid = exceptionIfInvalid; } + /** + * Flag to indicate that placeholders should be replaced during binding. Default is + * true. + * + * @param resolvePlaceholders flag value + */ + public void setResolvePlaceholders(boolean resolvePlaceholders) { + this.resolvePlaceholders = resolvePlaceholders; + } + @Override public void afterPropertiesSet() throws Exception { bindPropertiesToTarget(); @@ -322,7 +334,8 @@ public class PropertiesConfigurationFactory Iterable relaxedTargetNames) { PropertyNamePatternsMatcher includes = getPropertyNamePatternsMatcher(names, relaxedTargetNames); - return new PropertySourcesPropertyValues(this.propertySources, names, includes); + return new PropertySourcesPropertyValues(this.propertySources, names, includes, + this.resolvePlaceholders); } private PropertyNamePatternsMatcher getPropertyNamePatternsMatcher(Set names, diff --git a/spring-boot/src/main/java/org/springframework/boot/bind/PropertySourcesPropertyValues.java b/spring-boot/src/main/java/org/springframework/boot/bind/PropertySourcesPropertyValues.java index 132ba8bd2d2..b385233f023 100644 --- a/spring-boot/src/main/java/org/springframework/boot/bind/PropertySourcesPropertyValues.java +++ b/spring-boot/src/main/java/org/springframework/boot/bind/PropertySourcesPropertyValues.java @@ -56,12 +56,15 @@ public class PropertySourcesPropertyValues implements PropertyValues { private final ConcurrentHashMap> collectionOwners = new ConcurrentHashMap>(); + private boolean resolvePlaceholders = true; + /** * Create a new PropertyValues from the given PropertySources. * @param propertySources a PropertySources instance */ public PropertySourcesPropertyValues(PropertySources propertySources) { - this(propertySources, (Collection) null, PropertyNamePatternsMatcher.ALL); + this(propertySources, (Collection) null, PropertyNamePatternsMatcher.ALL, + true); } /** @@ -76,7 +79,7 @@ public class PropertySourcesPropertyValues implements PropertyValues { Collection includePatterns, Collection nonEnumerableFallbackNames) { this(propertySources, nonEnumerableFallbackNames, - new PatternPropertyNamePatternsMatcher(includePatterns)); + new PatternPropertyNamePatternsMatcher(includePatterns), true); } /** @@ -85,15 +88,17 @@ public class PropertySourcesPropertyValues implements PropertyValues { * @param nonEnumerableFallbackNames the property names to try in lieu of an * {@link EnumerablePropertySource}. * @param includes the property name patterns to include + * @param resolvePlaceholders flag to indicate the placeholders should be resolved */ PropertySourcesPropertyValues(PropertySources propertySources, Collection nonEnumerableFallbackNames, - PropertyNamePatternsMatcher includes) { + PropertyNamePatternsMatcher includes, boolean resolvePlaceholders) { Assert.notNull(propertySources, "PropertySources must not be null"); Assert.notNull(includes, "Includes must not be null"); this.propertySources = propertySources; this.nonEnumerableFallbackNames = nonEnumerableFallbackNames; this.includes = includes; + this.resolvePlaceholders = resolvePlaceholders; PropertySourcesPropertyResolver resolver = new PropertySourcesPropertyResolver( propertySources); for (PropertySource source : propertySources) { @@ -101,6 +106,16 @@ public class PropertySourcesPropertyValues implements PropertyValues { } } + /** + * Flag to indicate that placeholders should be replaced during binding. Default is + * true. + * + * @param resolvePlaceholders flag value + */ + public void setResolvePlaceholders(boolean resolvePlaceholders) { + this.resolvePlaceholders = resolvePlaceholders; + } + private void processPropertySource(PropertySource source, PropertySourcesPropertyResolver resolver) { if (source instanceof CompositePropertySource) { @@ -138,12 +153,14 @@ public class PropertySourcesPropertyValues implements PropertyValues { private Object getEnumerableProperty(EnumerablePropertySource source, PropertySourcesPropertyResolver resolver, String propertyName) { try { - return resolver.getProperty(propertyName, Object.class); + if (this.resolvePlaceholders) { + return resolver.getProperty(propertyName, Object.class); + } } catch (RuntimeException ex) { // Probably could not resolve placeholders, ignore it here - return source.getProperty(propertyName); } + return source.getProperty(propertyName); } private void processNonEnumerablePropertySource(PropertySource source, diff --git a/spring-boot/src/test/java/org/springframework/boot/bind/PropertiesConfigurationFactoryTests.java b/spring-boot/src/test/java/org/springframework/boot/bind/PropertiesConfigurationFactoryTests.java index ac999c4acae..9a669cec6a3 100644 --- a/spring-boot/src/test/java/org/springframework/boot/bind/PropertiesConfigurationFactoryTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/bind/PropertiesConfigurationFactoryTests.java @@ -105,6 +105,35 @@ public class PropertiesConfigurationFactoryTests { assertThat(foo.name).isEqualTo("bar"); } + @Test + public void systemEnvironmentBindingWithDefaults() throws Exception { + setupFactory(); + MutablePropertySources propertySources = new MutablePropertySources(); + MockPropertySource propertySource = new MockPropertySource( + StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME); + propertySource.setProperty("name", "${foo.name:bar}"); + propertySources.addFirst(propertySource); + this.factory.setPropertySources(propertySources); + this.factory.afterPropertiesSet(); + Foo foo = this.factory.getObject(); + assertThat(foo.name).isEqualTo("bar"); + } + + @Test + public void systemEnvironmentNoResolvePlaceholders() throws Exception { + setupFactory(); + MutablePropertySources propertySources = new MutablePropertySources(); + MockPropertySource propertySource = new MockPropertySource( + StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME); + propertySource.setProperty("name", "${foo.name:bar}"); + propertySources.addFirst(propertySource); + this.factory.setPropertySources(propertySources); + this.factory.setResolvePlaceholders(false); + this.factory.afterPropertiesSet(); + Foo foo = this.factory.getObject(); + assertThat(foo.name).isEqualTo("${foo.name:bar}"); + } + @Test public void systemPropertyBindingFailuresAreIgnored() throws Exception { setupFactory();