Hibernate Books

All times are UTC - 5 hours [ DST ]



Post new topic Reply to topic  [ 6 posts ] 
Author Message
 Post subject: Hibernate JSR303 validation and generated propertyPath
PostPosted: Wed Apr 20, 2011 2:20 pm 
Newbie

Joined: Wed Apr 20, 2011 1:52 pm
Posts: 2
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!


Top
 Profile  
 
 Post subject: Re: Hibernate JSR303 validation and generated propertyPath
PostPosted: Wed Apr 20, 2011 5:45 pm 
Hibernate Team
Hibernate Team

Joined: Sat Jan 24, 2009 12:46 pm
Posts: 176
Hi,

I think declaring the field "list" with type java.util.List (instead of j.u.Collection) should do the trick (btw. List/Set should IMO usually be preferred over j.u.Collection as they make clear for the user, whether duplicates are allowed in a given collection or not).

Nevertheless this situation raises an interesting question, namely whether the static or the runtime type of an association should be considered when building the property path for constraint violations. HV currently uses the static type (in your example j.u.Collection). Following to the BV spec. (section 4.5) the node representing the "list" field is iterable (j.u.Collection extends j.l.Iterable) but it has no index (j.u.Collection is no list nor an an array). This results in the String representation you got (you could get this information also in a safe way using Node#isInIterable() and Node#getIndex()).

That said, basing the path on the runtime type (here AutoPopulatingList, which implements j.u.List) instead would seem reasonable to me, too. I quickly scanned through the spec. and didn't find a definite answer on this. I'll discuss this with the team :-)

Gunnar

_________________
Visit my blog at http://musingsofaprogrammingaddict.blogspot.com/


Top
 Profile  
 
 Post subject: Re: Hibernate JSR303 validation and generated propertyPath
PostPosted: Wed Apr 20, 2011 6:18 pm 
Newbie

Joined: Wed Apr 20, 2011 1:52 pm
Posts: 2
Gunnar wrote:
I think declaring the field "list" with type java.util.List (instead of j.u.Collection) should do the trick (btw. List/Set should IMO usually be preferred over j.u.Collection as they make clear for the user, whether duplicates are allowed in a given collection or not).


Awesome! That was exactly what I was looking for! And I can confirm that I am now getting expected results. Thanks... you just made my day. :-)

Here's the new test that illustrates the better output:
Code:
package com.foo;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
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 List<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(List<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("");

        List<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);
        }
    }
}


And the expected output:
Code:
ConstraintViolationImpl{interpolatedMessage='may not be empty', propertyPath=list[1].bar, rootBeanClass=class com.foo.ValidatorTest$Person, messageTemplate='{org.hibernate.validator.constraints.NotEmpty.message}'}
ConstraintViolationImpl{interpolatedMessage='may not be empty', propertyPath=list[0].bar, rootBeanClass=class com.foo.ValidatorTest$Person, messageTemplate='{org.hibernate.validator.constraints.NotEmpty.message}'}


Top
 Profile  
 
 Post subject: Re: Hibernate JSR303 validation and generated propertyPath
PostPosted: Thu Apr 21, 2011 5:25 pm 
Hibernate Team
Hibernate Team

Joined: Sat Jan 24, 2009 12:46 pm
Posts: 176
Glad to hear that I could help :-) I also created a JIRA issue (BVAL-226) to have this behavior clearly specified in the next version of the Bean Validation specification.

_________________
Visit my blog at http://musingsofaprogrammingaddict.blogspot.com/


Top
 Profile  
 
 Post subject: Re: Hibernate JSR303 validation and generated propertyPath
PostPosted: Thu Sep 22, 2011 7:01 am 
Newbie

Joined: Wed Sep 21, 2011 9:34 am
Posts: 6
Hello!
I would like to use HibernateValidation with RichFaces. My custom ConstraintValidatorFactory shall be initialised by Spring so I can inject i.e. DAOs.
Unfortunately
ValidatorFactory validatorFactory = Validation.byDefaultProvider().configure()
.constraintValidatorFactory(this).buildValidatorFactory();
validator = validatorFactory.usingContext().getValidator();
does not seem to affect the standard way to get a validator
Validation.buildDefaultValidatorFactory().getValidator();
Maybe I'm totally wrong, but I would like to set my custom ConstraintValidator as default ConstraintValidator so RichFaces (and of course my tests) get a validator with a DAO.
Do I have a complete misunderstanding of the HibernateValidators?


Last edited by Celestejonas on Tue Oct 25, 2011 2:07 am, edited 1 time in total.

Top
 Profile  
 
 Post subject: Re: Hibernate JSR303 validation and generated propertyPath
PostPosted: Thu Sep 22, 2011 3:18 pm 
Hibernate Team
Hibernate Team

Joined: Sat Jan 24, 2009 12:46 pm
Posts: 176
Hi,

any configurations made using the context API apply only to that particular configured factory. So if you are retrieving another validator using the default validator factory you'll just get a default validator. You can learn more about the bootstrapping of validators and validator factories in the BV specification.

In order to configure the default validator returned by Validation.buildDefaultValidatorFactory().getValidator(); you can use the XML based configuration as described here. The constrained validator factory can be set by creating the file META-INF/validation.xml with the following contents:

Code:
<?xml version="1.0" encoding="UTF-8"?>
<validation-config xmlns="http://jboss.org/xml/ns/javax/validation/configuration" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://jboss.org/xml/ns/javax/validation/configuration validation-configuration-1.0.xsd">

    <constraint-validator-factory>
        your.custom.InjectingConstraintValidatorFactory
    </constraint-validator-factory>

</validation-config>

Note that Spring 3.x also provides out-of-the-box support for Bean Validation. When you're setting up your validator via Spring it will provide DI services within validator implementations by default.

Hope that helps,

--Gunnar

PS: Please prefer creating new threads for new questions instead of resurrecting unrelated old threads.

_________________
Visit my blog at http://musingsofaprogrammingaddict.blogspot.com/


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 6 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.