[hibernate-dev] Group sequence adjustments

Emmanuel Bernard emmanuel at hibernate.org
Fri Feb 20 09:40:21 EST 2009


Hi everyone,
Several questions have been raised around groups recently. I have  
adjusted the spec to answer them. Can you review the wording (esp the  
addition to the formal rules)? I put changes in red.

The idea is:
  - an interface representing a group sequence cannot have inherited  
groups
  - a constraint declaration cannot use a group reprensenting a group  
sequence
  - when a group A is sequenced before B, all groups composing A (via  
inheritance or group sequence) are sequenced before B
  - if a circularity arise, an exception happens



3.4. Group and group sequence

A group defines a subset of constraints. Instead of validating all  
constraints for a given object graph, only a subset is validated  
depending on the group targeted. Each constraint declaration defines  
the list of groups it belongs to. If no group is explicitly declared,  
a constraint belongs to the Default group.

Groups are represented by interfaces.

Example 3.1. Definition of groups

/**
  * Validation group checking a user is billable
  */
public interface Billable {}

/**
  * customer can buy without harrassing checking process
  */
public interface BuyInOneClick {
}
A constraint can belong to one or more groups.

Example 3.2. Assign groups to constraints

/**
  * User representation
  */
public class User {
     @NotNull
     private String firstname;

     @NotNull(groups = Default.class)
     private String lastname;

     @NotNull(groups = {Billable.class, BuyInOneClick.class})
     private CreditCard defaultCreditCard;
}
During the validation call, one or more groups are validated. All the  
constraints belonging to this set of group is evaluated on the object  
graph. In Example 3.2, “Assign groups to constraints”, at NotNull is  
checked on defaultCreditCard when either the Billable or BuyInOneClick  
group is validated. @NotNull on firstname and on lastname are  
validated when the Default group is validated. Reminder: constraints  
held on superclasses and interfaces are considered.

Default is a group predefined by the specification

package javax.validation.groups;

/**
  * Default Bean Validation group
  *
  * @author Emmanuel Bernard
  */
public interface Default {
}
3.4.1. Group inheritance

In some situations, a group is a super set of one or more groups. This  
can be described by Bean Validation. A group can inherit one or more  
groups by using interface inheritance.

Example 3.3. Groups can inherit other groups

/**
  * Customer can buy without harrassing checking process
  */
public interface BuyInOneClick extends Default, Billable {
}
For a given interface Z, constraints marked as belonging to the group  
Z (ie where the annotation groups property contains the interface Z)  
or any of the super interfaces of Z (inherited groups) are considered  
part of the group Z.

In the following example:

Example 3.4. Use of a inherited group

/**
  * User representation
  */
public class User {
     @NotNull
     private String firstname;

     @NotNull(groups = Default.class)
     private String lastname;

     @NotNull(groups = {Billable.class})
     private CreditCard defaultCreditCard;
}
validating the group BuyInOneClick will lead to the following  
constraints checking:

@NotNull on firstname and lastname

@NotNull on defaultCreditCard

because Default and Billable are subinterfaces of BuyInOneClick.

3.4.2. Group sequence

By default, constraints are evaluated in no particular order and this  
regardless of which groups they belong to. It is however useful in  
some situations to to control the order of constraints evaluation.  
There are often scenarios where a preliminary set of constraints  
should be evaluated prior to other constraints. Here are two examples:

The second group depends on a stable state to run properly. This  
stable state is verified by the first previous group.

The second group is a heavy consumer of time, CPU or memory and its  
evaluation should be avoided if possible.

To implement such ordering, a group can be defined as a sequence of  
other groups. Each group in a group sequence must be processed  
sequentially in the order defined by at GroupSequence.value when the  
group defined as a sequence is requested. Note that a group member of  
a sequence can itself be composed of several groups via inheritance or  
sequence definition. In this case, each composed group must respect  
the sequence order as well.

Processing a group is defined in Section 3.5, “Validation routine” ;  
if one of the groups processed in the sequence generates one or more  
constraint violation, the groups following in the sequence must not be  
processed. This ensure that a set of constraint is evaluated only if  
another set of constraint is valid.

Groups defining a sequence and groups composing a sequence must not be  
involved in a cyclic dependency either directly or indirectly, either  
through cascaded sequence definition or group inheritance.

Groups defining a sequence should not directly inherit other groups.  
In other words, the interface hosting the group sequence should not  
have any super interface.

Groups defining a sequence should not be used directly in constraint  
declarations. In other words, the interface hosting the group sequence  
should not be used in a constraint declaration.

