From a0870c1c4ae2f260902d746a92e2a2df6ae18f1f Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 26 Aug 2015 17:22:05 +0100 Subject: [PATCH] Ignore binding failures for env vars and system properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1.3.0.M4 tightened up the configuration property binding such that ignoreUnknownFields = false now works as intended. Unfortunately, this has led to some complaints as applications that worked with 1.2.x have stopped working. For example, a SERVER_HOME environment variable does not cause a failure in 1.2.x despite there being no home property on ServerProperties but will now fail in 1.3.0.M4. This commit updates RelaxedDataBinder to ignore binding failures for properties that come from a source outside of the application’s control, namely environment variables and system properties. Closes gh-3775 --- .../boot/bind/RelaxedDataBinder.java | 30 +++++++++++++--- .../PropertiesConfigurationFactoryTests.java | 34 +++++++++++++++++++ 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/spring-boot/src/main/java/org/springframework/boot/bind/RelaxedDataBinder.java b/spring-boot/src/main/java/org/springframework/boot/bind/RelaxedDataBinder.java index cc2c9945a3..96f6b2a7b1 100644 --- a/spring-boot/src/main/java/org/springframework/boot/bind/RelaxedDataBinder.java +++ b/spring-boot/src/main/java/org/springframework/boot/bind/RelaxedDataBinder.java @@ -18,6 +18,7 @@ package org.springframework.boot.bind; import java.net.InetAddress; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; @@ -35,6 +36,7 @@ import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.NotWritablePropertyException; import org.springframework.beans.PropertyValue; import org.springframework.core.convert.TypeDescriptor; +import org.springframework.core.env.StandardEnvironment; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; @@ -54,6 +56,11 @@ import org.springframework.validation.DataBinder; */ public class RelaxedDataBinder extends DataBinder { + private static final Set BENIGN_PROPERTY_SOURCE_NAMES = Collections + .unmodifiableSet(new HashSet(Arrays.asList( + StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, + StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME))); + private static final Object BLANK = new Object(); private String namePrefix; @@ -254,11 +261,19 @@ public class RelaxedDataBinder extends DataBinder { } catch (NotWritablePropertyException ex) { PropertyOrigin origin = findPropertyOrigin(pv); - if (origin != null) { - throw new RelaxedBindingNotWritablePropertyException(ex, - origin); + if (isFatal(origin)) { + if (origin != null) { + throw new RelaxedBindingNotWritablePropertyException( + ex, origin); + } + else { + throw ex; + } + } + else { + logger.debug("Ignoring benign property binding failure", + ex); } - throw ex; } } }; @@ -271,6 +286,13 @@ public class RelaxedDataBinder extends DataBinder { }; } + private boolean isFatal(PropertyOrigin origin) { + if (origin == null) { + return true; + } + return !BENIGN_PROPERTY_SOURCE_NAMES.contains(origin.getSource().getName()); + } + private PropertyOrigin findPropertyOrigin(PropertyValue propertyValue) { if (propertyValue instanceof OriginCapablePropertyValue) { return ((OriginCapablePropertyValue) propertyValue).getOrigin(); 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 3eab669c4f..33f23dabf8 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 @@ -24,8 +24,11 @@ import javax.validation.constraints.NotNull; import org.junit.Test; import org.springframework.beans.NotWritablePropertyException; import org.springframework.context.support.StaticMessageSource; +import org.springframework.core.env.MutablePropertySources; +import org.springframework.core.env.StandardEnvironment; import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.support.PropertiesLoaderUtils; +import org.springframework.mock.env.MockPropertySource; import org.springframework.validation.BindException; import org.springframework.validation.Validator; import org.springframework.validation.beanvalidation.SpringValidatorAdapter; @@ -82,6 +85,37 @@ public class PropertiesConfigurationFactoryTests { bindFoo("bar: blah"); } + @Test + public void systemEnvironmentBindingFailuresAreIgnored() throws Exception { + setupFactory(); + MutablePropertySources propertySources = new MutablePropertySources(); + MockPropertySource propertySource = new MockPropertySource( + StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME); + propertySource.setProperty("doesNotExist", "foo"); + propertySource.setProperty("name", "bar"); + propertySources.addFirst(propertySource); + this.factory.setPropertySources(propertySources); + this.factory.setIgnoreUnknownFields(false); + this.factory.afterPropertiesSet(); + this.factory.getObject(); + Foo foo = this.factory.getObject(); + assertEquals("bar", foo.name); + } + + @Test + public void systemPropertyBindingFailuresAreIgnored() throws Exception { + setupFactory(); + MutablePropertySources propertySources = new MutablePropertySources(); + MockPropertySource propertySource = new MockPropertySource( + StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME); + propertySource.setProperty("doesNotExist", "foo"); + propertySource.setProperty("name", "bar"); + propertySources.addFirst(propertySource); + this.factory.setPropertySources(propertySources); + this.factory.setIgnoreUnknownFields(false); + this.factory.afterPropertiesSet(); + } + private Foo createFoo(final String values) throws Exception { setupFactory(); return bindFoo(values);