Help

This is the third installment of a series of blog entries on the Bean Validation specification (JSR 303). If you have not done it yet, go read the first and second part.

The Bean Validation specification let's you define constraints on your domain model. In a couple of situations, validating a subset of constraints is needed.

In a wizard-style screen, the information is partially distilled over time from screen to screen. Validating data for each screen is useful but as a whole, the data is not ready for a complete constraint validation.

Some constraints need to run after others. There are several use cases for ordering constraints:

  • higher level constraints may expect data pre validated by basic constraints before being triggered themselves
  • some constraints are expensive in CPU, memory, time or even money. Running the previous constraints first and fail fast is required
  • some use cases need to validate additional or specific constraints not necessary for others use cases

To solve this class of problems, the Bean Validation specification uses the notion of group. Each constraint can define a set of groups it belongs to. When it comes to validate an object graph, one or several groups can be requested.

The principles

Let's first describe the theory behind groups

Declare a constraint as belonging to a group

Each constraint has a groups element whose default value is an empty array. When this element is not explicitly declared on an constraint annotation, the default group is assumed

public class Address {
        //not null belongs to the "default" group
        @NotNull String street1;
}

Any constraint declaration can define one or several groups it belongs to. Note that default is the special default group.

public class Address {
        @NotNull(groups={"basic", "default"}) private String street1;
}

In this example, @NotNull is checked any time either the basic or default group is requested.

Defining constraint ordering

Ordering constraints for the sake of ordering the execution does not make a lot of sense since the Bean Validation implementation ends up executing all the constraints and return all the failures (for a given set of groups). However, it is useful to have the ability to avoid the execution of a constraint if an other constraint fails.

The Bean Validation specification uses the notion of a group sequence. A group sequence declares a list of groups. Each group must be validated sequentially following the list order. Validating a group means validating all constraints of an object graph belonging to the group. If one or more constraints fail in a given group, the following groups in the group sequence are not processed.

A group sequence is declared with @GroupSequence and is applied on the class it is set on.

@GroupSequence(name="default", sequence={"basic", "complex"})
@AddressChecker(groups="complex")
public class Address {
        @NotNull(groups="basic") private String street1;
        ...
}

In an object graph (@Valid), group sequences from the parent object are inherited by the children objects. @GroupSequences is used to declare more than one sequence per class. A group sequence can contain the name of another group sequence.

Validating a group

By default, the validate method validates the default group. You can decide to validate one or several groups:

validator.validate(address, "basic");
validator.validate(address, "firstscreen", "secondscreen");

The groups passed to the validate method are not processed in a particular order (if you need ordering, use a group sequence).

Practical uses

Enough theory, let's see how groups and group sequences solve our use cases.

Use case specific validation

Defining constraints based on specific use cases is sometimes useful. A typical example comes from reusing the same object model for different purposes, in different contexts.

In our example, an account can be in three states, default, canbuy and oneclick. A canbuy account has enough information to buy items, a oneclick account can buy in... ahem one click.

public class Account {

        @NotEmpty @Length(max=50) private String firstname;
        @Length(max=50) private String middleName;
        @NotEmpty @Length(max=50) private String lastname;
        
        @NotNull(groups="canbuy") @Phone private String cellPhone;

        @Valid @NotEmpty(groups="canbuy")
        private Set<Address> addresses;

        @Valid @NotNull(groups="canbuy")
        private Address defaultAddress;
        
        @Valid @NotEmpty(groups="canbuy")
        private Set<PaymentMethod> paymentMethod;

        @Valid @NotNull(groups="oneclick")
        private Address defaultPaymentMethod;

}

Let's dissect the previous example. A default account must have a first name and last name. If it has addresses, payment methods and phone number, each of these properties must be valid. However, these properties do not have to be filled up to be valid.

An account can buy if it has a default address and at least one payment method. An account can buy in one click if it has a default payment method.

We could imagine the following logic

Set<InvalidConstraint> accountIssues = validator.validate(account);
if ( accountIssues.size() > 0 ) {
        //push that error list to the user
}
else {
        if ( validator.validate(account, "canbuy").size() == 0 ) {
                enableBuyButton();
                if ( validator.validate(account, "oneclick").size() == 0 ) {
                        enableOneClick();
                }
        }
}

The data consistency is ensured by the default group. And some additional state checking is provided by additional groups. Note that in an integrated world, your application framework or your web framework would let your declaratively express which group needs to be validated in a given page context, page, conversation etc. The application avoids the extra work of manually executing the validation.

