Ignore binding failures for env vars and system properties

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
pull/3535/merge
Andy Wilkinson 9 years ago
parent d74d657372
commit a0870c1c4a

@ -18,6 +18,7 @@ package org.springframework.boot.bind;
import java.net.InetAddress; import java.net.InetAddress;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
@ -35,6 +36,7 @@ import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.NotWritablePropertyException; import org.springframework.beans.NotWritablePropertyException;
import org.springframework.beans.PropertyValue; import org.springframework.beans.PropertyValue;
import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -54,6 +56,11 @@ import org.springframework.validation.DataBinder;
*/ */
public class RelaxedDataBinder extends DataBinder { public class RelaxedDataBinder extends DataBinder {
private static final Set<String> BENIGN_PROPERTY_SOURCE_NAMES = Collections
.unmodifiableSet(new HashSet<String>(Arrays.asList(
StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,
StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME)));
private static final Object BLANK = new Object(); private static final Object BLANK = new Object();
private String namePrefix; private String namePrefix;
@ -254,11 +261,19 @@ public class RelaxedDataBinder extends DataBinder {
} }
catch (NotWritablePropertyException ex) { catch (NotWritablePropertyException ex) {
PropertyOrigin origin = findPropertyOrigin(pv); PropertyOrigin origin = findPropertyOrigin(pv);
if (origin != null) { if (isFatal(origin)) {
throw new RelaxedBindingNotWritablePropertyException(ex, if (origin != null) {
origin); 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) { private PropertyOrigin findPropertyOrigin(PropertyValue propertyValue) {
if (propertyValue instanceof OriginCapablePropertyValue) { if (propertyValue instanceof OriginCapablePropertyValue) {
return ((OriginCapablePropertyValue) propertyValue).getOrigin(); return ((OriginCapablePropertyValue) propertyValue).getOrigin();

@ -24,8 +24,11 @@ import javax.validation.constraints.NotNull;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.NotWritablePropertyException; import org.springframework.beans.NotWritablePropertyException;
import org.springframework.context.support.StaticMessageSource; 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.ByteArrayResource;
import org.springframework.core.io.support.PropertiesLoaderUtils; import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.mock.env.MockPropertySource;
import org.springframework.validation.BindException; import org.springframework.validation.BindException;
import org.springframework.validation.Validator; import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.SpringValidatorAdapter; import org.springframework.validation.beanvalidation.SpringValidatorAdapter;
@ -82,6 +85,37 @@ public class PropertiesConfigurationFactoryTests {
bindFoo("bar: blah"); 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 { private Foo createFoo(final String values) throws Exception {
setupFactory(); setupFactory();
return bindFoo(values); return bindFoo(values);

Loading…
Cancel
Save