Skip to content

Commit 8e75eee

Browse files
jhoellerunknown
authored andcommitted
SpringValidationAdapter properly detects invalid value for JSR-303 field-level bean constraints
Issue: SPR-9332
1 parent 5a773b7 commit 8e75eee

File tree

2 files changed

+74
-11
lines changed

2 files changed

+74
-11
lines changed

spring-context/src/main/java/org/springframework/validation/beanvalidation/SpringValidatorAdapter.java

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2013 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -99,7 +99,8 @@ public void validate(Object target, Errors errors, Object... validationHints) {
9999
}
100100
}
101101
}
102-
processConstraintViolations(this.targetValidator.validate(target, groups.toArray(new Class[groups.size()])), errors);
102+
processConstraintViolations(
103+
this.targetValidator.validate(target, groups.toArray(new Class[groups.size()])), errors);
103104
}
104105

105106
/**
@@ -114,10 +115,11 @@ protected void processConstraintViolations(Set<ConstraintViolation<Object>> viol
114115
FieldError fieldError = errors.getFieldError(field);
115116
if (fieldError == null || !fieldError.isBindingFailure()) {
116117
try {
117-
String errorCode = violation.getConstraintDescriptor().getAnnotation().annotationType().getSimpleName();
118-
Object[] errorArgs = getArgumentsForConstraint(errors.getObjectName(), field, violation.getConstraintDescriptor());
118+
ConstraintDescriptor<?> cd = violation.getConstraintDescriptor();
119+
String errorCode = cd.getAnnotation().annotationType().getSimpleName();
120+
Object[] errorArgs = getArgumentsForConstraint(errors.getObjectName(), field, cd);
119121
if (errors instanceof BindingResult) {
120-
// can do custom FieldError registration with invalid value from ConstraintViolation,
122+
// Can do custom FieldError registration with invalid value from ConstraintViolation,
121123
// as necessary for Hibernate Validator compatibility (non-indexed set path in field)
122124
BindingResult bindingResult = (BindingResult) errors;
123125
String nestedField = bindingResult.getNestedPath() + field;
@@ -128,8 +130,9 @@ protected void processConstraintViolations(Set<ConstraintViolation<Object>> viol
128130
}
129131
else {
130132
Object invalidValue = violation.getInvalidValue();
131-
if (!"".equals(field) && invalidValue == violation.getLeafBean()) {
132-
// bean constraint with property path: retrieve the actual property value
133+
if (field.contains(".") && !field.contains("[]")) {
134+
// Possibly a bean constraint with property path: retrieve the actual property value.
135+
// However, explicitly avoid this for "address[]" style paths that we can't handle.
133136
invalidValue = bindingResult.getRawFieldValue(field);
134137
}
135138
String[] errorCodes = bindingResult.resolveMessageCodes(errorCode, field);

spring-context/src/test/java/org/springframework/validation/beanvalidation/ValidatorFactoryTests.java

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2013 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -30,13 +30,15 @@
3030
import javax.validation.ConstraintValidator;
3131
import javax.validation.ConstraintValidatorContext;
3232
import javax.validation.ConstraintViolation;
33+
import javax.validation.Payload;
3334
import javax.validation.Valid;
3435
import javax.validation.constraints.NotNull;
3536

3637
import org.hibernate.validator.HibernateValidator;
3738
import org.junit.Test;
3839

3940
import org.springframework.validation.BeanPropertyBindingResult;
41+
import org.springframework.validation.Errors;
4042
import org.springframework.validation.FieldError;
4143
import org.springframework.validation.ObjectError;
4244

@@ -193,6 +195,18 @@ public void testSpringValidationWithErrorInSetElement() throws Exception {
193195
System.out.println(fieldError.getDefaultMessage());
194196
}
195197

198+
@Test
199+
public void testInnerBeanValidation() throws Exception {
200+
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
201+
validator.afterPropertiesSet();
202+
203+
MainBean mainBean = new MainBean();
204+
Errors errors = new BeanPropertyBindingResult(mainBean, "mainBean");
205+
validator.validate(mainBean, errors);
206+
Object rejected = errors.getFieldValue("inner.value");
207+
assertNull(rejected);
208+
}
209+
196210

197211
@NameAddressValid
198212
public static class ValidPerson {
@@ -242,7 +256,6 @@ public void setAddressSet(Set<ValidAddress> addressSet) {
242256
}
243257
}
244258

245-
246259
public static class ValidAddress {
247260

248261
@NotNull
@@ -257,7 +270,6 @@ public void setStreet(String street) {
257270
}
258271
}
259272

260-
261273
@Target(ElementType.TYPE)
262274
@Retention(RetentionPolicy.RUNTIME)
263275
@Constraint(validatedBy = NameAddressValidator.class)
@@ -270,7 +282,6 @@ public void setStreet(String street) {
270282
Class<?>[] payload() default {};
271283
}
272284

273-
274285
public static class NameAddressValidator implements ConstraintValidator<NameAddressValid, ValidPerson> {
275286

276287
@Override
@@ -283,4 +294,53 @@ public boolean isValid(ValidPerson value, ConstraintValidatorContext constraintV
283294
}
284295
}
285296

297+
298+
public static class MainBean {
299+
300+
@InnerValid
301+
private InnerBean inner = new InnerBean();
302+
303+
public InnerBean getInner() {
304+
return inner;
305+
}
306+
}
307+
308+
public static class InnerBean {
309+
310+
private String value;
311+
312+
public String getValue() {
313+
return value;
314+
}
315+
public void setValue(String value) {
316+
this.value = value;
317+
}
318+
}
319+
320+
@Retention(RetentionPolicy.RUNTIME)
321+
@Target(ElementType.FIELD)
322+
@Constraint(validatedBy=InnerValidator.class)
323+
public static @interface InnerValid {
324+
String message() default "NOT VALID";
325+
Class<?>[] groups() default { };
326+
Class<? extends Payload>[] payload() default {};
327+
}
328+
329+
public static class InnerValidator implements ConstraintValidator<InnerValid, InnerBean> {
330+
331+
@Override
332+
public void initialize(InnerValid constraintAnnotation) {
333+
}
334+
335+
@Override
336+
public boolean isValid(InnerBean bean, ConstraintValidatorContext context) {
337+
context.disableDefaultConstraintViolation();
338+
if (bean.getValue() == null) {
339+
context.buildConstraintViolationWithTemplate("NULL"). addNode("value").addConstraintViolation();
340+
return false;
341+
}
342+
return true;
343+
}
344+
}
345+
286346
}

0 commit comments

Comments
 (0)