Partial data validation: the wizard screen model

In a wizard-style user interface, data is provided partially and gets more and more complete when the user move from one wizard screen to the other.

Let's take the previous example and let's imagine a wizard building a one click account from the ground up:

  • the first screen acquires the firstname, lastname and phone
  • the second screen adds one or several addresses
  • the third screen adds one or several payment methods
  • the forth screen makes sure the user selects a default address and payment method and that everything is set up for a one click process
  • the fifth screen is a summary and make sure that everything is in place
public class Account {

        @NotEmpty(groups={"firstscreen", "default"}) 
        @Length(max=50, groups={"firstscreen", "default"}) 
        private String firstname;
        
        @NotEmpty(groups={"firstscreen", "default"})
        @Length(max=50, groups={"firstscreen", "default"}) 
        private String lastname;
        
        @NotNull(groups={"firstscreen", "canbuy"}) 
        @Phone(groups={"firstscreen", "default"})
        private String cellPhone;

        @Valid @NotEmpty(groups={"secondscreen", "canbuy"})
        private Set<Address> addresses;

        @Valid @NotNull(groups={"forthscreen", "canbuy"})
        private Address defaultAddress;
        
        @Valid @NotEmpty(groups={"thirdscreen", "canbuy"})
        private Set<PaymentMethod> paymentMethod;

        @Valid @NotNull(groups={"forthscreen", "oneclick"})
        private Address defaultPaymentMethod;

}

For each screen but the last, a specific group has been set up. The last screen needs to ensure an account is valid for a oneclick operation. It then needs to validate default, canbuy and oneclick groups.

While the group validation can be called programmatically, we expect application frameworks and web frameworks to let you define the targeted groups declaratively.

In JSF, it could look like:

<s:validateAll groups="firstscreen">
  <s:decorate>
    <h:inputText id="firstname" value="#{account.firstname}" />
    <h:message for="firstname" styleClass="error" />
    <br/>
    <h:inputText id="lastname" value="#{account.lastname}" />
    <h:message for="lastname" styleClass="error" />
    <br/>
    <h:inputText id="cellPhone" value="#{account.cellPhone}" />
    <h:message for="cellPhone" styleClass="error" />
  </s:decorate>
</s:validateAll>

This is just a proposal / vision. This integration is not part of the JSR 303 specification.

Constraint ordering

An other use case is the ability to stop constraint validations if a group of constraint fails. It is quite useful in (at least) two situations:

  • some constraint validations (especially class level constraints) expect to work on pre-validated constraints (for example pre validated with nullability, length and global pattern matchings): the basic level constraints must be valid before calling a higher level constraint checking.
  • some constraints are expensive to run (long processing, access an external resource etc)

Validating an address can be fairly simple (some basic constraints) especially when targeting one country. It could also be much more complex and involve address coherence checking (the zipcode must match the city, the street name much match the zipcode and so on). The address coherence checking cannot really be applied until we are sure the basic constraints are valid as it expects some pre normalized data. On top of that, our application has to call an external service to check the coherence. And it turns out calling this service has two drawbacks:

  • it is a bit slow (at least slower than the other constraint validations)
  • it costs a fee per execution

We want to make sure the address coherence constraint is not called unless the other constraints are valid:

@GroupSequence(name="default", sequence={"basic", "complex"})
@Address(groups="complex")
public class Address {
    @NotNull(groups="basic") @Max(50, groups="basic")) private String street1;
    @Max(50, groups="basic")) private String street2;
    @Max(10, groups="basic")) @NotNull(groups="basic") private String zipCode;
    @Max(20, groups="basic")) @NotNull(groups="basic") String city;
    @NotNull(groups="basic") private Country country;
    
    ...
}

The group sequence will first validate the constraints marked as basic, then validate the constraints marked as complex unless one (or more) constraint from the basic group failed.

Conclusion

In most cases, the default group will suffice. But more complex use cases need additional flexibility in the constraint definition and validation. While declaring groups is the application developers responsibility, validating a given set of groups will most likely be driven by the client framework (application frameworks, web frameworks and to a certain extends persistence frameworks). Either by using method annotations or template declarations, frameworks like WebBeans or JSF will capture the requested constraint validation groups and execute the validation calls for the application.

