@ -16,7 +16,10 @@
@@ -16,7 +16,10 @@
package org.springframework.boot.diagnostics.analyzer ;
import java.util.HashMap ;
import java.util.List ;
import java.util.Locale ;
import java.util.Map ;
import javax.validation.Valid ;
import javax.validation.constraints.Min ;
@ -32,6 +35,8 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
@@ -32,6 +35,8 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.boot.diagnostics.FailureAnalysis ;
import org.springframework.context.annotation.AnnotationConfigApplicationContext ;
import org.springframework.context.i18n.LocaleContextHolder ;
import org.springframework.core.env.MapPropertySource ;
import org.springframework.core.env.MutablePropertySources ;
import org.springframework.validation.Errors ;
import org.springframework.validation.Validator ;
import org.springframework.validation.annotation.Validated ;
@ -76,20 +81,60 @@ public class BindFailureAnalyzerTests {
@@ -76,20 +81,60 @@ public class BindFailureAnalyzerTests {
. contains ( "Reason: This object could not be bound." ) ;
}
@Test
public void bindExceptionWithOriginDueToValidationFailure ( ) throws Exception {
FailureAnalysis analysis = performAnalysis (
FieldValidationFailureConfiguration . class , "test.foo.value=4" ) ;
assertThat ( analysis . getDescription ( ) )
. contains ( "Origin: \"test.foo.value\" from property source \"test\"" ) ;
}
@Test
public void bindExceptionDueToUnboundElements ( ) throws Exception {
FailureAnalysis analysis = performAnalysis (
UnboundElementsFailureConfiguration . class , "test.foo.listValue[0]=hello" ,
"test.foo.listValue[2]=world" ) ;
assertThat ( analysis . getDescription ( ) ) . contains ( failure ( "test.foo.listvalue[2]" ,
"world" , "\"test.foo.listValue[2]\" from property source \"test\"" ,
"The elements [test.foo.listvalue[2]] were left unbound." ) ) ;
}
@Test
public void bindExceptionDueToOtherFailure ( ) throws Exception {
FailureAnalysis analysis = performAnalysis ( GenericFailureConfiguration . class ,
"test.foo.value=${BAR}" ) ;
assertThat ( analysis . getDescription ( ) ) . contains ( failure ( "test.foo.value" , "${BAR}" ,
"\"test.foo.value\" from property source \"test\"" ,
"Could not resolve placeholder 'BAR' in value \"${BAR}\"" ) ) ;
}
private static String failure ( String property , String value , String reason ) {
return String . format ( "Property: %s%n Value: %s%n Reason: %s" , property ,
value , reason ) ;
}
private FailureAnalysis performAnalysis ( Class < ? > configuration ) {
BeanCreationException failure = createFailure ( configuration ) ;
private static String failure ( String property , String value , String origin ,
String reason ) {
return String . format (
"Property: %s%n Value: %s%n Origin: %s%n Reason: %s" , property ,
value , origin , reason ) ;
}
private FailureAnalysis performAnalysis ( Class < ? > configuration ,
String . . . environment ) {
BeanCreationException failure = createFailure ( configuration , environment ) ;
assertThat ( failure ) . isNotNull ( ) ;
return new BindFailureAnalyzer ( ) . analyze ( failure ) ;
}
private BeanCreationException createFailure ( Class < ? > configuration ) {
private BeanCreationException createFailure ( Class < ? > configuration ,
String . . . environment ) {
try {
new AnnotationConfigApplicationContext ( configuration ) . close ( ) ;
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext ( ) ;
addEnvironment ( context , environment ) ;
context . register ( configuration ) ;
context . refresh ( ) ;
context . close ( ) ;
return null ;
}
catch ( BeanCreationException ex ) {
@ -97,6 +142,19 @@ public class BindFailureAnalyzerTests {
@@ -97,6 +142,19 @@ public class BindFailureAnalyzerTests {
}
}
private void addEnvironment ( AnnotationConfigApplicationContext context ,
String [ ] environment ) {
MutablePropertySources sources = context . getEnvironment ( ) . getPropertySources ( ) ;
Map < String , Object > map = new HashMap < > ( ) ;
for ( String pair : environment ) {
int index = pair . indexOf ( "=" ) ;
String key = pair . substring ( 0 , index > 0 ? index : pair . length ( ) ) ;
String value = index > 0 ? pair . substring ( index + 1 ) : "" ;
map . put ( key . trim ( ) , value . trim ( ) ) ;
}
sources . addFirst ( new MapPropertySource ( "test" , map ) ) ;
}
@EnableConfigurationProperties ( FieldValidationFailureProperties . class )
static class FieldValidationFailureConfiguration {
@ -107,6 +165,16 @@ public class BindFailureAnalyzerTests {
@@ -107,6 +165,16 @@ public class BindFailureAnalyzerTests {
}
@EnableConfigurationProperties ( UnboundElementsFailureProperties . class )
static class UnboundElementsFailureConfiguration {
}
@EnableConfigurationProperties ( GenericFailureProperties . class )
static class GenericFailureConfiguration {
}
@ConfigurationProperties ( "test.foo" )
@Validated
static class FieldValidationFailureProperties {
@ -162,6 +230,7 @@ public class BindFailureAnalyzerTests {
@@ -162,6 +230,7 @@ public class BindFailureAnalyzerTests {
}
@ConfigurationProperties ( "foo.bar" )
@Validated
static class ObjectErrorFailureProperties implements Validator {
@Override
@ -176,4 +245,33 @@ public class BindFailureAnalyzerTests {
@@ -176,4 +245,33 @@ public class BindFailureAnalyzerTests {
}
@ConfigurationProperties ( "test.foo" )
static class UnboundElementsFailureProperties {
private List < String > listValue ;
public List < String > getListValue ( ) {
return this . listValue ;
}
public void setListValue ( List < String > listValue ) {
this . listValue = listValue ;
}
}
@ConfigurationProperties ( "test.foo" )
static class GenericFailureProperties {
private String value ;
public String getValue ( ) {
return this . value ;
}
public void setValue ( String value ) {
this . value = value ;
}
}
}