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@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.

  • 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.

  • 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 VU 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).

  • 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

DeclarationResolution
@Size Collection getAddresses() { ... }SizeValidatorForCollection: direct match
@Size Collection<?> getAddresses() { ... }SizeValidatorForCollectionCollection is a direct supertype of Collection<?>
@Size Collection<Address> getAddresses() { ... }SizeValidatorForCollectionCollection is a direct supertype of Collection<Address>
@Size Set<Address> getAddresses() { ... }SizeValidatorForSet: direct supertype of Set<Address>
@Size SortedSet<Address> getAddresses() { ... }SizeValidatorForSetSet 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();