[hibernate-dev] [Bean Validation] Type-safe Validator and resolution algorithm

Emmanuel Bernard emmanuel at hibernate.org
Thu Jan 29 00:39:56 EST 2009


OK

Here is the proposal I integrated in the spec. Main changes are in the  
email, the whole spec is here for reference.

Review very much welcome.


2.1. Constraint annotation

A constraint on a JavaBean is expressed through one or more  
annotations. An annotation is considered a constraint definition if  
its retention policy contains RUNTIME and if the annotation itself is  
annotated with javax.validation.Constraint.

/**
  * Link between a constraint annotation and its constraint validation  
implementations.
  * <p/>
  * A given constraint annotation should be annotated by a @Constraint
  * annotation which refers to its list of constraint validation  
implementation.
  *
  * @author Emmanuel Bernard (emmanuel at hibernate.org)
  * @author Gavin King
  * @author Hardy Ferentschik
  */
@Documented
@Target({ ANNOTATION_TYPE })
@Retention(RUNTIME)
public @interface Constraint {
	/**
	 * ConstraintValidator classes must reference distinct target types.
	 * If two validators refer to the same type, an exception will occur
	 *
	 * @return aray of ConstraintValidator classes implementing the  
constraint
	 */
	public Class<? extends ConstraintValidator<?,?>>[] validatedBy();
}
Constraint annotations can target any of the following ElementTypes:

FIELD for constrained attributes

METHOD for constrained getters

TYPE for constrained beans

ANNOTATION_TYPE for constraints composing other constraints

While other ElementTypes are not forbidden, the provider does not have  
to recognize and process constraints placed on such types.

Since a given constraint definition applies to one or more specific  
Java types, the JavaDoc for the constraint annotation should clearly  
state which types are supported. Applying a constraint annotation to  
an incompatible type will raise a  
UnexpectedTypeForConstraintException. Care should be taken on defining  
the list of ConstraintValidator. The type resolution algorithm (see  
Section 3.5.3, “ConstraintValidator resolution algorithm”) could lead  
to exceptions due to ambiguity.



[...]




2.4. Constraint validation implementation

A constraint validation implementation performs the validation of a  
given constraint annotation for a given type. The implementation  
classes are specified by the validatedBy element of the at Contraint  
annotation that decorates the constraint definition. The constraint  
validation implementation implements the ConstraintValidator interface.

Example 2.10. ConstraintValidator interface

/**
  * Defines the logic to validate a given constraint A
  * for a given object type T.
  * Implementations must comply to the following restriction:
  * T must resolve to a non parameterized type
  *
  * @author Emmanuel Bernard
  * @author Hardy Ferentschik
  */
public interface ConstraintValidator<A extends Annotation, T> {
	/**
	 * Validator parameters for a given constraint definition
	 * Annotations parameters are passed as key/value into parameters
	 * <p/>
	 * This method is guaranteed to be called before any of the other  
Constraint
	 * implementation methods
	 *
	 * @param constraintAnnotation parameters for a given constraint  
definition
	 */
	void initialize(A constraintAnnotation);

	/**
	 * Implement the validation constraint.
	 * <code>object</code> state must not be changed by a Constraint  
implementation
	 *
	 * @param object object to validate
	 * @param constraintValidatorContext context in which the constraint  
is evaluated
	 *
	 * @return false if <code>object</code> does not pass the constraint
	 */
	boolean isValid(T object, ConstraintValidatorContext  
constraintValidatorContext);
}
Some restrictions apply on the generic type T (used in the isValid  
method). T must resolve in a non parameterized type:

the type is not using generics

because the raw type is used instead of the generic version

Warning

Should we support unbounded wildcards?

Here are some examples of valid definitions in Example 2.11, “Valid  
ConstraintValidator definitions”.

Example 2.11. Valid ConstraintValidator definitions

//String is not making use of generics
public class SizeValidatorForString implements<Size, String> {...}

//Collection uses generics but the raw type is used
public class SizeValidatorForCollection implements<Size, Collection>  
{...}
And some invalid definitions in Example 2.12, “Invalid  
ConstraintValidator definitions”.

Example 2.12. Invalid ConstraintValidator definitions

//parameterized type
public class SizeValidatorForString implements<Size,  
Collection<String> {...}

//parameterized type using unbounded wildcard
public class SizeValidatorForCollection implements<Size, Collection<? 
 >> {...}

//parameterized type using bounded wildcard
public class SizeValidatorForCollection implements<Size, Collection<?  
extends Address>> {...}
Note