To define a group as a sequence, the interface must be annotated with  
the @GroupSequence annotation.

@Target({TYPE})
@Retention(RUNTIME)
public @interface GroupSequence {
     Class<?>[] value();
}
Here is a usage example

Example 3.5. Make use of group sequence

@ZipCodeCoherenceChecker(groups = Address.HighLevelCoherence.class)
public class Address {
     @NotNull @Size(max = 50)
     private String street1;

     @ZipCode
     private String zipcode;

     @NotNull @Size(max = 30)
     private String city;

     /**
      * check conherence on the overall object
      * Needs basic checking to be green first
      */
     public interface HighLevelCoherence {}

     /**
      * check both basic constraints and high level ones.
      * high level constraints are not cheked if basic constraints fail
      */
     @GroupSequence({Default.class, HighLevelCoherence.class})
     public interface Complete {}
}
In Example 3.5, “Make use of group sequence”, when the  
Address.Complete group is validated, all constraints belonging to the  
Default group are validated. If any of them fail, the validation skips  
the HighLevelCoherence group. If all Default constraints pass,  
HighLevelCoherence constraints are evaluated.

Note

A given constraint can belong to two groups ordered by a sequence. In  
this case, the constraint is evaluated as part of the first group and  
ignored in the subsequent group(s). SeeSection 3.5, “Validation  
routine” for more informations.

3.4.3. Redefining the Default group for a class

In Example 3.5, “Make use of group sequence”, validating the Default  
group does not validate HighLevelCoherence constraints. To ensure a  
complete validation, a user must use the Completegroup. This breaks  
some of the encapsulation you could expect. You can work around this  
by redefining what the Default group means for a given class. To  
redefine Default for a class, place a at GroupSequence annotation on the  
class ; this sequence expresses the sequence of groups that does  
substitute Default for this class.

Example 3.6. Redefining Default group for Address

@GroupSequence({Address.class, HighLevelCoherence.class})
@ZipCodeCoherenceChecker(groups = Address.HighLevelCoherence.class)
public class Address {
     @NotNull @Size(max = 50)
     private String street1;

     @ZipCode
     private String zipcode;

     @NotNull @Size(max = 30)
     private String city;

     /**
      * check conherence on the overall object
      * Needs basic checking to be green first
      */
     public interface HighLevelCoherence {}
}
In Example 3.6, “Redefining Default group for Address”, when an  
address object is validated for the group Default, all constraints  
belonging to the group Default and hosted on Address are evaluated. If  
none fails, all HighLevelCoherence constraints present on Address are  
evaluated. In other words, when validating the Default group for  
Address, the group sequence defined on the Address class is used.

Since sequences cannot have circular dependencies, using Default in  
the declaration of a sequence is not an option. Constraints hosted on  
a class A and belonging to the Default group (by default or  
explicitly) implicitly belong to the group A.

A sequence defined on a class A (ie. redefining the Default groups for  
the class) must contain the group A. In other words, the default  
constraints hosted on a class must be part of the sequence definition.

3.4.4. Implicit grouping

It is possible to implicitly group some constraints in the same group  
without explicitly listing such a group in the constraint declaration.  
Every constraint hosted on an interface Z and part of theDefault group  
(implicitly or explicitly) belongs to the group Z. This is useful to  
validate the partial state of an object based on a role represented by  
an interface.

Example 3.7. Example of interface / group hosting constraints

/**
  * Auditable object contract
  */
public interface Auditable {
     @NotNull String getCreationDate();
     @NotNull String getLastUpdate();
     @NotNull String getLastModifier();
     @NotNull String getLastReader();
}

/**
  * Represents an order in the system
  */
public class Order implements Auditable {
     private String creationDate;
     private String lastUpdate;
     private String lastModifier;
     private String lastReader;

     private String orderNumber;

     public String getCreationDate() {
         return this.creationDate;
     }

     public String getLastUpdate() {
         return this.lastUpdate;
     }

     public String getLastModifier() {
         return this.lastModifier;
     }

     public String getLastReader() {
         return this.lastReader;
     }

     @NotNull @Size(min=10, max=10)
     public String getOrderNumber() {
         return this.orderNumber;
     }
}
When an Order object is validated on the Default group, the following  
constraints are validated: @NotNull on getCreationDate, getLastUpdate,  
getLastModifier, getLastReader,getOrderNumber and @Size on  
getOrderNumber as all belong to the Default group.

When an Order object is validated on the Auditable group, the  
following constraints are validated: @NotNull on getCreationDate,  
getLastUpdate, getLastModifier, getLastReader. Only the constraints  
present on Auditable (and any of its super interfaces) and belonging  
to the Default group are validated when the group Auditable is  
requested. It allows the caller to validate that a given object can be  
safely audited even if the object state itself is not valid.