This part of the specification is probably the most controversial. The current proposal tries to achieve several goals:

  • embrace the declarative model
  • stay as simple and understandable as possible
  • provide enough flexibility to match most use cases

The expert group is seeking feedback in several forms on the groups approach:

  • use cases: describe your use cases, see how (or if) they can be addressed by the current proposal
  • enhancements: propose enhancements on the groups concept to make it more flexible or more simple
  • alternatives: you think you have the perfect solution? go describe it, let's see how it fits the model

We encourage you to read the specification and provide feedbacks and comments on this forum.

11 comments:
 
11. Apr 2008, 18:53 CET | Link
Gilson Tavares | gilson(AT)ufrj.br

This is a really useful proposal. But I am worried with this massive use of annotations. A few even help you understand some business rules, but others just pollute the class definition.

In EJB3 I use annotations for things connected to business rules (@Length, @NotNull, @Temporal, etc...) but for mapping info (@Table, @Column, etc...) and query definitions, I use an <classname>.orm.xml file.

Just like in EJB3, are you considering possible to optionally split these declarations between annotations and some placement external to the class definition?

Maybe this question is out of place but, in your vision, will it be possible to integrate this with the rules engines like the framework adopted by Seam?

ReplyQuote
 
12. Apr 2008, 01:07 CET | Link

Yes for people who still think XML is a superior way of dealing with class level metadata, there will be an XML deployment descriptor provided.

As for integrating with rule engine, can you elaborate more on what you have in mind?

 
12. Apr 2008, 16:04 CET | Link

Hahaha! It just wouldn't be Java without the deployment descriptor option!

 
12. Apr 2008, 16:11 CET | Link

Perhaps this is better to bring up in the forum, but I am curious how the cross-field constraint checking can be made to fit with how JSF handles validation. JSF works by converting and then validating one field at a time. Thus, when in the validator, you can never be sure that the other fields you need to check have already been validated. I guess the solution is to perform a post-Apply Validation phase routine that invokes the Bean Validation framework.

I recognize that they way validation is enforced is not the responsibility of the JSR 303 team, but since JSF is also a standard, it would be nice to provide them with a recommendation for how to integrate the two implementations.

 
07. May 2008, 23:23 CET | Link

Since you asked for real-world use cases that can benefit from validation groups, here is our use case: we have an Address model that contains an email field, which is required for one use case (gift shipping,) yet unnecessary for another (special shipping.) With Hibernate Validators, one way to solve the problem is by relying on inheritance. We create one abstract Address class, and then subclass it for both use cases with different validation constraints, keeping common constraints in the abstract class. This is a great scenario where using the validation group approach solves the problem in a more elegant fashion.

On another note, I have a question about using validation groups with wizards. Would having the group name firstscreen in the model introduce coupling between the model and the presentation details? I guess to get around that, validation groups need to have more business-related names, or the model needs to be split into four different models, each representing a different screen. Any other cleaner solutions without coupling the wizard model to the presentation details?

 
01. Oct 2008, 22:44 CET | Link
Anjaneya reddy | anj.reddy(AT)gmail.com

I would like to know, if Hibernate Validator supports Validation Groups. If so, what version.

If not, is it going to be supported in the future versions. Thank you

 
20. Jun 2010, 18:13 CET | Link

I want to test if an entity already exists in the database before persist, so I am thinking that create a constraint that test if the entity already exist is worthy. It´s a good practice? I will need to inject an entitymanager inside de constraint implementation.

 
19. Mar 2011, 17:01 CET | Link

any idea how I will do to include in jsf validation phase?

25. Jun 2014, 15:11 CET | Link

Wow! It is indeed amazing and interesting to see how different countries instill and implement the importance of entrepreneurship in their younger generation. No doubt, entreprenership is the way to go in thia day and age.text your ex back

26. Jun 2014, 15:14 CET | Link

This site is a leading resource for information about the Jon Hildebrandt Superhandles DVD video and online streaming basketball training systems that teach you how to master ball handling, shooting, dribbling skills, passing skills, fundamentals, finishing, and advanced pro-level moves like the Iverson crossover, mirror hesitation crossover, and shake triple. superhandles drills

09. Jul 2014, 13:30 CET | Link

Folkersystem Vert Shock: Adam Folker And Justin 'Jus Fly' Darlington Reveal An Insane Method To Pump Up Your Vertical By 9-15 Inches And Throw Down Dunks On Helpless Defenders Like A Total Badass...about the program

Post Comment