This restriction is not a theoretical limitation and a future version  
of the specification will likely allow that in the future.






[...]


3.5.3. ConstraintValidator resolution algorithm

A constraint is associated to one or more ConstraintValidator  
implementations. Each ConstraintValidator<A, T> accepts the type T.  
The ConstraintValidator executed depends on the type declared by the  
target hosting the constraint. For a given constraint evaluation, a  
single ConstraintValidator is considered.

If the constraint is hosted on a class or an interface, the targeted  
type is the class or the interface. If the constraint is hosted on a  
class attribute, the type of the attribute is the targeted type. If  
the constraint is hosted on a getter, the return type of the getter is  
the targeted type.

The rules written below describe formally the following statement: the  
ConstraintValidator chosen to validate a declared type T is the one  
where the type supported by theConstraintValidator is a supertype of T  
and where there is no other ConstraintValidator whose supported type  
is a supertype of T and not a supertype of the  
chosenConstraintValidator supported type.

When validating a constraint A placed on a target declaring the type  
T, the following resolution rules apply.

Primitive types are considered equivalent to their respective  
primitive wrapper class.

A ConstraintValidator<A, U> is said to be compliant with T if T is a  
subtype of U (according to the Java Language Specification 3rd edition  
chapter 4.10 Subtyping). Note that T is a subtype of U if T = U.

If no ConstraintValidator compliant with T is found amongst the  
ConstraintValidators listed by the constraint A, a  
UnexpectedTypeForConstraintException is raised.

A ConstraintValidator<A, U> compliant with T is considered strictly  
more specific than a ConstraintValidator<A, V> compliant with T if U  
is a strict subtype of V. U is a strict subtype of V if U is a subtype  
of V and U != V (according to theJava Language Specification 3rd  
edition chapter 4.10 Subtyping).

A ConstraintValidator<A, U> compliant with T is considered maximally  
specific if no other ConstraintValidator<A, V> compliant with T is  
strictly more specific thanConstraintValidator<A, U>.

If more than one maximally specific ConstraintValidator is found, a  
AmbiguousConstraintUsageException is raised.

Note

While the Java compiler itself cannot determine if a constraint  
declaration will lead to a UnexpectedTypeForConstraintException or  
aAmbiguousConstraintUsageException, rules can be statically checked. A  
tool such as an IDE or a Java 6 annotation processor can apply these  
rules and prevent a compilation in case of ambiguity. The  
specification encourages Bean Validation provider to provide such a  
tool to their users.

Let's see a couple of declaration their respective ConstraintValidator  
resolution. Assuming the following definitions:

[...]
@Constraint(validatedBy={
     SizeValidatorForCollection.class,
     SizeValidatorForSet.class,
     SizeValidatorForSerializable.class })
public @interface Size { ...}

public class SizeValidatorForCollection implements  
ConstraintValidator<Size, Collection> { ... }
public class SizeValidatorForSet implements ConstraintValidator<Size,  
Set> { ... }
public class SizeValidatorForSerializable implements  
ConstraintValidator<Size, Serializable> { ... }

public interface SerializableCollection extends Serializable,   
Collection {}
The following resolutions occur.

Table 3.1. Resolution of ConstraintValidator for various constraints  
declarations

Declaration	Resolution
@Size Collection getAddresses() { ... }	SizeValidatorForCollection:  
direct match
@Size Collection<?> getAddresses() { ... }	SizeValidatorForCollection:  
Collection is a direct supertype of Collection<?>
@Size Collection<Address> getAddresses() { ... }	 
SizeValidatorForCollection: Collection is a direct supertype of  
Collection<Address>
@Size Set<Address> getAddresses() { ... }	SizeValidatorForSet: direct  
supertype of Set<Address>
@Size SortedSet<Address> getAddresses() { ... }	SizeValidatorForSet:  
Set is the closest supertype of SortedSet<Address>
@Size SerializableCollection getAddresses() { ... }	 
AmbiguousConstraintUsageExceptionSerializableCollection is a subtype  
of both Collection and Serializable and neitherCollection nor  
Serializable are subtypes of each other.
@Size String getName() { ... }	UnexpectedTypeForConstraintException  
none of the ConstraintValidator types are supertypes of String.



[...]

public interface ConstraintDescriptor {
	/**
	 * Returns the annotation describing the constraint declaration.
	 * If a composing constraint, parameter values are reflecting
	 * the overridden parameters from the main constraint
	 *
	 * @return The annotation for this constraint.
	 */
	Annotation getAnnotation();

