Skip to content

Commit 3091fee

Browse files
committed
SpringValidatorAdapter skips value retrieval for Set field without index
Issue: SPR-16177
1 parent 0e49e32 commit 3091fee

File tree

3 files changed

+365
-2
lines changed

3 files changed

+365
-2
lines changed

spring-context-support/src/test/java/org/springframework/validation/beanvalidation2/SpringValidatorAdapterTests.java

+180
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,16 @@
2121
import java.lang.annotation.Repeatable;
2222
import java.lang.annotation.Retention;
2323
import java.lang.annotation.Target;
24+
import java.lang.reflect.Field;
25+
import java.util.ArrayList;
2426
import java.util.Arrays;
2527
import java.util.HashMap;
28+
import java.util.LinkedHashSet;
29+
import java.util.LinkedList;
2630
import java.util.List;
2731
import java.util.Locale;
2832
import java.util.Map;
33+
import java.util.Set;
2934
import javax.validation.Constraint;
3035
import javax.validation.ConstraintValidator;
3136
import javax.validation.ConstraintValidatorContext;
@@ -148,6 +153,44 @@ public void testApplyMessageSourceResolvableToStringArgumentValueWithAlwaysUseMe
148153
is("Email required"));
149154
}
150155

156+
@Test // SPR-16177
157+
public void testWithList() {
158+
Parent parent = new Parent();
159+
parent.setName("Parent whit list");
160+
parent.getChildList().addAll(createChildren(parent));
161+
162+
BeanPropertyBindingResult errors = new BeanPropertyBindingResult(parent, "parent");
163+
validatorAdapter.validate(parent, errors);
164+
165+
assertTrue(errors.getErrorCount() > 0);
166+
}
167+
168+
@Test // SPR-16177
169+
public void testWithSet() {
170+
Parent parent = new Parent();
171+
parent.setName("Parent whith set");
172+
parent.getChildSet().addAll(createChildren(parent));
173+
174+
BeanPropertyBindingResult errors = new BeanPropertyBindingResult(parent, "parent");
175+
validatorAdapter.validate(parent, errors);
176+
177+
assertTrue(errors.getErrorCount() > 0);
178+
}
179+
180+
private List<Child> createChildren(Parent parent) {
181+
Child child1 = new Child();
182+
child1.setName("Child1");
183+
child1.setAge(null);
184+
child1.setParent(parent);
185+
186+
Child child2 = new Child();
187+
child2.setName(null);
188+
child2.setAge(17);
189+
child2.setParent(parent);
190+
191+
return Arrays.asList(child1, child2);
192+
}
193+
151194
@Test // SPR-15839
152195
public void testListElementConstraint() {
153196
BeanWithListElementConstraint bean = new BeanWithListElementConstraint();
@@ -308,6 +351,143 @@ public boolean isValid(Object value, ConstraintValidatorContext context) {
308351
}
309352

310353

354+
public static class Parent {
355+
356+
private Integer id;
357+
358+
@NotNull
359+
private String name;
360+
361+
@Valid
362+
private Set<Child> childSet = new LinkedHashSet<>();
363+
364+
@Valid
365+
private List<Child> childList = new LinkedList<>();
366+
367+
public Integer getId() {
368+
return id;
369+
}
370+
371+
public void setId(Integer id) {
372+
this.id = id;
373+
}
374+
375+
public String getName() {
376+
return name;
377+
}
378+
379+
public void setName(String name) {
380+
this.name = name;
381+
}
382+
383+
public Set<Child> getChildSet() {
384+
return childSet;
385+
}
386+
387+
public void setChildSet(Set<Child> childSet) {
388+
this.childSet = childSet;
389+
}
390+
391+
public List<Child> getChildList() {
392+
return childList;
393+
}
394+
395+
public void setChildList(List<Child> childList) {
396+
this.childList = childList;
397+
}
398+
}
399+
400+
401+
@AnythingValid
402+
public static class Child {
403+
404+
private Integer id;
405+
406+
@javax.validation.constraints.NotNull
407+
private String name;
408+
409+
@javax.validation.constraints.NotNull
410+
private Integer age;
411+
412+
@javax.validation.constraints.NotNull
413+
private Parent parent;
414+
415+
public Integer getId() {
416+
return id;
417+
}
418+
419+
public void setId(Integer id) {
420+
this.id = id;
421+
}
422+
423+
public String getName() {
424+
return name;
425+
}
426+
427+
public void setName(String name) {
428+
this.name = name;
429+
}
430+
431+
public Integer getAge() {
432+
return age;
433+
}
434+
435+
public void setAge(Integer age) {
436+
this.age = age;
437+
}
438+
439+
public Parent getParent() {
440+
return parent;
441+
}
442+
443+
public void setParent(Parent parent) {
444+
this.parent = parent;
445+
}
446+
}
447+
448+
449+
@Constraint(validatedBy = AnythingValidator.class)
450+
@Retention(RUNTIME)
451+
public @interface AnythingValid {
452+
453+
String message() default "{AnythingValid.message}";
454+
455+
Class<?>[] groups() default {};
456+
457+
Class<? extends Payload>[] payload() default {};
458+
}
459+
460+
461+
public static class AnythingValidator implements ConstraintValidator<AnythingValid, Object> {
462+
463+
private static final String ID = "id";
464+
465+
@Override
466+
public void initialize(AnythingValid constraintAnnotation) {
467+
}
468+
469+
@Override
470+
public boolean isValid(Object value, ConstraintValidatorContext context) {
471+
List<Field> fieldsErros = new ArrayList<>();
472+
Arrays.asList(value.getClass().getDeclaredFields()).forEach(f -> {
473+
f.setAccessible(true);
474+
try {
475+
if (!f.getName().equals(ID) && f.get(value) == null) {
476+
fieldsErros.add(f);
477+
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate())
478+
.addPropertyNode(f.getName())
479+
.addConstraintViolation();
480+
}
481+
} catch (IllegalAccessException ex) {
482+
throw new IllegalStateException(ex);
483+
}
484+
485+
});
486+
return fieldsErros.isEmpty();
487+
}
488+
}
489+
490+
311491
public class BeanWithListElementConstraint {
312492

313493
@Valid

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -285,8 +285,8 @@ protected MessageSourceResolvable getResolvableField(String objectName, String f
285285
@Nullable
286286
protected Object getRejectedValue(String field, ConstraintViolation<Object> violation, BindingResult bindingResult) {
287287
Object invalidValue = violation.getInvalidValue();
288-
if (!"".equals(field) && (invalidValue == violation.getLeafBean() ||
289-
(!field.contains("[]") && (field.contains("[") || field.contains("."))))) {
288+
if (!"".equals(field) && !field.contains("[]") &&
289+
(invalidValue == violation.getLeafBean() || field.contains("[") || field.contains("."))) {
290290
// Possibly a bean constraint with property path: retrieve the actual property value.
291291
// However, explicitly avoid this for "address[]" style paths that we can't handle.
292292
invalidValue = bindingResult.getRawFieldValue(field);

0 commit comments

Comments
 (0)