@ -16,10 +16,11 @@
package org.springframework.boot.context.properties.bind.validation ;
package org.springframework.boot.context.properties.bind.validation ;
import java.util.Arrays ;
import java.util.Deque ;
import java.util.Deque ;
import java.util.LinkedHashMap ;
import java.util.LinkedHashSet ;
import java.util.LinkedHashSet ;
import java.util.LinkedList ;
import java.util.LinkedList ;
import java.util.Map ;
import java.util.Set ;
import java.util.Set ;
import java.util.stream.Collectors ;
import java.util.stream.Collectors ;
@ -29,8 +30,8 @@ import org.springframework.boot.context.properties.bind.BindHandler;
import org.springframework.boot.context.properties.bind.Bindable ;
import org.springframework.boot.context.properties.bind.Bindable ;
import org.springframework.boot.context.properties.source.ConfigurationProperty ;
import org.springframework.boot.context.properties.source.ConfigurationProperty ;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName ;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName ;
import org.springframework.validation.BeanPropertyBindingResult ;
import org.springframework.core.ResolvableType ;
import org.springframework.validation.BindingResult ;
import org.springframework.validation.Abstract BindingResult ;
import org.springframework.validation.Validator ;
import org.springframework.validation.Validator ;
/ * *
/ * *
@ -44,6 +45,10 @@ public class ValidationBindHandler extends AbstractBindHandler {
private final Validator [ ] validators ;
private final Validator [ ] validators ;
private final Map < ConfigurationPropertyName , ResolvableType > boundTypes = new LinkedHashMap < > ( ) ;
private final Map < ConfigurationPropertyName , Object > boundResults = new LinkedHashMap < > ( ) ;
private final Set < ConfigurationProperty > boundProperties = new LinkedHashSet < > ( ) ;
private final Set < ConfigurationProperty > boundProperties = new LinkedHashSet < > ( ) ;
private final Deque < BindValidationException > exceptions = new LinkedList < > ( ) ;
private final Deque < BindValidationException > exceptions = new LinkedList < > ( ) ;
@ -57,8 +62,15 @@ public class ValidationBindHandler extends AbstractBindHandler {
this . validators = validators ;
this . validators = validators ;
}
}
@Override
public < T > Bindable < T > onStart ( ConfigurationPropertyName name , Bindable < T > target , BindContext context ) {
this . boundTypes . put ( name , target . getType ( ) ) ;
return super . onStart ( name , target , context ) ;
}
@Override
@Override
public Object onSuccess ( ConfigurationPropertyName name , Bindable < ? > target , BindContext context , Object result ) {
public Object onSuccess ( ConfigurationPropertyName name , Bindable < ? > target , BindContext context , Object result ) {
this . boundResults . put ( name , result ) ;
if ( context . getConfigurationProperty ( ) ! = null ) {
if ( context . getConfigurationProperty ( ) ! = null ) {
this . boundProperties . add ( context . getConfigurationProperty ( ) ) ;
this . boundProperties . add ( context . getConfigurationProperty ( ) ) ;
}
}
@ -70,12 +82,20 @@ public class ValidationBindHandler extends AbstractBindHandler {
throws Exception {
throws Exception {
Object result = super . onFailure ( name , target , context , error ) ;
Object result = super . onFailure ( name , target , context , error ) ;
if ( result ! = null ) {
if ( result ! = null ) {
this . exceptions . clear ( ) ;
clear ( ) ;
this . boundResults . put ( name , result ) ;
}
}
validate ( name , target , context , result ) ;
validate ( name , target , context , result ) ;
return result ;
return result ;
}
}
private void clear ( ) {
this . boundTypes . clear ( ) ;
this . boundResults . clear ( ) ;
this . boundProperties . clear ( ) ;
this . exceptions . clear ( ) ;
}
@Override
@Override
public void onFinish ( ConfigurationPropertyName name , Bindable < ? > target , BindContext context , Object result )
public void onFinish ( ConfigurationPropertyName name , Bindable < ? > target , BindContext context , Object result )
throws Exception {
throws Exception {
@ -105,20 +125,78 @@ public class ValidationBindHandler extends AbstractBindHandler {
}
}
private void validateAndPush ( ConfigurationPropertyName name , Object target , Class < ? > type ) {
private void validateAndPush ( ConfigurationPropertyName name , Object target , Class < ? > type ) {
BindingResult errors = new BeanPropertyBindingResult ( target , name . toString ( ) ) ;
ValidationResult result = null ;
Arrays . stream ( this . validators ) . filter ( ( validator ) - > validator . supports ( type ) )
for ( Validator validator : this . validators ) {
. forEach ( ( validator ) - > validator . validate ( target , errors ) ) ;
if ( validator . supports ( type ) ) {
if ( errors . hasErrors ( ) ) {
result = ( result ! = null ) ? result : new ValidationResult ( name , target ) ;
this . exceptions . push ( getBindValidationException ( name , errors ) ) ;
validator . validate ( target , result ) ;
}
}
}
if ( result ! = null & & result . hasErrors ( ) ) {
this . exceptions . push ( new BindValidationException ( result . getValidationErrors ( ) ) ) ;
}
}
/ * *
* { @link AbstractBindingResult } implementation backed by the bound properties .
* /
private class ValidationResult extends AbstractBindingResult {
private final ConfigurationPropertyName name ;
private Object target ;
protected ValidationResult ( ConfigurationPropertyName name , Object target ) {
super ( null ) ;
this . name = name ;
this . target = target ;
}
@Override
public String getObjectName ( ) {
return this . name . toString ( ) ;
}
}
private BindValidationException getBindValidationException ( ConfigurationPropertyName name , BindingResult errors ) {
@Override
Set < ConfigurationProperty > boundProperties = this . boundProperties . stream ( )
public Object getTarget ( ) {
. filter ( ( property ) - > name . isAncestorOf ( property . getName ( ) ) )
return this . target ;
}
@Override
public Class < ? > getFieldType ( String field ) {
try {
ResolvableType type = ValidationBindHandler . this . boundTypes . get ( getName ( field ) ) ;
Class < ? > resolved = ( type ! = null ) ? type . resolve ( ) : null ;
if ( resolved ! = null ) {
return resolved ;
}
}
catch ( Exception ex ) {
}
return super . getFieldType ( field ) ;
}
@Override
protected Object getActualFieldValue ( String field ) {
try {
return ValidationBindHandler . this . boundResults . get ( getName ( field ) ) ;
}
catch ( Exception ex ) {
}
return null ;
}
private ConfigurationPropertyName getName ( String field ) {
return this . name . append ( field ) ;
}
ValidationErrors getValidationErrors ( ) {
Set < ConfigurationProperty > boundProperties = ValidationBindHandler . this . boundProperties . stream ( )
. filter ( ( property ) - > this . name . isAncestorOf ( property . getName ( ) ) )
. collect ( Collectors . toCollection ( LinkedHashSet : : new ) ) ;
. collect ( Collectors . toCollection ( LinkedHashSet : : new ) ) ;
ValidationErrors validationErrors = new ValidationErrors ( name , boundProperties , errors . getAllErrors ( ) ) ;
return new ValidationErrors ( this . name , boundProperties , getAllErrors ( ) ) ;
return new BindValidationException ( validationErrors ) ;
}
}
}
}
}