[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