From 8e75eee9b2b35f807fefe9575e59be5e2f4768eb Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 22 Jan 2013 16:46:01 +0100 Subject: [PATCH] SpringValidationAdapter properly detects invalid value for JSR-303 field-level bean constraints Issue: SPR-9332 --- .../SpringValidatorAdapter.java | 17 +++-- .../beanvalidation/ValidatorFactoryTests.java | 68 +++++++++++++++++-- 2 files changed, 74 insertions(+), 11 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/validation/beanvalidation/SpringValidatorAdapter.java b/spring-context/src/main/java/org/springframework/validation/beanvalidation/SpringValidatorAdapter.java index 1a51c37bb9b..7565e82099c 100644 --- a/spring-context/src/main/java/org/springframework/validation/beanvalidation/SpringValidatorAdapter.java +++ b/spring-context/src/main/java/org/springframework/validation/beanvalidation/SpringValidatorAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -99,7 +99,8 @@ public class SpringValidatorAdapter implements SmartValidator, javax.validation. } } } - processConstraintViolations(this.targetValidator.validate(target, groups.toArray(new Class[groups.size()])), errors); + processConstraintViolations( + this.targetValidator.validate(target, groups.toArray(new Class[groups.size()])), errors); } /** @@ -114,10 +115,11 @@ public class SpringValidatorAdapter implements SmartValidator, javax.validation. FieldError fieldError = errors.getFieldError(field); if (fieldError == null || !fieldError.isBindingFailure()) { try { - String errorCode = violation.getConstraintDescriptor().getAnnotation().annotationType().getSimpleName(); - Object[] errorArgs = getArgumentsForConstraint(errors.getObjectName(), field, violation.getConstraintDescriptor()); + ConstraintDescriptor cd = violation.getConstraintDescriptor(); + String errorCode = cd.getAnnotation().annotationType().getSimpleName(); + Object[] errorArgs = getArgumentsForConstraint(errors.getObjectName(), field, cd); if (errors instanceof BindingResult) { - // can do custom FieldError registration with invalid value from ConstraintViolation, + // Can do custom FieldError registration with invalid value from ConstraintViolation, // as necessary for Hibernate Validator compatibility (non-indexed set path in field) BindingResult bindingResult = (BindingResult) errors; String nestedField = bindingResult.getNestedPath() + field; @@ -128,8 +130,9 @@ public class SpringValidatorAdapter implements SmartValidator, javax.validation. } else { Object invalidValue = violation.getInvalidValue(); - if (!"".equals(field) && invalidValue == violation.getLeafBean()) { - // bean constraint with property path: retrieve the actual property value + if (field.contains(".") && !field.contains("[]")) { + // Possibly a bean constraint with property path: retrieve the actual property value. + // However, explicitly avoid this for "address[]" style paths that we can't handle. invalidValue = bindingResult.getRawFieldValue(field); } String[] errorCodes = bindingResult.resolveMessageCodes(errorCode, field); diff --git a/spring-context/src/test/java/org/springframework/validation/beanvalidation/ValidatorFactoryTests.java b/spring-context/src/test/java/org/springframework/validation/beanvalidation/ValidatorFactoryTests.java index 5c2dad22a4b..3e6cac57598 100644 --- a/spring-context/src/test/java/org/springframework/validation/beanvalidation/ValidatorFactoryTests.java +++ b/spring-context/src/test/java/org/springframework/validation/beanvalidation/ValidatorFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -30,6 +30,7 @@ import javax.validation.Constraint; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import javax.validation.ConstraintViolation; +import javax.validation.Payload; import javax.validation.Valid; import javax.validation.constraints.NotNull; @@ -37,6 +38,7 @@ import org.hibernate.validator.HibernateValidator; import org.junit.Test; import org.springframework.validation.BeanPropertyBindingResult; +import org.springframework.validation.Errors; import org.springframework.validation.FieldError; import org.springframework.validation.ObjectError; @@ -193,6 +195,18 @@ public class ValidatorFactoryTests { System.out.println(fieldError.getDefaultMessage()); } + @Test + public void testInnerBeanValidation() throws Exception { + LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean(); + validator.afterPropertiesSet(); + + MainBean mainBean = new MainBean(); + Errors errors = new BeanPropertyBindingResult(mainBean, "mainBean"); + validator.validate(mainBean, errors); + Object rejected = errors.getFieldValue("inner.value"); + assertNull(rejected); + } + @NameAddressValid public static class ValidPerson { @@ -242,7 +256,6 @@ public class ValidatorFactoryTests { } } - public static class ValidAddress { @NotNull @@ -257,7 +270,6 @@ public class ValidatorFactoryTests { } } - @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = NameAddressValidator.class) @@ -270,7 +282,6 @@ public class ValidatorFactoryTests { Class[] payload() default {}; } - public static class NameAddressValidator implements ConstraintValidator { @Override @@ -283,4 +294,53 @@ public class ValidatorFactoryTests { } } + + public static class MainBean { + + @InnerValid + private InnerBean inner = new InnerBean(); + + public InnerBean getInner() { + return inner; + } + } + + public static class InnerBean { + + private String value; + + public String getValue() { + return value; + } + public void setValue(String value) { + this.value = value; + } + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.FIELD) + @Constraint(validatedBy=InnerValidator.class) + public static @interface InnerValid { + String message() default "NOT VALID"; + Class[] groups() default { }; + Class[] payload() default {}; + } + + public static class InnerValidator implements ConstraintValidator { + + @Override + public void initialize(InnerValid constraintAnnotation) { + } + + @Override + public boolean isValid(InnerBean bean, ConstraintValidatorContext context) { + context.disableDefaultConstraintViolation(); + if (bean.getValue() == null) { + context.buildConstraintViolationWithTemplate("NULL"). addNode("value").addConstraintViolation(); + return false; + } + return true; + } + } + }