	/**
	 * @return The groups the constraint is applied on.
	 */
	Set<Class<?>> getGroups();

	/**
	 * @return the constraint validation implementation class
	 */
	Class<? extends ConstraintValidator<?,?>>[]
	    getConstraintValidatorClasses();



On  Jan 23, 2009, at 15:13, Emmanuel Bernard wrote:

> Today, ConstraintValidator accepts Object and the implementation is  
> responsible for handling the casting and raise an exception if the  
> type is not supported.
>
> The idea is to get type-safe validator and a discovery mechanism to  
> associate a validator to a given runtime type being validated. The  
> resolution is not 100% complete, you might end up with:
>  - no valid validator
>  - no way to decide between two or more validators
> and get exceptions at runtime.
>
> I would really like feedback on it as we could arrange some of these  
> rules to not fail (at least in the latter case):
>  - do we want this type-safe + auto resolution algorithm strategy or  
> should we stick with the untyped solution
>  - do we want to fail in case of ambiguity or rather choose the  
> first compatible provider?
>
> Personally I find the feature quite elegant and the metadata can be  
> used by tools to warn a user if a constraint does not match the  
> static type it is applied on.
>
> API and declaration
>
> A more typesafe proposal would be:
>
> public interface ConstraintValidator<A extends Annotation, T> {
>     void initialize(A annotation);
>     boolean isValid(T object, ConstraintValidationContext context);
> }
>
> public class StringSizeValidator implements  
> ConstraintValidator<Size, String> {
>    ...
> }
>
> public class CollectionSizeValidator implements  
> ConstraintValidator<Size, Collection> {
>    ...
> }
>
> public class MapSizeValidator implements ConstraintValidator<Size,  
> Map> {
>    ...
> }
>
> public class ArraySizeValidator implements ConstraintValidator<Size,  
> Object[]> {
>    ...
> }
>
>
> @Constraint(validatedBy={
>     StringSizeValidator.class, CollectionSizeValidator.class,
>     MapSizeValidator.class, ArraySizeValidator.class} )
> public @interface Size { ... }
>
> We then need to decide at runtime, which validator needs to be  
> executed.
> Here is a proposed algorithm heavily based on the Java Language  
> Specification (boy it's hard to read it).
>
> Resolution algorithm
> The type T of a ConstraintValidator must not make direct use of  
> generics itself.
>
> This is because the algorithm compare the actual type at runtime and  
> such notion would be lost. So you can do ConstraintValidator<Size,  
> Collection> but not ConstraintValidator<Size, Collection<String>>.
>
> For a given runtime object t of type T about to be validated for a  
> given constraint:
> if t is null the first validator in the array of available  
> validators is used.
> otherwise
>
> A validator accepting U is said to be compliant with the type T if T  
> is a subtype of U according to the JLS chapter 4.10.
>
> If no compliant validator is found for T, a  
> UnexpectedTypeForConstraint exception is raised.
> If a validator is taking the responsibility to dispatch types (ie  
> ConstraintValidator<Size,Object>), it must use the same exception in  
> the same circumstances.
>
> If one compliant validator is found, it is used.
>
> If more than one compliant validator is found, the one with the most  
> specific parameter is used. The informal intuition is that one  
> parameter is more specific than another if any assignment handled by  
> the first parameter could be passed on to the other one without a  
> compile-time type error.
>
> Of a given type T, a compliant validator accepting U is considered  
> maximally specific if no other T compliant validator accepting V is  
> such as V is a strict subtype of U. String subtype is defined by JSL  
> chapter 4.10.
>
> If more than one maximally specific validator is found, a  
> AmbiguousValidatorException is raised.
>
> WDYT?
> _______________________________________________
> hibernate-dev mailing list
> hibernate-dev at lists.jboss.org
> https://lists.jboss.org/mailman/listinfo/hibernate-dev

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.jboss.org/pipermail/hibernate-dev/attachments/20090129/ba6306d8/attachment.html 
-------------- next part --------------
A non-text attachment was scrubbed...
Name: JSR-303-Specification-20090128.pdf.zip
Type: application/zip
Size: 191537 bytes
Desc: not available
Url : http://lists.jboss.org/pipermail/hibernate-dev/attachments/20090129/ba6306d8/attachment.zip 
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.jboss.org/pipermail/hibernate-dev/attachments/20090129/ba6306d8/attachment-0001.html 


More information about the hibernate-dev mailing list