Skip to content

Commit

Permalink
Ignore top-level ConfigurationProperty binding failures
Browse files Browse the repository at this point in the history
  • Loading branch information
mbhave committed Mar 9, 2018
1 parent f47d60e commit 26811b8
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.bind.PropertySourcesPlaceholdersResolver;
import org.springframework.boot.context.properties.bind.handler.IgnoreErrorsBindHandler;
import org.springframework.boot.context.properties.bind.handler.IgnoreTopLevelConverterNotFoundBindHandler;
import org.springframework.boot.context.properties.bind.handler.NoUnboundElementsBindHandler;
import org.springframework.boot.context.properties.bind.validation.ValidationBindHandler;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
Expand Down Expand Up @@ -103,7 +104,7 @@ private List<Validator> getValidators(Bindable<?> target) {

private BindHandler getBindHandler(ConfigurationProperties annotation,
List<Validator> validators) {
BindHandler handler = BindHandler.DEFAULT;
BindHandler handler = new IgnoreTopLevelConverterNotFoundBindHandler();
if (annotation.ignoreInvalidFields()) {
handler = new IgnoreErrorsBindHandler(handler);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright 2012-2017 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.boot.context.properties.bind.handler;

import org.springframework.boot.context.properties.bind.AbstractBindHandler;
import org.springframework.boot.context.properties.bind.BindContext;
import org.springframework.boot.context.properties.bind.BindHandler;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
import org.springframework.core.convert.ConverterNotFoundException;

/**
* {@link BindHandler} that can be used to ignore top-level {@link ConverterNotFoundException}s.
*
* @author Madhura Bhave
* @since 2.0.0
*/
public class IgnoreTopLevelConverterNotFoundBindHandler extends AbstractBindHandler {

@Override
public Object onFailure(ConfigurationPropertyName name, Bindable<?> target,
BindContext context, Exception error) throws Exception {
if (context.getDepth() == 0 && error instanceof ConverterNotFoundException) {
return null;
}
throw error;
}

}


Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,11 @@ public void loadWhenBindingCurrentDirectoryToFileShouldBind() {
assertThat(bean.getFile()).isEqualTo(new File("."));
}

@Test
public void loadWhenTopLevelConverterNotFoundExceptionShouldNotFail() {
load(PersonProperties.class, "test=boot");
}

private AnnotationConfigApplicationContext load(Class<?> configuration,
String... inlinedProperties) {
return load(new Class<?>[] { configuration }, inlinedProperties);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* Copyright 2012-2017 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.boot.context.properties.bind.handler;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import org.springframework.boot.context.properties.bind.BindException;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.context.properties.source.MockConfigurationPropertySource;
import org.springframework.core.convert.ConverterNotFoundException;

import static org.hamcrest.Matchers.instanceOf;

/**
* Tests for {@link IgnoreTopLevelConverterNotFoundBindHandler}.
*
* @author Madhura Bhave
*/
public class IgnoreTopLevelConverterNotFoundBindHandlerTests {

@Rule
public ExpectedException thrown = ExpectedException.none();

private List<ConfigurationPropertySource> sources = new ArrayList<>();

private Binder binder;

@Before
public void setup() {
MockConfigurationPropertySource source = new MockConfigurationPropertySource();
source.put("example", "bar");
this.sources.add(source);
this.binder = new Binder(this.sources);
}

@After
public void tearDown() {
this.sources.clear();
}

@Test
public void bindWhenHandlerNotPresentShouldFail() {
this.thrown.expectCause(instanceOf(ConverterNotFoundException.class));
this.binder.bind("example", Bindable.of(Example.class));
}

@Test
public void bindWhenTopLevelContextAndExceptionIgnorableShouldNotFail() {
this.binder.bind("example", Bindable.of(Example.class), new IgnoreTopLevelConverterNotFoundBindHandler());
}

@Test
public void bindWhenExceptionNotIgnorableShouldNotFail() {
MockConfigurationPropertySource source = new MockConfigurationPropertySource();
source.put("example.foo", "1");
this.sources.add(source);
this.thrown.expectCause(instanceOf(IllegalStateException.class));
this.binder.bind("example", Bindable.of(Example.class), new IgnoreTopLevelConverterNotFoundBindHandler());
}

@Test
public void bindWhenExceptionInNestedContextShouldFail() {
MockConfigurationPropertySource source = new MockConfigurationPropertySource();
source.put("example.map", "hello");
this.sources.add(source);
this.thrown.expect(BindException.class);
this.thrown.expectCause(instanceOf(ConverterNotFoundException.class));
this.binder.bind("example", Bindable.of(Example.class), new IgnoreTopLevelConverterNotFoundBindHandler());
}

public static class Example {

private int foo;

private Map<String, String> map;

public int getFoo() {
return this.foo;
}

public void setFoo(int foo) {
throw new IllegalStateException();
}

public Map<String, String> getMap() {
return this.map;
}

public void setMap(Map<String, String> map) {
this.map = map;
}

}

}

0 comments on commit 26811b8

Please sign in to comment.