3.4.5. Formal group definitions

The formal rules defining groups are as followed. Text in italic are  
comments about the rules.

For every class X:

A. For each superclass Y of X, the group Y contains all constraints of  
the group Y of Y

this rule prepares formal concepts for recursive discovery

B. The group X contains the following constraints:

group X is a group used on sequences redefining the default group on a  
class (see Section 3.4.3, “Redefining the Default group for a class”)

every constraint declared by the class X which does not declare a  
group or does declare the group Default explicitly.

all Default constraints hosted on X

every constraint declared by any interface implemented by X and not  
annotated @GroupSequence which does not explicitly declare a group or  
does declare the group Default explicitly.

all Default constraints hosted on interfaces of X: constraints are  
inherited by the class hierarchy. Interfaces marked as @GroupSequence  
are ignored.

if X has a direct superclass Y, every constraint in the group Y

all Default constraints hosted on the superclasses of X: constraints  
are inherited by the class hierarchy

C. If X has no @GroupSequence annotation, the group Default contains  
the following constraints:

this rule defines which constraints are evaluated when validating  
Default on X.

every constraint in the group X

if X has a direct superclass Y, every constraint in the group Default  
of Y

this rule is necessary in case Y redefines the group Default

D. If X does have a @GroupSequence annotation, the group Default  
contains every constraint belonging to every group declared by the  
@GroupSequence annotation.

this rule describes how a class can redefine the group Default for  
itself (see Section 3.4.3, “Redefining the Default group for a class”)

the @GroupSequence annotation must declare the group X

E. For every interface Z, the group Z contains the following  
constraints:

this rule defines how non Default groups are defined

every constraint declared by the interface Z which does not explicitly  
declare a group or does declare the group Default explicitly.

all Default constraints hosted on Z: this rule formally defines  
implicit grouping per interface (see Section 3.4.4, “Implicit grouping”)

every constraint declared by any superinterface not annotated  
@GroupSequence of the interface Z which does not explicitly declare a  
group

all Default constraints hosted on interfaces of Z: groups can be  
inherited (see Section 3.4.1, “Group inheritance”)

every constraint declared by the class X which explicitly declares the  
group Z

every constraint hosted by X and marked as belonging to the group Z

every constraint declared by any interface implemented by X and not  
annotated @GroupSequence which explicitly declares the group Z

every constraint hosted by any interface of X and marked as belonging  
to the group Z

if X has a direct superclass Y, every constraint in the group Z of Y

every constraint hosted by any superclass of X and marked as belonging  
to the group Z

F. For every interface Z annotated @GroupSequence, the group Z  
contains every constraint belonging to every group declared by the  
@GroupSequence annotation.

defines the composition side of group sequence but does not define the  
ordering behavior of sequence (see Section 3.4.2, “Group sequence”)



When a given group G (represented by an interface G) is requested for  
the validation of a class X:

constraints belonging to the group G are evaluated

if the interface G is not annotated @GroupSequence, every group  
represented by the super interface of G are requested for validation

if the interface G is annotated with @GroupSequence, every group  
represented by the interfaces declared by the @GroupSequence  
annotation are requested for validation

the validation of groups declared to the @GroupSequence must happen in  
the sequencing order declared by @GroupSequence: the sequencing order  
is propagated to the groups composing the sequenced group (via  
inheritance or group sequence)

if a group validation triggers the failure of one or more constraints,  
groups following in the sequence must not be evaluated.

if the group G represents the Default group of X overridden by  
@GroupSequence, operations are equivalent

When the Default group of a given class X is overridden via  
@GroupSequence, its validation is as followed:

every group represented by the interfaces declared by the  
@GroupSequence annotation are requested for validation

the validation of groups declared to the @GroupSequence must happen in  
the sequencing order declared by @GroupSequence: the sequencing order  
is propagated to the groups composing the sequenced group (via  
inheritance or group sequence)

if a group validation triggers the failure of one or more constraints,  
groups following in the sequence must not be evaluated.

Unless defined by a @GroupSequence, evaluation ordering is not  
constrained. In particular, several groups can be validated in the  
same pass. If a group definition leads to a circular sequencing order  
between groups, an exception is raised.

Note

A group G sequenced (directly or indirectly) to be executed before  
itself is not considered a circular reference.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.jboss.org/pipermail/hibernate-dev/attachments/20090220/cde500de/attachment.html 


More information about the hibernate-dev mailing list