I'm trying to set up JSR-303 validation of forms using Spring MVC. I have everything configured correctly (or at least I thought I did), and validations are working mostly correctly. However, if I have a command object that contains a Collection of objects that I want validated, and I annotate that Collection with @Valid, the Hibernate JSR-303 provider is not providing the correct propertyPath. The propertyPath inside the ContraintViolation object should (I think) be populated like list[0].bar and list[1].bar, but the Hibernate validator is simply providing list[].bar and list[].bar. This causes a NumberFormatException when Spring's SpringValidatorAdaptor.validate() method tries to add field level errors (since it internally expects an index to exist within those brackets).
Using spring-context-3.0.5 and hibernate-validator-4.1.0.Final (I also tried 4.0.2.GA and 4.2.0.Beta2 and got the same results), I wrote a small unit test to illustrate the issue:
Code:
package com.foo;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Valid;
import javax.validation.Validation;
import javax.validation.Validator;
import org.hibernate.validator.constraints.NotEmpty;
import org.junit.Test;
import org.springframework.util.AutoPopulatingList;
public class ValidatorTest {
class Person {
@NotEmpty
private String name;
@NotEmpty
@Valid
private Collection<Foo> list = new AutoPopulatingList<Foo>(Foo.class);
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public Collection<Foo> getList() {
return list;
}
public void setList(Collection<Foo> foos) {
this.list = foos;
}
}
class Foo {
@NotEmpty
private String bar;
public void setBar(String bar) {
this.bar = bar;
}
public String getBar() {
return bar;
}
}
@Test
public void testValidator() throws Exception {
Foo foo0 = new Foo();
foo0.setBar("");
Foo foo1 = new Foo();
foo1.setBar("");
Collection<Foo> list = new ArrayList<ValidatorTest.Foo>();
list.add(foo0);
list.add(foo1);
Person person = new Person();
person.setName("Test Person");
person.setList(list);
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Set<ConstraintViolation<Person>> violations = validator.validate(person);
for (ConstraintViolation<Person> constraintViolation : violations) {
System.out.println(constraintViolation);
}
}
}
The above test produces the following output (notice the propertyPath values with no indexes):
Code:
ConstraintViolationImpl{interpolatedMessage='may not be empty', propertyPath=list[].bar, rootBeanClass=class com.foo.ValidatorTest$Person, messageTemplate='{org.hibernate.validator.constraints.NotEmpty.message}'}
ConstraintViolationImpl{interpolatedMessage='may not be empty', propertyPath=list[].bar, rootBeanClass=class com.foo.ValidatorTest$Person, messageTemplate='{org.hibernate.validator.constraints.NotEmpty.message}'}
The validation errors that it produces are correct; however, the propertyPath isn't (at least from what I understand).
Now, if I replace Hibernate's JSR-303 implementation with Apache's (org.apache.bval.bundle-0.2-incubating--using the dependencies noted at
http://incubator.apache.org/bval/cwiki/downloads.html), I get output that I expect. This is the exact same test, but using Apache's JSR-303 annotations instead of Hibernate's. Note the indexes that now exist in the propertyPath field:
Code:
ConstraintViolationImpl{rootBean=com.foo.ValidatorTest$Person@5f989f84, propertyPath='list[0].bar', message='may not be empty', leafBean=com.foo.ValidatorTest$Foo@4393722c, value=}
ConstraintViolationImpl{rootBean=com.foo.ValidatorTest$Person@5f989f84, propertyPath='list[1].bar', message='may not be empty', leafBean=com.foo.ValidatorTest$Foo@528acf6e, value=}
I'd prefer to stick with hibernate-validator since Apache's JSR-303 implementation is still in incubation. Is there something that I'm doing that is causing the Hibernate validator not to populate those indexes? I'm trying to keep from doing manual error handling in my Spring controllers as much as possible.
I can provide the Apache Bean Validation version of the test if needed; I just didn't want to clutter up this post since it's exactly the same aside from an import statement.
Thanks for any help that you're able to provide!