License
Copyright Red Hat, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 .
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

1. Introduction

This document is the specification of the Java API for JavaBean validation in Java EE and Java SE. The technical objective of this work is to provide an object level constraint declaration and validation facility for the Java application developer, as well as a constraint metadata repository and query API.

It also offers method and constructor validation facilities to ensure constraints on their parameters and return values.

1.1. Expert group

This work has been conducted as part of JSR 380349 and formerly JSRs 349 andJSR 303 under the Java Community Process Program. This specification is the result of the collaborative work of the members of the JSR 380349 Expert Group and the community at large.

The following persons have actively contributed to Bean Validation 2.0 as members of the JSR 380 expert group and the community at large in alphabetical order:

  • Matt Benson

  • Emmanuel Bernard (Red Hat, Inc.)

  • Linda DeMichiel (Oracle)

  • Hendrik Ebbers (Canoo AG)

  • Hardy Ferentschik (Red Hat, Inc.)

  • Christian Kaltepoth (ingenit GmbH & Co. KG)

  • Werner Keil

  • Marco Molteni (Genidea Sagl)

  • Gunnar Morling (Red Hat, Inc.) - Specification Lead

  • Michael Nascimento Santos

  • Otavio Santana

  • Guillaume Smet (Red Hat, Inc.)

  • Tsuyoshi Yoshitomi (Fujitsu Limited)

The following persons have actively contributed to Bean Validation 1.1 as members of the JSR 349 expert group and the community at large in alphabetical order:

  • Matt Benson

  • Paul Benedict

  • Emmanuel Bernard (Red Hat, Inc.) - Specification Lead

  • Edward Burns (Oracle)

  • Peter Davis

  • Linda DeMichiel (Oracle)

  • Hardy Ferentschik (Red Hat, Inc.)

  • Antonio Goncalves

  • Cemalettin Koç

  • Rich Midwinter

  • Gunnar Morling (individual then Red Hat, Inc.)

  • Pete Muir (Red Hat, Inc.)

  • Michael Nascimento Santos

  • Gerhard Petracek

  • Kevin Pollet (SERLI)

  • Jagadish Prasath Ramu (Oracle)

  • Bill Shannon (Oracle)

  • Sebastian Thomschke

Former expert group members of JSR-303 in alphabetical order are:

  • Geert Bevin

  • Emmanuel Bernard (Red Hat, Inc.) - Specification Lead

  • Uri Boness

  • Erik Brakkee (Ericsson AB)

  • Ed Burns (Sun Microsystems, Inc.)

  • Jason Carreira

  • Robert Clevenger (Oracle - retired)

  • Linda DeMichiel (Sun Microsystems, Inc.)

  • Tim Fennel

  • Bharath Ganesh (Pramati Technologies)

  • Romain Guy (Google Inc.)

  • Robert Harrop

  • Jacob J. Hookom

  • Bob Lee (Google Inc.)

  • Craig R. McClanahan (Sun Microsystems, Inc.)

  • Niall K. Pemberton

  • Steve Peterson

  • Dhanji R. Prasanna (Google Inc., formerly individual)

  • Gerhard Petracek

  • Matt Raible

  • Michael Nascimento Santos

  • Sebastian Thomschke

  • Jon Wetherbee (Oracle)

1.2. Specification goals

Validating data is a common task that occurs throughout an application, from the presentation layer to the persistence layer. Often the same validation logic is implemented in each layer, proving to be time consuming and error-prone. To avoid duplication of these validations in each layer, developers often bundle validation logic directly into the domain model, cluttering domain classes with validation code that is, in fact, metadata about the class itself.

This JSR defines a metadata model and API for JavaBean validation. The default metadata source is annotations, with the ability to override and extend the metadata through the use of XML validation descriptors.

The validation API developed by this JSR is not intended for use in any one tier or programming model. It is specifically not tied to either the web tier or the persistence tier, and is available for both server-side application programming, as well as rich client Swing application developers. This API is seen as a general extension to the JavaBeans object model, and as such is expected to be used as a core component in other specifications. Ease of use and flexibility have influenced the design of this specification.

As of version 1.1, Bean Validation constraints can also be applied to the parameters and return values of methods of arbitrary Java types. Thus the Bean Validation API can be used to describe and validate the contract (comprising pre- and postconditions) applying to a given method ("Programming by Contract", PbC). Note that it is not the goal of this specification to develop a fully-fledged PbC solution but rather an easy-to-use facility satisfying the most common needs related to applying constraints to method parameters and return values, based on the proven concepts of the Bean Validation API.

1.3. Required Java version

The specification uses Java 86.0 language features. There is no requirement that implementations be compatible with Java language versions prior to 86.0.

1.4. How this document is organized

This document describes each aspect of the Bean Validation specification in a separate chapter. One should remember that the specification is a consistent whole.

Constraint definitionDefinition describes how constraints are defined.

Value extractor definition describes how extractors for the values of container types are defined.

Constraint declaration and validation process describes how a JavaBean class is decorated with annotations to describe constraints.

Validation APIs describes how to programmatically validate a JavaBean.

Constraint metadata request APIs describes how the metadata query API works.

Built-in Constraint definitions list all the built-in constraints.

XML deployment descriptor describes the XML deployment descriptors for the configuration and the mapping.

Exception model describes the exception model and hierarchy used by Bean Validation.

Integration describes the different integration points of Bean Validation with other technologies. In some cases one has to refer to the respective specifications for the up-to-date integration rules.

In Terminology, key concepts are summarized. Some reviewers have found that reading the terminology section first helps to better understand the specification.

The changelog can be found at Changelog.

1.5. How to comment

The expert group is eager to receive feedback from readers. Feel free to contact us. You can get all the details at http://beanvalidation.org/contribute/.

2. What’s new

2.1. What’s new in 2.0

The main contribution of Bean Validation 2.0 is leveraging the new language features and API additions of Java 8 for the purposes of validation. Java 8 or later is required to use Bean Validation 2.0.

The changes include:

2.2. Whats new in 1.1

Bean Validation 1.1 improves and builds upon Bean Validation 1.0. The expert group and the community have been working on a few specific areas.

2.2.12.1.1. Openness

All of Bean Validation 1.1 work has been done in the open and in an open source way. Source code for the API, reference implementation, test compatibility kit as well as the specification and the website sources are available in the open. All discussions are done in the open in the publicly available development mailing list. Road map and proposals are also published on the website.

You can find all the details (mailing lists, source repositories etc.) at http://beanvalidation.org.

2.2.22.1.2. Dependency injection

Bean Validation uses a few components MessageInterpolator, TraversableResolver, ParameterNameProvider, ConstraintValidatorFactory and ConstraintValidator. Bean Validation 1.1 standardizes how these objects are managed by a container and how these objects can benefit from container services. In particular, CDI support within Java EE is being defined.

2.2.32.1.3. Method validation

Bean Validation 1.1 allows to put constraints to the parameters and return values of arbitrary methods and constructors. That way the Bean Validation API can be used to describe and validate the contract applying to a given method or constructor, that is:

  • the preconditions that must be met by the caller before the method or constructor may be invoked and

  • the postconditions that are guaranteed to the caller after a method or constructor invocation returns.

This enables a programming style known as "Programming by Contract" (PbC). Compared to traditional means of checking the sanity of argument and return values this approach has several advantages:

  • These checks are expressed declaratively and don’t have to be performed manually, which results in less code to write, read and maintain.

  • The pre- and postconditions applying for a method or constructor don’t have to be expressed again in the documentation, since any of its annotations will automatically be included in the generated JavaDoc. This reduces redundancies, thus avoiding efforts and inconsistencies between implementation and documentation.

2.2.42.1.4. Integration with Context and Dependency Injection

The integration points with Context and Dependency Injection (CDI) have been increased and reworked. This opens up for a more natural and standard integration both in Java EE and Java SE and encompass dependency injection, component lifecycle management and interception for method validation.

2.2.52.1.5. Group conversion

The specification offers a way to alter the targeted group when validation cascading isin happening. This feature is particularly useful to reuse a given object (graph) and to avoid leaking groups between various object subgraphs. It also makes for more readable constraints.

2.2.62.1.6. Message interpolation via the unified expression language

Constraint violation messages can now use EL expressions for a much more flexible rendering and string formatting. In particular a formatter object is injected in the EL context to convert numbers, dates etc. into the locale specific string representation. Likewise, the validated value is also available in the EL context.

2.2.72.1.7. Others

Many more minor changes have been done. Check out the change log for more details at Changelog.

3. Constraint definitionDefinition

Constraints are defined by the combination of a constraint annotation and a list of constraint validation implementations. The constraint annotation is applied on types, fields, methods, constructors, parameters, container elements or other constraint annotations in case of composition.

Unless stated otherwise the default package name for the Bean Validation APIs is javax.validation.

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

Listing 3.1: @Constraint annotation
/**
 * Marks an annotation as being a Bean Validation constraint.
 * <p>
 * A given constraint annotation must be annotated by a {@code @Constraint}
 * annotation which refers to its list of constraint validation implementations.
 * <p>
 * Each constraint annotation must host the following attributes:
 * <ul>
 *     <li>{@code String message() default [...];} which should default to an error
 *     message key made of the fully-qualified class name of the constraint followed by
 *     {@code .message}. For example {@code "{com.acme.constraints.NotSafe.message}"}</li>
 *     <li>{@code Class<?>[] groups() default {};} for user to customize the targeted
 *     groups</li>
 *     <li>{@code Class<? extends Payload>[] payload() default {};} for
 *     extensibility purposes</li>
 * </ul>
 * <p>
 * When building a constraint that is both generic and cross-parameter, the constraint
 * annotation must host the {@code validationAppliesTo()} property.
 * A constraint is generic if it targets the annotated element and is cross-parameter if
 * it targets the array of parameters of a method or constructor.
 * <pre>
 *     ConstraintTarget validationAppliesTo() default ConstraintTarget.IMPLICIT;
 * </pre>
 * This property allows the constraint user to choose whether the constraint
 * targets the return type of the executable or its array of parameters.
 *
 * A constraint is both generic and cross-parameter if
 * <ul>
 *     <li>two kinds of {@code ConstraintValidator}s are attached to the
 *     constraint, one targeting {@link ValidationTarget#ANNOTATED_ELEMENT}
 *     and one targeting {@link ValidationTarget#PARAMETERS},</li>
 *     <li>or if a {@code ConstraintValidator} targets both
 *     {@code ANNOTATED_ELEMENT} and {@code PARAMETERS}.</li>
 * </ul>
 *
 * Such dual constraints are rare. See {@link SupportedValidationTarget} for more info.
 * <p>
 * Here is an example of constraint definition:
 * <pre>
 * &#64;Documented
 * &#64;Constraint(validatedBy = OrderNumberValidator.class)
 * &#64;Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
 * &#64;Retention(RUNTIME)
 * public &#64;interface OrderNumber {
 *     String message() default "{com.acme.constraint.OrderNumber.message}";
 *     Class&lt;?&gt;[] groups() default {};
 *     Class&lt;? extends Payload&gt;[] payload() default {};
 * }
 * </pre>
 *
 * @author Emmanuel Bernard
 * @author Gavin King
 * @author Hardy Ferentschik
 */
@Documented
@Target({ ANNOTATION_TYPE })
@Retention(RUNTIME)
public @interface Constraint {

    /**
     * {@link ConstraintValidator} classes implementing the constraint. The given classes
     * must reference distinct target types*  for a given {@link ValidationTarget}.} *  If two
     * {@code ConstraintValidator}s refer to the same type,*  an exception will occur.
     * <p>
     * At most one {@code ConstraintValidator} targeting the array of parameters of
     * methods or constructors (aka cross-parameter) is accepted. If two or more
     * are present, an exception will occur.
     *
     * @return array of {@(@code ConstraintValidator} classes implementing the constraint
     */
    Class<? extends ConstraintValidator<?, ?>>[] validatedBy();
}

A constraint is said to be generic if it has at least one constraint validator targeting the element annotated i.e. targeting the (returned) element annotated by the constraint (a bean, a field, a getter, a method/constructor return value or a method/constructor parameter). A constraint is said to be cross-parameter if it has one constraint validator targeting the array of parameters of a method or constructor (to validate the consistency of several method/constructor parameters). A Bean Validation constraint is most of the time either a generic constraint or a cross-parameter constraint. In rare situations, a constraint can be both.

Generic constraint annotations can target any of the following ElementTypes:

  • FIELD for constrained attributes

  • METHOD for constrained getters and constrained method return values

  • CONSTRUCTOR for constrained constructor return values

  • PARAMETER for constrained method and constructor parameters

  • TYPE for constrained beans

  • ANNOTATION_TYPE for constraints composing other constraints

  • TYPE_USE for container element constraints

Cross-parameter constraint annotations can target any of the following ElementTypes:

  • METHOD

  • CONSTRUCTOR

  • ANNOTATION_TYPE for cross-parameter constraints composing other cross-parameter constraints

A constraint annotation that is both can target the union of the generic and cross-parameter constraint annotations targets.

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 an UnexpectedTypeException. Care should be taken on defining the list of ConstraintValidators. The type resolution algorithm (see ConstraintValidator resolution algorithm) could lead to exceptions if the ConstraintValidator list leads to ambiguities.

At most one ConstraintValidator supporting cross-parameter validation must be present for a given constraint. A ConstraintDefinitionException is raised otherwise. The JavaDoc should clearly state if the constraint is a generic and / or a cross-parameter constraint.

If a constraint definition is not valid, a ConstraintDefinitionException is raised either at validation time or when the metadata is requested. Invalid constraint definitions causes are multiple but include missing or illegal message or groups elements (see Constraint definition properties).

Note

Bean Validation defines rules for applying constraint annotations in inheritance hierarchies, described in Inheritance (interface and superclass) and Method constraints in inheritance hierarchies. It is therefore not recommended to specify the meta annotation java.lang.annotation.Inherited at constraint annotation types, as it is not relevant in the context of Bean Validation and would conflict with the proposed rules.

3.1.1. Constraint definition properties

A constraint definition may have attributes that are specified at the time the constraint is applied to a JavaBean. The properties are mapped as annotation elements. The annotation element names message, groups, validationAppliesTo and payload are considered reserved names; annotation elements starting with valid are not allowed ; a constraint may use any other element name for its attributes.

3.1.1.1. message

Every constraint annotation must define a message element of type String.

String message() default "{com.acme.constraint.MyConstraint.message}";

The message element value is used to create the error message. See Message interpolation for a detailed explanation. It is recommended to default message values to resource bundle keys to enable internationalization. It is also recommended to use the following convention: the resource bundle key should be the fully qualified class name of the constraint annotation concatenated to .message as shown in the previous program listing.

Built-in Bean Validation constraints follow this convention.

3.1.1.2. groups

Every constraint annotation must define a groups element that specifies the processing groups with which the constraint declaration is associated. The type of the groups parameter is Class<?>[].

Class<?>[] groups() default {};

The default value must be an empty array.

If no group is specified when declaring the constraint on an element, the Default group is considered declared.

See groups for more information.

Groups are typically used to control the order in which constraints are evaluated, or to perform validation of the partial state of a JavaBean.

3.1.1.3. payload

Constraint annotations must define a payload element that specifies the payload with which the constraint declaration is associated. The type of the payload parameter is Payload[].

Class<? extends Payload>[] payload() default {};

The default value must be an empty array.

Each attachable payload extends Payload.

Listing 3.2: Payload interface
/**
 * Payload type that can be attached to a given
 * constraint declaration.
 * <p>/> 
 * Payloads are typically used to carry on metadata information
 * consumed by a validation client.
 * </p>
 * With the exceptionUse  of the {@link Unwrapping} payload types, the use of payloads is not
 * considered portable.
 *
 * @author Emmanuel Bernard
 * @author Gerhard Petracek
 */
public interface Payload {
}

Payloads are typically used by validation clients to associate some metadata information with a given constraint declaration.Payloads are typically non-portable. Describing payloads as interface extensions as opposed to a string-based approach allows an easier and more type-safe approach. Payloads are typically non-portable. An exception are the Unwrapping.Skip and Unwrapping.Unwrap payload types which are defined by this specification (see Implicit unwrapping of containers).

One use case for payload shown in Use of payload to associate severity to a constraint is to associate a severity to a constraint. This severity can be exploited by a presentation framework to adjust how a constraint failure is displayed.

Example 3.1:1. Use of payload to associate severity to a constraint
package com.acme.severity;

public class Severity {
    public static class Info implements Payload {};
    public static class Error implements Payload {};
}

public class Address {
    @NotNull(message="would be nice if we had one", payload=Severity.Info.class)
    public String getZipCode() { [...] }

    @NotNull(message="the city is mandatory", payload=Severity.Error.class)
    String getCity() { [...] }
}

The payload information can be retrieved from error reports via the ConstraintDescriptor either accessed through the ConstraintViolation objects (see ConstraintViolation) or through the metadata API (see ConstraintDescriptor).

3.1.1.4. validationAppliesTo

validationAppliesTo is used at constraint declaration time to clarify what the constraint targets (i.e. the annotated element, the method return value or the method parameters).

The element validationAppliesTo must only be present for constraints that are both generic and cross-parameter, it is mandatory in this situation. A ConstraintDefinitionException is raised if these rules are violated.

The type of the validationAppliesTo parameter is ConstraintTarget. The default value must be ConstraintTarget.IMPLICIT.

Listing 3.3:Example 2. validationAppliesTo and ConstraintTarget
ConstraintTarget validationAppliesTo() default ConstraintTarget.IMPLICIT;
/**
 * Defines the constraint target.
 *
 * @author Emmanuel Bernard
 * @since 1.1
 */
public enum ConstraintTarget {

    /**
     * Discover the type when no ambiguity is present
     * <ul>
     *     <li>if neither on a method nor a constructor, it implies the annotated element
     *     (type, field etc),</li>
     *     <li>if on a method or constructor with no parameter, it implies
     *     {@code RETURN_VALUE},</li>
     *     <li>if on a method with no return value ({@code void}), it implies
     *     {@code PARAMETERS}.</li>
     * </ul>
     * Otherwise, {@code IMPLICIT} is not accepted and either {@code RETURN_VALUE} or
     * {@code PARAMETERS} is required. This is the case for constructors with parameters
     * and methods with parameters and return value.
     */
    IMPLICIT,

    /**
     * Constraint applies to the return value of a method or a constructor.
     */
    RETURN_VALUE,

    /**
     * Constraint applies to the parameters of a method or a constructor
     */
    PARAMETERS
}

If a ConstraintTarget is used in an illegal situation, a ConstraintDeclarationException is raised either at validation time or when the metadata is requested. Examples of illegal situations are:

  • using IMPLICIT in a situation that cannot be inferred (see the JavaDoc for the detailed rules),

  • using PARAMETERS on a constructor or method that has no parameter,

  • using RETURN_VALUE on a method with no return value,

  • using PARAMETERS or RETURN_VALUE on a type - class or interface - or on a field.

Constraint users are encouraged to explicitly set the ConstraintTarget target when using a constraint supporting both on a method or constructor as it improves readability.

3.1.1.5. Constraint specific parameter

The constraint annotation definitions may define additional elements to parameterize the constraint. For example, a constraint that validates the length of a string can use an annotation element named length to specify the maximum length at the time the constraint is declared.

3.1.2. Examples

Example 3.2:3. Simple constraint definition
//assuming OrderNumberValidator is a generic constraint validator

package com.acme.constraint;

/**
 * Mark a String as representing a well formed order number
 */
@Documented
@Constraint(validatedBy = OrderNumberValidator.class)
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface OrderNumber {

    String message() default "{com.acme.constraint.OrderNumber.message}";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

Simple constraint definition marks a String as a well-formed order number. The constraint validator is implemented by OrderNumberValidator.

Example 3.3:4. Simple cross-parameter constraint definition
//assuming DateParametersConsistentValidator is a cross-parameter
//constraint validator

package com.acme.constraint;

/**
 * Cross-parameter constraint ensuring that two date parameters of a method are in the
 * correct order.
 */
@Documented
@Constraint(validatedBy = DateParametersConsistentValidator.class)
@Target({ METHOD, CONSTRUCTOR, ANNOTATION_TYPE })
@Retention(RUNTIME)
public @interface DateParametersConsistent {

    String message() default "{com.acme.constraint.DateParametersConsistent.message}";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

Simple cross-parameter constraint definition shows a cross-parameter constraint which ensures that two date parameters of a method are in the correct order. The constraint validator is implemented by DateParametersConsistentValidator.

Example 3.4:5. Constraint that is both generic and cross parameter
//assuming ELAssertValidator is both a generic and cross-parameter
//constraint validator

package com.acme.constraint;

/**
 * EL expression to be validated. This constraint accepts any type and can validate both the
 * annotated type or apply*  restrictions across parameters.
 */
@Documented
@Constraint(validatedBy = ELAssertValidator.class)
@Target({ METHOD, FIELD, TYPE, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface ELAssert {

    String message() default "{com.acme.constraint.ELAssertDateParametersConsistent.message}";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    ConstraintTarget validationAppliesTo() default ConstraintTarget.IMPLICIT;

    String expression();
}
@ELAssert(
    message="Please check that your passwords match and try again.",
    expression="param[1]==param[2]",
    validationAppliesTo=ConstraintType.PARAMETERS
)
public User createUser(String email, String password, String repeatPassword) { [...] }

Constraint that is both generic and cross parameter shows a constraint that can be applied both on the annotated element and across parameters of a method or a constructor. Note in this case the presence of validationAppliesTo.

Example 3.5:6. Constraint definition with default parameter
package com.acme.constraint;

/**
 * A frequency in Hz as audible to human ear. Adjustable to the age of the person. Accepts
 *Accept  Numbers.
 */
@Documented
@Constraint(validatedBy = AudibleValidator.class)
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface Audible {

    Age age() default Age.YOUNG;

    String message() default "{com.acme.constraint.Audible.message}";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    public enum Age {
        YOUNG,
        WONDERING,
        OLD
    }
}

Constraint definition with default parameter ensures that a given frequency is within the scope of human ears. The constraint definition includes an optional parameter that may be specified when the constraint is applied.

Example 3.6:7. Constraint definition with mandatory parameter
package com.acme.constraint;

/**
 * Defines the list of values accepted. Accepts int or Integer objects.
 */
@Documented
@Constraint(validatedBy = DiscreteListOfIntegerValidator.class)
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface Acceptable {

    int[] value();

    String message() default "{com.acme.constraint.Acceptable.message}";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

Constraint definition with mandatory parameter defines a list of acceptable values expressed as an array: the value property must be specified when the constraint is applied.

3.2. Applying multiple constraints of the same type

It is often useful to declare the same constraint more than once to the same target, with different properties. A common example is the @Pattern constraint, which validates that its target matches a specified regular expression. Other constraints have this requirement as well. The same constraint type can belong to different groups and have specific error messages depending on the targeted group.

To support this requirement, the Bean Validation provider treats regular annotations (annotations not annotated by @Constraint) whose value element has a return type of an array of constraint annotations in a special way. Each element in the value array are processed by the Bean Validation implementation as regular constraint annotations. This means that each constraint specified in the value element is applied to the target. The annotation must have retention RUNTIME and can be applied on a type, field, property, executable parameter, executable return value, executable cross-parameter or another annotation. It is recommended to use the same set of targets as the initial constraint.

Note to constraint designers: each constraint annotation should be coupled with its corresponding multi-valued annotation. The specification recommends, though does not mandate, the definition of an inner annotation named List. Each constraint annotation type should be meta-annotated with java.lang.annotation.Repeatable, referencing the corresponding List annotation. This marks the constraint annotation type as repeatable and lets users specify the constraint several times without explicitly using the List annotation. All built-in annotations follow this pattern.

Example 3.7:8. Multi-valued constraint definition
/**
 * Validate a zip code for a given country
 * The only supported type is String
 */
@Documented
@Constraint(validatedBy = ZipCodeValidator.class)
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Repeatable(List.class)
public @interface ZipCode {

    String countryCode();

    String message() default "{com.acme.constraint.ZipCode.message}";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    /**
     * Defines several @ZipCode annotations on the same element
     * @see (@link ZipCode}
     */
    @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
    @Retention(RUNTIME)
    @Documented
    @interface List {
        ZipCode[] value();
    }
}
Example 3.8:9. Multi-valued constraint declaration
public class Address {
    @ZipCode.List({ @ZipCode (countryCode = "fr", groups = Default.class, message = "zip code is not valid")), 
    @ZipCode(
        countryCode = "fr",
        groups = SuperUser.class,
        message = "zip code invalid. Requires overriding before saving."
    )} ) 
    private String zipCode;
}

In this example, both constraints apply to the zipCode field but with different groups and with different error messages. It is also possible to specify a constraint several times by explicitly using the @List annotation (though simply repeating the annotation is the preferred idiom as of Bean Validation 2.0 and Java 8):

Example 3.9: Multi-valued constraint declaration using explicit @List annotation (discouraged)
public class Address {
    @ZipCode.List( {
        @ZipCode(countryCode="fr", groups=Default.class,
            message = "zip code is not valid"),
        @ZipCode(countryCode="fr", groups=SuperUser.class,
            message = "zip code invalid. Requires overriding before saving.")
    } )
    private String zipCode;
}

Using two different multi-constraint annotations for the same underlying constraint type on the same target (i.e. class or property) is not considered portable and is discouraged.

3.3. Constraint composition

This specification allows you to compose constraints to create higher level constraints.

Constraint composition is useful in several ways:

  • Avoid duplication and facilitate reuse of more primitive constraints.

  • Expose primitive constraints as part of a composed constraint in the metadata API and enhance tool awareness.

Composition is done by annotating a constraint annotation with the composing constraint annotations.

Example 3.10:10. Composition is done by annotating the composed constraint
@Pattern(regexp = "[0-9]*")
@Size(min = 5, max = 5)
@Constraint(validatedBy = FrenchZipCodeValidator.class)
@Documented
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface FrenchZipCode {

    String message() default "Wrong zip code";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
    @Retention(RUNTIME)
    @Documented
    @interface List {
        FrenchZipCode[] value();
    }
}

Annotating an element with @FrenchZipCode (the composed annotation) is equivalent to annotating it with @Pattern(regexp="[0-9]*"), @Size(min=5, max=5) (the composing annotations) and @FrenchZipCode. More formally, each constraint annotation hosted on a constraint annotation is applied to the target element and this is done recursively. Note that the main annotation and its constraint validation implementation is also applied. By default, each failing constraint generates an error report. Groups from the main constraint annotation are inherited by the composing annotations. Any groups definition on a composing annotation is ignored. Payload from the main constraint annotation is inherited by the composing annotations. Any payload definition on a composing annotation is ignored. The constraint target from the main constraint annotation is inherited by the composing annotations. Any validationAppliesTo definition on a composing annotation is ignored.

The type upon which composed constraint is placed must be compatible with all constraints (composing and composed). A constraint designer should ensure that such a type exists and lists in the JavaDoc all the compatible types.

All composed and composing constraints must have a constraint type in common. In particular, it is not legal to mix a pure generic constraint and a pure cross-parameter constraint.

It is possible to ensure that composing annotations do not raise individual error reports. In this scenario, if one or more composing annotations are invalid, the main constraint is automatically considered invalid and the corresponding error report is generated. To mark a constraint as raising a single constraint error report if either the composed or one of the composing constraints fail, use the @ReportAsSingleViolation annotation.

Example 3.11:11. If any of the composing constraints fail, the error report corresponding to @FrenchZipCode is raised and none other.
@Pattern(regexp = "[0-9]*")
@Size(min = 5, max = 5)
@ReportAsSingleViolation
@Constraint(validatedBy = FrenchZipCodeValidator.class)
@Documented
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface FrenchZipCode {

    String message() default "Wrong zip code";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
    @Retention(RUNTIME)
    @Documented
    @interface List {
        FrenchZipCode[] value();
    }
}

The definition of @ReportAsSingleViolation is as follows.

Listing 3.4: @ReportAsSingleViolation annotation
/**
 * A constraint annotation hosting this annotation will return the
 * composed annotation error report if any of the composing annotations fail.
 * The error reports of each individual composing constraint are ignored.
 * <p>
 * Note: Evaluation of composed constraints stops on the first validation
 * error in case the composing constraint is annotated with
 * {@code @ReportAsSingleViolation}.
 *
 * @author Emmanuel Bernard
 */
@Target({ ANNOTATION_TYPE })
@Retention(RUNTIME)
@Documented
public @interface ReportAsSingleViolation {
}

More specifically, if a composed constraint is marked as @ReportAsSingleViolation, the evaluation of the composing constraints stops at the first failing constraint and the error report corresponding to the composed constraint is generated and returned.

Composing annotations can define the value of message and custom attributes (excluding groups, payload and validationAppliesTo) but these are fixed in the composed constraint definition.

Example 3.12:12. Composing annotations can use attributes. They are fixed for a given main annotation. All @FrenchZipCode constraints have a @Size restricted to 5.
@Pattern(regexp = "[0-9]*")
@Size(min = 5, max = 5)
@Constraint(validatedBy = FrenchZipCodeValidator.class)
@Documented
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface FrenchZipCode {

    String message() default "Wrong zip code";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
    @Retention(RUNTIME)
    @Documented
    @interface List {
        FrenchZipCode[] value();
    }
}

It is possible to override attributes and messages defined on a composing annotation. An attribute from the main annotation is used to override one or more attributes of the composing annotations. Such an attribute is annotated with onethe @OverridesAttribute annotation or moreits multivalued equivalent @OverridesAttribute.List annotations.

Example 3.13:13. Attributes from composing annotations can be overridden by attributes from the composed annotation
@Pattern(regexp = "[0-9]*")
@Size
@Constraint(validatedBy = FrenchZipCodeValidator.class)
@Documented
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface FrenchZipCode {

    String message() default "Wrong zip code";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    @OverridesAttribute.List({ @OverridesAttribute (constraint = Size.class, name = "min")), 
    @OverridesAttribute(constraint = Size.class, name = "max")}) 
    int size() default 5;

    @OverridesAttribute(constraint = Size.class, name = "message")
    String sizeMessage() default "{com.acme.constraint.FrenchZipCode.zipCode.size}";

    @OverridesAttribute(constraint = Pattern.class, name = "message")
    String numberMessage() default "{com.acme.constraint.FrenchZipCode.number.size}";

    @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
    @Retention(RUNTIME)
    @Documented
    @interface List {

        FrenchZipCode[] value();
    }
}

The value of the composed constraint attribute annotated with @OverridesAttribute (@FrenchZipCode.sizeMessage) is applied to the composing constraint attribute named after @OverridesAttribute.name and hosted on the composing constraint of type @OverridesAttribute.constraint (@Size.message). Similarly, @FrenchZipCode.numberMessage value is mapped to @Pattern.message.

If left undefined, the default value for @OverridesAttribute.name is the name of the composed constraint attribute hosting the @OverridesAttribute annotation.

The types of the overridden and overriding attributes must be identical.

Note

A composing constraint can itself be a composed constraint. In this case, attribute values are overridden recursively according to the described rules. Note however, that a forwarding rule (as defined by @OverridesAttribute) is only applied to the direct composing constraints.

@FrenchZipCode(size=9, sizeMessage="Zip code should be of size {max}")

is equivalent to

@FrenchZipCode

if @FrenchZipCode is defined as

@Pattern(regexp = "[0-9]*")
@Size(min = 9, max = 9, message = "Zip code should be of size {max}")
@Constraint(validatedBy = FrenchZipCodeValidator.class)
@Documented
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface FrenchZipCode {

    String message() default "Wrong zip code";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
    @Retention(RUNTIME)
    @Documented
    @interface List {

        FrenchZipCode[] value();
    }
}

If a constraint is used more than once as a composing constraint, the multi value constraints model as described in Applying multiple constraints of the same type is used.

To select a specific composing constraint, OverridesAttribute.constraintIndex is used. IfIt represents the composing constraints are directly given on the composed constraint (i.e. via the repeatable annotation feature), constraintIndex refers to the left-to-right order of the constraints of this typeindex in which they are given on the composed constraint. If the composing constraints are specified using their corresponding List annotation, constraintIndex refers to the index within the value array.

A composing constraint must not be given directly on the composed constraint and using the corresponding List annotation at the same time. A ConstraintDeclarationException will be raised in this case.

If index is undefined, the single constraint declaration is targeted.

Example 3.14:14. Use of constraintIndex in @OverridesAttribute
@DocumentedPattern .List({ 
@Constraint(validatedBy = {})
@Pattern(regexp = "[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}")),  // email
@Pattern(regexp = ".*?emmanuel.*?") // emmanuel}) 
@Constraint (validatedBy = {}) @Documented @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface EmmanuelsEmail {

    String message() default "Not emmanuel's email";

    @OverridesAttribute(constraint = Pattern.class, name = "message", constraintIndex = 0)
    String emailMessage() default "Not an email";

    @OverridesAttribute(constraint = Pattern.class, name = "message", constraintIndex = 1)
    String emmanuelMessage() default "Not Emmanuel";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
    @Retention(RUNTIME)
    @Documented
    @interface List {

        EmmanuelsEmail[] value();
    }
}

@OverridesAttribute definition is as follows:

Listing 3.5: @OverridesAttribute annotation
/**
 * Marks an attribute as overriding the attribute of a composing constraint.
 * Both attributes must share the same type.
 *
 * @author Emmanuel Bernard
 */
@Documented
@Retention(RUNTIME)
@Target({ METHOD })
@Repeatable(List.class)
public @interface OverridesAttribute {

    /**
     * @return constraint type the attribute is overriding
     */
    Class<? extends Annotation> constraint();

    /**
     * Name of the Constraint attribute overridden.
     * Defaults to the name of the attribute hosting {@code @OverridesAttribute}.
     *
     * @return name of constraint attribute overridden
     */
    String name()();  default "";

    /**
     * The index of the targeted constraint declaration when using
     * multiple constraints of the same type.
     * <p>
     * The index represents the index of the constraint in the
     * {@code value()} array.
     * <p>
     * By default, no index is defined and the single constraint declaration
     * is targeted.
     *
     * @return constraint declaration index if multivalued annotation is used
     */
    int constraintIndex() default -1;

    /**
     * Defines several {@link OverridesAttribute} annotations on the same element
     *
     * @see javax.validation.OverridesAttribute
     */
    @Documented
    @Target({ METHOD })
    @Retention(RUNTIME)
    public @interface List {

        OverridesAttribute[] value();
    }
}

The following elements uniquely identify an overridden constraint attribute:

  • @OverridesAttribute.constraint

  • @OverridesAttribute.name

  • @OverridesAttribute.constraintIndex

If the composition is invalid, e.g.

  • infinitely recursive composition

  • wrong attribute overriding

  • a single attribute mapped to more than one source attribute

  • a composing and composed constraint marked as different constraint types (i.e., generic and cross-parameter)

  • etc.

a ConstraintDefinitionException is raised either at validation time or when the metadata is requested.

Constraint designers are encouraged to make use of composition (recursively or not) based on the built-in constraints defined by the specification. The composing constraints are exposed through the Bean Validation metadata API (ConstraintDescriptor). This metadata is particularly useful for third-party metadata consumers like persistence frameworks generating database schemas (such as Java Persistence) or presentation frameworks.

3.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 @Constraint annotation that decorates the constraint definition. The constraint validation implementation implements the ConstraintValidator interface.

Listing 3.6: ConstraintValidator interface
/**
 * Defines the logic to validate a given constraint {@code A}
 * for a given object type {@code T}.
 * <p>
 * Implementations must comply to the following restriction:
 * <ul>
 *     <li>{@code T} must resolve to a non parameterized type</li>
 *     <li>or generic parameters of {@code T} must be unbounded
 *     wildcard types</li>
 * </ul>
 * <p>
 * The annotation {@link SupportedValidationTarget} can be put on a
 * {@code ConstraintValidator} implementation to mark it as supporting
 * cross-parameter constraints. Check out {@link SupportedValidationTarget}
 * and {@link Constraint} for more information.
 *
 * @param <A> the annotation type handled by an implementation
 * @param <T> the target type supported by an implementation
 *
 * @author Emmanuel Bernard
 * @author Hardy Ferentschik
 */
public interface ConstraintValidator<A extends Annotation, T> {

    /**
     * Initializes the validator in preparation for
     * {@link #isValid(Object, ConstraintValidatorContext)} calls.
     * The constraint annotation for a given constraint declaration
     * is passed.
     * <p>
     * This method is guaranteed to be called before any use of this instance for
     * validation.
     * <p>
     * The default implementation is a no-op.
     *
     * @param constraintAnnotation annotation instance for a given constraint declaration
     */
    default void initialize(A constraintAnnotation) {
    }); 

    /**
     * Implements the validation logic.
     * The state of {@code value} must not be altered.
     * <p>
     * This method can be accessed concurrently, thread-safety must be ensured
     * by the implementation.
     *
     * @param value object to validate
     * @param context context in which the constraint is evaluated
     *
     * @return {@code false} if {@code value} does not pass the constraint
     */
    boolean isValid(T value, ConstraintValidatorContext context);
}

Some restrictions apply on the generic type T (used in the isValid() method). T must

  • resolve to a non parameterized type (i.e. because the type is not using generics or because the raw type is used instead of the generic version)

  • or generic parameters of T must be unbounded wildcard types (i.e. <?>).

Note

This restriction is not a theoretical limitation and a future version of the specification might allow it.

By default, a ConstraintValidator targets the (returned) element annotated by the constraint. You can make a ConstraintValidator target the array of parameters of a method or constructor (aka cross-parameter) by annotating the validator implementation with @SupportedValidationTarget.

Listing 3.7: Example 15. @SupportedValidationTarget annotation and ValidationTarget enum
package javax.validation.constraintvalidation;

/**
 * Defines the target(s) a {@link ConstraintValidator} can validate.
 * <p>/> 
 * A {@code ConstraintValidator} can target the (returned) element
 * annotated by the constraint, the array of parameters of a method
 * or constructor (aka cross-parameter) or both.
 * <p>/> 
 * If {@code @SupportedValidationTarget} is not present, the
 * {@code ConstraintValidator} targets the (returned) element annotated
 * by the constraint.
 * <p>/> 
 * A {@code ConstraintValidator} targeting cross-parameter must accept
 * {@code Object[]} (or {@code Object}) as the type of object it validates.
 *
 * @author Emmanuel Bernard
 * @since 1.1
 */
@Documented
@Target({ TYPE })
@Retention(RUNTIME)
public @interface SupportedValidationTarget {

    ValidationTarget[] value();
}
package javax.validation.constraintvalidation;

/**
 * List of possible targets for a {@link ConstraintValidator}.
 *
 * @author Emmanuel Bernard
 * @since 1.1
 */
public enum ValidationTarget {

    /**
     * (Returned) element annotated by the constraint.
     */
    ANNOTATED_ELEMENT,

    /**
     * Array of parameters of the annotated method or constructor (aka cross-parameter).
     */
    PARAMETERS
}

A ConstraintValidator implementation can target both annotated elements and array of parameters.

If a ConstraintValidator targets array of parameters (cross-parameter), T must resolve to Object[] (or Object) in order to have the array of parameter values passed to the isValid() method. A ConstraintDefinitionException is raised otherwise.

Example 3.15:16. Example of cross parameter ConstraintValidator
@SupportedValidationTarget(ValidationTarget.PARAMETERS)
public class ScriptAssertValidator implements ConstraintValidator<ScriptAssert,Object[]> {
    @Override
    public void initialize(ScriptAssert constraintAnnotation) {
        [...]
    }

    @Override
    public boolean isValid(Object[] value, ConstraintValidatorContext context) {
        [...]
    }
}

Valid ConstraintValidator definitions shows some examples of valid definitions.

Example 3.16:17. Valid ConstraintValidator definitions
//String is not making use of generics
public class SizeValidatorForString implements ConstraintValidator<Size, String> {
    [...]
}

//Collection uses generics but the raw type is used
public class SizeValidatorForCollection implements ConstraintValidator<Size, Collection> {
    [...]
}

//Collection uses generics and unbounded wildcardwindcard  type
public class SizeValidatorForCollection implements ConstraintValidator<Size, Collection<?>> {
    [...]
}

//Validator for cross-parameter constraint
@SupportedValidationTarget(ValidationTarget.PARAMETERS)
public class DateParametersConsistentValidator
    implements ConstraintValidator<DateParametersConsistent, Object[]> {
    [...]
}

//Validator for both annotated elements and executable parameters
@SupportedValidationTarget({ValidationTarget.ANNOTATED_ELEMENT, ValidationTarget.PARAMETERS})
public class ELScriptValidator implements ConstraintValidator<ELScript, Object> {
    [...]
}

And some invalid definitions in Invalid ConstraintValidator definitions.

Example 3.17:18. Invalid ConstraintValidator definitions
//parameterized type
public class SizeValidatorForString implements ConstraintValidator<Size, Collection<String>> {
    [...]
}

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

//cross-parameter validator accepting the wrong type
@SupportedValidationTarget(ValidationTarget.PARAMETERS)
public class NumberPositiveValidator implements ConstraintValidator<NumberPositive, Number> {
    [...]
}

The lifecycle of a constraint validation implementation instance is undefined. Bean Validation providers are allowed to cache ConstraintValidator instances retrieved from the ConstraintValidatorFactory.

The initialize() method is called by the Bean validation provider prior to any use of the constraint implementation. As of Bean Validation 2.0, initialize() has an empty default implementation, allowing to omit the implementation from validators that dont need to access any constraint attributes.

The isValid() method is evaluated by the Bean Validation provider each time a given value is validated. It returns false if the value is not valid, true otherwise. isValid() implementations must be thread-safe.

If the property is of an unanticipated type, an UnexpectedTypeException is raised. ConstraintValidator implementations raise this exception themselves if they receive an unsupported type. However, constraint designers are encouraged to make use of specialized ConstraintValidator implementations and delegate the type resolution to the Bean Validation provider (see the type matching algorithm described in ConstraintValidator resolution algorithm).

If an exception occurs either in the initialize() or isValid() method, the runtime exception is wrapped into a ValidationException by the Bean Validation engine.

The constraint validation implementation is not allowed to change the state of the value passed to isValid().

Note

While not mandatory, it is considered a good practice to split the core constraint validation from the not null constraint validation (for example, an @Email constraint will return true on a null object, i.e. will not take care of the @NotNull validation).

null can have multiple meanings but is commonly used to express that a value does not make sense, is not available or is simply unknown. Those constraints on the value are orthogonal in most cases to other constraints. For example a String, if present, must be an email but can be null. Separating both concerns is a good practice.

The ConstraintValidatorContext object passed to the isValid() method carries information and operations available in the context the constraint is validated to.

Listing 3.8: ConstraintValidatorContext interface
/**
 * Provides contextual data and operation when applying a given constraint validator.
 *
 * At least one {@link ConstraintViolation} must be defined (either the default one,
 * of if the default {@code ConstraintViolation} is disabled, a custom one).
 *
 * @author Emmanuel Bernard
 * @author Guillaume Smet
 */
public interface ConstraintValidatorContext {

    /**
     * Disables the default {@link ConstraintViolation} object generation (which
     * is using the message template declared on the constraint).
     * <p>
     * Useful to set a different violation message or generate a {@code ConstraintViolation}
     * based on a different property.
     */
    void disableDefaultConstraintViolation();

    /**
     * @return the current un-interpolated default message
     */
    String getDefaultConstraintMessageTemplate();

    /**
     * Returns the provider for obtaining the current time in the form of a {@link Clock},
     * e.g. when validating the {@code Future} and {@code Past} constraints.
     *
     * @return the provider for obtaining the current time, never {@code null}. If no
     * specific provider has been configured during bootstrap, a default implementation using
     * the current system time and the current default time zone as returned by
     * {@link Clock#systemDefaultZone()} will be returned.
     *
     * @since 2.0
     */
    ClockProvider getClockProvider();

    /**
     * Returns a constraint violation builder building a violation report
     * allowing to optionally associate it to a sub path.
     * The violation message will be interpolated.
     * <p>
     * To create the {@link ConstraintViolation}, one must call either one of
     * the {@code addConstraintViolation()} methods available in one of the
     * interfaces of the fluent API.
     * If another method is called after {@code addConstraintViolation()} on
     * {@code ConstraintViolationBuilder} or any of its associated nested interfaces
     * an {@code IllegalStateException} is raised.
     * <p>
     * If {@link ConstraintValidator#isValid(Object, ConstraintValidatorContext)} returns
     * {@code false}, a {@code ConstraintViolation} object will be built per constraint
     * violation report including the default one (unless
     * {@link #disableDefaultConstraintViolation()} has been called).
     * <p>
     * {@code ConstraintViolation} objects generated from such a call
     * contain the same contextual information (root bean, path and so on) unless
     * the path has been overridden.
     * <p>
     * To create a different {@code ConstraintViolation}, a new constraint violation builder
     * has to be retrieved from {@code ConstraintValidatorContext}
     *
     * Here are a few usage examples:
     * <pre>
     * //assuming the following domain model
     * public class User {
     *     public Map&lt;String,Address&gt; getAddresses() { ... }
     * }
     *
     * public class Address {
     *     public String getStreet() { ... }
     *     public Country getCountry() { ... }
     * }
     *
     * public class Country {
     *     public String getName() { ... }
     * }
     *
     * //From a property-level constraint on User.addresses
     * //Build a constraint violation on the default path - i.e. the "addresses" property
     * context.buildConstraintViolationWithTemplate( "this detail is wrong" )
     *             .addConstraintViolation();
     *
     * //From a class level constraint on Address
     * //Build a constraint violation on the default path + "street"
     * //i.e. the street property of Address
     * context.buildConstraintViolationWithTemplate( "this detail is wrong" )
     *             .addPropertyNode( "street" )
     *             .addConstraintViolation();
     *
     * //From a property-level constraint on  User.addresses
     * //Build a constraint violation on the default path + the bean stored
     * //under the "home" key in the map
     * context.buildConstraintViolationWithTemplate( "Incorrect home address" )
     *             .addBeanNode()
     *                 .inContainer( Map.class, 1 )
     *                 .inIterable().atKey( "home" )
     *             .addConstraintViolation();
     *
     * //From a class level constraint on User
     * //Build a constraint violation on the default path + addresses["home"].country.name
     * //i.e. property "country.name" on the object stored under "home" in the map
     * context.buildConstraintViolationWithTemplate( "this detail is wrong" )
     *             .addPropertyNode( "addresses" )
     *             .addPropertyNode( "country" )
     *                 .inContainer( Map.class, 1 )
     *                 .inIterable().atKey( "home" )
     *             .addPropertyNode( "name" )
     *             .addConstraintViolation();
     *
     * //From a class level constraint on User
     * //Build a constraint violation on the default path + addresses["home"].&lt;map key&gt;
     * //i.e. a container element constraint violation for the map key
     * context.buildConstraintViolationWithTemplate( "the map key is invalid" )
     *             .addPropertyNode( "addresses" )
     *             .addContainerElementNode( "&lt;map key&gt;", Map.class, 0 )
     *                 .inIterable().atKey( "invalid" )
     *             .addConstraintViolation();
     * </pre>
     * <p>
     * Cross-parameter constraints on a method can create a node specific
     * to a particular parameter if required. Let's explore a few examples:
     * <pre>
     * //Cross-parameter constraint on method
     * //createUser(String password, String passwordRepeat)
     * //Build a constraint violation on the default path + "passwordRepeat"
     * context.buildConstraintViolationWithTemplate("Passwords do not match")
     *             .addParameterNode(1)
     *             .addConstraintViolation();
     *
     * //Cross-parameter constraint on a method
     * //mergeAddresses(Map&lt;String,Address&gt; addresses,
     * //        Map&lt;String,Address&gt; otherAddresses)
     * //Build a constraint violation on the default path + "otherAddresses["home"]
     * //i.e. the Address bean hosted in the "home" key of the "otherAddresses" map parameter
     * context.buildConstraintViolationWithTemplate(
     *         "Map entry home present in both and does not match")
     *             .addParameterNode(1)
     *             .addBeanNode()
     *                 .inContainer( Map.class, 1 )
     *                 .inIterable().atKey("home")
     *             .addConstraintViolation();
     *
     * //Cross-parameter constraint on a method
     * //mergeAddresses(Map&lt;String,Address&gt; addresses,
     * //        Map&lt;String,Address&gt; otherAddresses)
     * //Build a constraint violation on the default path + "otherAddresses["home"].city
     * //i.e. on the "city" property of the Address bean hosted in
     * //the "home" key of the "otherAddresses" map
     * context.buildConstraintViolationWithTemplate(
     *         "Map entry home present in both but city does not match")
     *             .addParameterNode(1)
     *             .addPropertyNode("city")
     *                 .inContainer( Map.class, 1 )
     *                 .inIterable().atKey("home")
     *             .addConstraintViolation();
     * </pre>
     *
     * @param messageTemplate new un-interpolated constraint message
     * @return returns a constraint violation builder
     */
    ConstraintViolationBuilder buildConstraintViolationWithTemplate(String messageTemplate);

    /**
     * Returns an instance of the specified type allowing access to
     * provider-specific APIs. If the Bean Validation provider
     * implementation does not support the specified class,
     * {@link ValidationException} is thrown.
     *
     * @param type the class of the object to be returned
     * @param <T> the type of the object to be returned
     * @return an instance of the specified class
     * @throws ValidationException if the provider does not support the call
     *
     * @since 1.1
     */
    <T> T unwrap(Class<T> type);

    /**
     * {@link ConstraintViolation} builder allowing to optionally associate
     * the violation report to a sub path.
     * <p>
     * To create the {@code ConstraintViolation}, one must call either one of
     * the {@code addConstraintViolation()} methods available in one of the
     * interfaces of the fluent API.
     * <p>
     * If another method is called after {@code addConstraintViolation()} on
     * {@code ConstraintViolationBuilder} or any of its associated objects
     * an {@code IllegalStateException} is raised.
     */
    interface ConstraintViolationBuilder {

        /**
         * Adds a node to the path the {@link ConstraintViolation} will be associated to.
         * <p>
         * {@code name} describes a single property. In particular,
         * dot (.) is not allowed.
         *
         * @param name property name
         * @return a builder representing node {@code name}
         * @deprecated since 1.1 - replaced by {@link #addPropertyNode(String)},
         *             {@link #addBeanNode()} and {@link #addParameterNode(int)}
         */
        NodeBuilderDefinedContext addNode(String name);

        /**
         * Adds a property node to the path the {@link ConstraintViolation}
         * will be associated to.
         * <p>
         * {@code name} describes a single property. In particular,
         * dot (.) is not allowed.
         *
         * @param name property name
         * @return a builder representing node {@code name}
         * @throws IllegalArgumentException if the name is null
         *
         * @since 1.1
         */
        NodeBuilderCustomizableContext addPropertyNode(String name);

        /**
         * Adds a bean node (class-level) to the path the {@link ConstraintViolation}
         * will be associated to.
         * Note that bean nodes are always leaf nodes.
         *
         * @return a builder representing the bean node
         *
         * @since 1.1
         */
        LeafNodeBuilderCustomizableContext addBeanNode();

        /**
         * Adds a container element node to the path the {@link ConstraintViolation}
         * will be associated to.
         *
         * @param name the node name
         * @param containerType the type of the container
         * @param typeArgumentIndex the index of the type argument
         * @return a builder representing the container element node
         * @throws IllegalArgumentException if the index is not valid
         *
         * @since 2.0
         */
        ContainerElementNodeBuilderCustomizableContext addContainerElementNode(String name,
                Class<?> containerType, Integer typeArgumentIndex);

        /**
         * Adds a method parameter node to the path the {@link ConstraintViolation}
         * will be associated to.
         * The parameter index must be valid (i.e. within the boundaries of the method
         * parameter indexes). May only be called from within cross-parameter validators.
         *
         * @param index the parameter index
         * @return a builder representing the index-th parameter node
         * @throws IllegalArgumentException if the index is not valid
         *
         * @since 1.1
         */
        NodeBuilderDefinedContext addParameterNode(int index);

        /**
         * Adds the new {@link ConstraintViolation} to be generated if the
         * constraint validator marks the value as invalid.
         * <p>
         * Methods of this {@code ConstraintViolationBuilder} instance and its nested
         * objects throw {@code IllegalStateException} from now on.
         *
         * @return the {@code ConstraintValidatorContext} instance the
         *         {@code ConstraintViolationBuilder} comes from
         */
        ConstraintValidatorContext addConstraintViolation();

        /**
         * Represents a node whose context is known
         * (i.e. index, key and isInIterable)
         * and that is a leaf node (i.e. no subnode can be added).
         *
         * @since 1.1
         */
        interface LeafNodeBuilderDefinedContext {

            /**
             * Adds the new {@link ConstraintViolation} to be generated if the
             * constraint validator marks the value as invalid.
             * <p>
             * Methods of the {@code ConstraintViolationBuilder} instance this object
             * comes from and the constraint violation builder nested
             * objects throw {@code IllegalStateException} after this call.
             *
             * @return {@code ConstraintValidatorContext} instance the
             *         {@code ConstraintViolationBuilder} comes from
             */
            ConstraintValidatorContext addConstraintViolation();
        }

        /**
         * Represents a node whose context is
         * configurable (i.e. index, key and isInIterable)
         * and that is a leaf node (i.e. no subnode can be added).
         *
         * @since 1.1
         */
        interface LeafNodeBuilderCustomizableContext {

            /**
             * Marks the node as being in an iterable, e.g. array, {@code Iterable} or a
             * {@code Map}.
             *
             * @return a builder representing iterable details
             */
            LeafNodeContextBuilder inIterable();

            /**
             * Marks the node as being in a container such as a {@code List}, {@code Map} or
             * {@code Optional}.
             *
             * @param containerClass the type of the container
             * @param typeArgumentIndex type index of the concerned type argument
             * @return a builder representing the current node
             * @throws IllegalArgumentException if the index is not valid
             *
             * @since 2.0
             */
            LeafNodeBuilderCustomizableContext inContainer(Class<?> containerClass,
                                                           Integer typeArgumentIndex);

            /**
             * Adds the new {@link ConstraintViolation} to be generated if the
             * constraint validator mark the value as invalid.
             * <p>
             * Methods of the {@code ConstraintViolationBuilder} instance this object
             * comes from and the constraint violation builder nested
             * objects throw {@code IllegalStateException} after this call.
             *
             * @return {@code ConstraintValidatorContext} instance the
             *         {@code ConstraintViolationBuilder} comes from
             */
            ConstraintValidatorContext addConstraintViolation();
        }

        /**
         * Represents refinement choices for a node which is
         * in an iterable, e.g. array, {@code IterableIterator} or {@code Map}.
         * <p>
         * If the iterableiterator  is an indexed collection or a map,
         * the index or the key should be set.
         * <p>
         * The node is a leaf node (i.e. no subnode can be added).
         *
         * @since 1.1
         */
        interface LeafNodeContextBuilder {

            /**
             * Defines the key the object is into the {@code Map}.
             *
             * @param key map key
             * @return a builder representing the current node
             */
            LeafNodeBuilderDefinedContext atKey(Object key);

            /**
             * Defines the index the object is into the {@code List} or array
             *
             * @param index index
             * @return a builder representing the current node
             */
            LeafNodeBuilderDefinedContext atIndex(Integer index);

            /**
             * Adds the new {@link ConstraintViolation} to be generated if the
             * constraint validator mark the value as invalid.
             * <p>
             * Methods of the {@code ConstraintViolationBuilder} instance this object
             * comes from and the constraint violation builder nested
             * objects throw {@code IllegalStateException} after this call.
             *
             * @return {@code ConstraintValidatorContext} instance the
             *           {@code ConstraintViolationBuilder} comes from
             */
            ConstraintValidatorContext addConstraintViolation();
        }

        /**
         * Represents a node whose context is known
         * (i.e. index, key and isInIterable)
         * and that is not necessarily a leaf node (i.e. subnodes can
         * be added).
         */
        interface NodeBuilderDefinedContext {

            /**
             * Adds a node to the path the {@link ConstraintViolation} will be associated to.
             * <p>
             * {@code name} describes a single property. In particular,
             * dot (.) is not allowed.
             *
             * @param name property name
             * @return a builder representing node {@code name}
             * @deprecated since 1.1 - replaced by {@link #addPropertyNode(String)}
             *             and {@link #addBeanNode()}
             */
            NodeBuilderCustomizableContext addNode(String name);

            /**
             * Adds a property node to the path the {@link ConstraintViolation}
             * will be associated to.
             * <p>
             * {@code name} describes a single property. In particular,
             * dot (.) is not allowed.
             *
             * @param name property name
             * @return a builder representing node {@code name}
             * @throws IllegalArgumentException if the name is null
             *
             * @since 1.1
             */
            NodeBuilderCustomizableContext addPropertyNode(String name);

            /**
             * Adds a bean node (class-level) to the path the {@link ConstraintViolation}
             * will be associated to.
             * Note that bean nodes are always leaf nodes.
             *
             * @return a builder representing the bean node
             *
             * @since 1.1
             */
            LeafNodeBuilderCustomizableContext addBeanNode();

            /**
             * Adds a container element node to the path the {@link ConstraintViolation}
             * will be associated to.
             *
             * @param name the node name
             * @param containerType the type of the container
             * @param typeArgumentIndex the index of the type argument
             * @return a builder representing the container element node
             * @throws IllegalArgumentException if the index is not valid
             *
             * @since 2.0
             */
            ContainerElementNodeBuilderCustomizableContext addContainerElementNode(
                    String name, Class<?> containerType, Integer typeArgumentIndex);

            /**
             * Adds the new {@link ConstraintViolation} to be generated if the
             * constraint validator marks the value as invalid.
             * <p>
             * Methods of the {@code ConstraintViolationBuilder} instance this object
             * comes from and the constraint violation builder nested
             * objects throw {@code IllegalStateException} after this call.
             *
             * @return {@code ConstraintValidatorContext} instance the
             *           {@code ConstraintViolationBuilder} comes from
             */
            ConstraintValidatorContext addConstraintViolation();
        }

        /**
         * Represents a node whose context is
         * configurable (i.e. index, key and isInIterable)
         * and that is not necessarily a leaf node (i.e. subnodes can
         * be added).
         */
        interface NodeBuilderCustomizableContext {

            /**
             * Marks the node as being in an iterable, e.g. array, {@code Iterable} or a
             * {@code Map}.
             *
             * @return a builder representing iterable details
             */
            NodeContextBuilder inIterable();

            /**
             * Marks the node as being in a container such as a {@code List}, {@code Map} or
             * {@code Optional}.
             *
             * @param containerClass the type of the container
             * @param typeArgumentIndex type index of the concerned type argument
             * @return a builder representing the current node
             * @throws IllegalArgumentException if the index is not valid
             *
             * @since 2.0
             */
            NodeBuilderCustomizableContext inContainer(Class<?> containerClass,
                                                       Integer typeArgumentIndex);

            /**
             * Adds a node to the path the {@link ConstraintViolation} will be associated to.
             *
             * {@code name} describes a single property. In particular,
             * dot (.) is not allowed.
             *
             * @param name property name
             * @return a builder representing node {@code name}
             * @deprecated since 1.1 - replaced by {@link #addPropertyNode(String)}
             *             and {@link #addBeanNode()}
             */
            NodeBuilderCustomizableContext addNode(String name);

            /**
             * Adds a property node to the path the {@link ConstraintViolation}
             * will be associated to.
             *
             * {@code name} describes a single property. In particular,
             * dot (.) is not allowed.
             *
             * @param name property name
             * @return a builder representing node {@code name}
             * @throws IllegalArgumentException if the name is null
             *
             * @since 1.1
             */
            NodeBuilderCustomizableContext addPropertyNode(String name);

            /**
             * Adds a bean node (class-level) to the path the {@link ConstraintViolation}
             * will be associated to.
             * Note that bean nodes are always leaf nodes.
             *
             * @return a builder representing the bean node
             *
             * @since 1.1
             */
            LeafNodeBuilderCustomizableContext addBeanNode();

            /**
             * Adds a container element node to the path the {@link ConstraintViolation}
             * will be associated to.
             *
             * @param name the node name
             * @param containerType the type of the container
             * @param typeArgumentIndex the index of the type argument
             * @return a builder representing the container element node
             * @throws IllegalArgumentException if the index is not valid
             *
             * @since 2.0
             */
            ContainerElementNodeBuilderCustomizableContext addContainerElementNode(
                    String name, Class<?> containerType, Integer typeArgumentIndex);

            /**
             * Adds the new {@link ConstraintViolation} to be generated if the
             * constraint validator mark the value as invalid.
             * <p>
             * Methods of the {@code ConstraintViolationBuilder} instance this object
             * comes from and the constraint violation builder nested
             * objects throw {@code IllegalStateException} after this call.
             *
             * @return {@code ConstraintValidatorContext} instance the
             *           {@code ConstraintViolationBuilder} comes from
             */
            ConstraintValidatorContext addConstraintViolation();
        }

        /**
         * Represents refinement choices for a node which is
         * in an iterable, e.g. array, {@code IterableIterator} or {@code Map}.
         * <p>
         * If the iterableiterator  is an indexed collection or a map,
         * the index or the key should be set.
         * <p>
         * The node is not necessarily a leaf node (i.e. subnodes can
         * be added).
         */
        interface NodeContextBuilder {

            /**
             * Defines the key the object is into the {@code Map}.
             *
             * @param key map key
             * @return a builder representing the current node
             */
            NodeBuilderDefinedContext atKey(Object key);

            /**
             * Defines the index the object is into the {@code List} or array.
             *
             * @param index index
             * @return a builder representing the current node
             */
            NodeBuilderDefinedContext atIndex(Integer index);

            /**
             * Adds a node to the path the {@code ConstraintViolation} will be associated to.
             *
             * {@code name} describes a single property. In particular,
             * dot (.) is not allowed.
             *
             * @param name property name
             * @return a builder representing node {@code name}
             * @deprecated since 1.1 - replaced by {@link #addPropertyNode(String)}
             *             and {@link #addBeanNode()}
             */
            NodeBuilderCustomizableContext addNode(String name);

            /**
             * Adds a property node to the path the {@link ConstraintViolation}
             * will be associated to.
             *
             * {@code name} describes a single property. In particular,
             * dot (.) is not allowed.
             *
             * @param name property name
             * @return a builder representing node {@code name}
             * @throws IllegalArgumentException if the name is null
             *
             * @since 1.1
             */
            NodeBuilderCustomizableContext addPropertyNode(String name);

            /**
             * Adds a bean node (class-level) to the path the {@link ConstraintViolation}
             * will be associated to.
             * <p>
             * Note that bean nodes are always leaf nodes.
             *
             * @return a builder representing the bean node
             *
             * @since 1.1
             */
            LeafNodeBuilderCustomizableContext addBeanNode();

            /**
             * Adds a container element node to the path the {@link ConstraintViolation}
             * will be associated to.
             *
             * @param name the node name
             * @param containerType the type of the container
             * @param typeArgumentIndex the index of the type argument
             * @return a builder representing the container element node
             * @throws IllegalArgumentException if the index is not valid
             *
             * @since 2.0
             */
            ContainerElementNodeBuilderCustomizableContext addContainerElementNode(
                    String name, Class<?> containerType, Integer typeArgumentIndex);

            /**
             * Adds the new {@link ConstraintViolation} to be generated if the
             * constraint validator mark the value as invalid.
             * <p>
             * Methods of the {@code ConstraintViolationBuilder} instance this object
             * comes from and the constraint violation builder nested
             * objects throw {@code IllegalStateException} after this call.
             *
             * @return {@code ConstraintValidatorContext} instance the
             *         {@code ConstraintViolationBuilder} comes from
             */
            ConstraintValidatorContext addConstraintViolation();
        }

        /**
         * Represents a container element node whose context is known
         * (i.e. index, key and isInIterable)
         * and that is not necessarily a leaf node (i.e. subnodes can
         * be added).
         *
         * @since 2.0
         */
        interface ContainerElementNodeBuilderDefinedContext {

            /**
             * Adds a property node to the path the {@link ConstraintViolation}
             * will be associated to.
             * <p>
             * {@code name} describes a single property. In particular,
             * dot (.) is not allowed.
             *
             * @param name property name
             * @return a builder representing node {@code name}
             * @throws IllegalArgumentException if the name is null
             */
            NodeBuilderCustomizableContext addPropertyNode(String name);

            /**
             * Adds a bean node (class-level) to the path the {@link ConstraintViolation}
             * will be associated to.
             * Note that bean nodes are always leaf nodes.
             *
             * @return a builder representing the bean node
             */
            LeafNodeBuilderCustomizableContext addBeanNode();

            /**
             * Adds a container element node to the path the {@link ConstraintViolation}
             * will be associated to.
             *
             * @param name the node name
             * @param containerType the type of the container
             * @param typeArgumentIndex the index of the type argument
             * @return a builder representing the container element node
             * @throws IllegalArgumentException if the index is not valid
             */
            ContainerElementNodeBuilderCustomizableContext addContainerElementNode(
                    String name, Class<?> containerType, Integer typeArgumentIndex);

            /**
             * Adds the new {@link ConstraintViolation} to be generated if the
             * constraint validator marks the value as invalid.
             * <p>
             * Methods of the {@code ConstraintViolationBuilder} instance this object
             * comes from and the constraint violation builder nested
             * objects throw {@code IllegalStateException} after this call.
             *
             * @return {@code ConstraintValidatorContext} instance the
             *           {@code ConstraintViolationBuilder} comes from
             */
            ConstraintValidatorContext addConstraintViolation();
        }

        /**
         * Represents a container element node whose context is
         * configurable (i.e. index, key and isInIterable)
         * and that is not necessarily a leaf node (i.e. subnodes can
         * be added).
         *
         * @since 2.0
         */
        interface ContainerElementNodeBuilderCustomizableContext {

            /**
             * Marks the node as being in an iterable, e.g. array, {@code Iterable} or a
             * {@code Map}.
             *
             * @return a builder representing iterable details
             */
            ContainerElementNodeContextBuilder inIterable();

            /**
             * Adds a property node to the path the {@link ConstraintViolation}
             * will be associated to.
             *
             * {@code name} describes a single property. In particular,
             * dot (.) is not allowed.
             *
             * @param name property name
             * @return a builder representing node {@code name}
             * @throws IllegalArgumentException if the name is null
             */
            NodeBuilderCustomizableContext addPropertyNode(String name);

            /**
             * Adds a bean node (class-level) to the path the {@link ConstraintViolation}
             * will be associated to.
             * <p>
             * Note that bean nodes are always leaf nodes.
             *
             * @return a builder representing the bean node
             */
            LeafNodeBuilderCustomizableContext addBeanNode();

            /**
             * Adds a container element node to the path the {@link ConstraintViolation}
             * will be associated to.
             *
             * @param name the node name
             * @param containerType the type of the container
             * @param typeArgumentIndex the index of the type argument
             * @return a builder representing the container element node
             * @throws IllegalArgumentException if the index is not valid
             */
            ContainerElementNodeBuilderCustomizableContext addContainerElementNode(
                    String name, Class<?> containerType, Integer typeArgumentIndex);

            /**
             * Adds the new {@link ConstraintViolation} to be generated if the
             * constraint validator mark the value as invalid.
             * <p>
             * Methods of the {@code ConstraintViolationBuilder} instance this object
             * comes from and the constraint violation builder nested
             * objects throw {@code IllegalStateException} after this call.
             *
             * @return {@code ConstraintValidatorContext} instance the
             *         {@code ConstraintViolationBuilder} comes from
             */
            ConstraintValidatorContext addConstraintViolation();
        }

        /**
         * Represents refinement choices for a container element node.
         * <p>
         * If the container is an indexed collection or a map,
         * the index or the key should be set.
         * <p>
         * The node is not necessarily a leaf node (i.e. subnodes can
         * be added).
         *
         * @since 2.0
         */
        interface ContainerElementNodeContextBuilder {

            /**
             * Defines the key the object is into the {@code Map}.
             *
             * @param key map key
             * @return a builder representing the current node
             */
            ContainerElementNodeBuilderDefinedContext atKey(Object key);

            /**
             * Defines the index the object is into the {@code List} or array.
             *
             * @param index index
             * @return a builder representing the current node
             */
            ContainerElementNodeBuilderDefinedContext atIndex(Integer index);

            /**
             * Adds a property node to the path the {@link ConstraintViolation}
             * will be associated to.
             *
             * {@code name} describes a single property. In particular,
             * dot (.) is not allowed.
             *
             * @param name property name
             * @return a builder representing node {@code name}
             * @throws IllegalArgumentException if the name is null
             */
            NodeBuilderCustomizableContext addPropertyNode(String name);

            /**
             * Adds a bean node (class-level) to the path the {@link ConstraintViolation}
             * will be associated to.
             * <p>
             * Note that bean nodes are always leaf nodes.
             *
             * @return a builder representing the bean node
             */
            LeafNodeBuilderCustomizableContext addBeanNode();

            /**
             * Adds a container element node to the path the {@link ConstraintViolation}
             * will be associated to.
             *
             * @param name the node name
             * @param containerType the type of the container
             * @param typeArgumentIndex the index of the type argument
             * @return a builder representing the container element node
             * @throws IllegalArgumentException if the index is not valid
             */
            ContainerElementNodeBuilderCustomizableContext addContainerElementNode(
                    String name, Class<?> containerType, Integer typeArgumentIndex);

            /**
             * Adds the new {@link ConstraintViolation} to be generated if the
             * constraint validator mark the value as invalid.
             * <p>
             * Methods of the {@code ConstraintViolationBuilder} instance this object
             * comes from and the constraint violation builder nested
             * objects throw {@code IllegalStateException} after this call.
             *
             * @return {@code ConstraintValidatorContext} instance the
             *         {@code ConstraintViolationBuilder} comes from
             */
            ConstraintValidatorContext addConstraintViolation();
        }
    }
}

The ConstraintValidatorContext interface provides access to contextual information useful for the validation of specific constraints (e.g. getClockProvider(), see Implementation of temporal constraint validators ).

It also allows redefinition of the default constraint message generated when a constraint is not valid. By default, each invalid constraint leads to the generation of one error object represented by a ConstraintViolation object. This object is built from the default constraint message template as defined by the constraint declaration and the context in which the constraint declaration is placed (bean, property, executable parameter, cross-parameter,or executable return value or container element).

The ConstraintValidatorContext methods let the constraint implementation disable the default ConstraintViolation generation and create one or more custom ones. The non-interpolated message passed as a parameter is used to build the ConstraintViolation message (the message interpolation operation is applied to it).

By default, the Path exposed on the ConstraintViolation represents the path to the bean, property, parameter, cross-parameter,or return value or container element hosting the constraint (see ConstraintViolation for more information). You can point it to a subpath of this default path by using the constraint violation builder fluent API.

Example 3.18:19. Using the fluent API to build custom constraint violations
//assuming the following domain model
public class User {
    public Map<String,Address> getAddresses() { [...] }
}

public class Address {
    public String getStreet() { [...] }
    public Country getCountry() { [...] }
}

public class Country {
   public String getName() { [...] }
}

//From a property-level constraint on User.addresses
//Build a constraint violation on the default path - i.e. the "addresses" property
context.buildConstraintViolationWithTemplate( "this detail is wrong" )
            .addConstraintViolation();

//From a class level constraint on Address
//Build a constraint violation on the default path + "street"
//i.e. the street property of Address
context.buildConstraintViolationWithTemplate( "this detail is wrong" )
            .addPropertyNode( "street" )
            .addConstraintViolation();

//From a property-level constraint on  User.addresses
//Build a constraint violation on the default path + the bean stored
//under the "home" key in the map
context.buildConstraintViolationWithTemplate( "Incorrect home address" )
            .addBeanNode()
                .inContainer( Map.class, 1 )
                .inIterable().atKey( "home" )
            .addConstraintViolation();

//From a class level constraint on User
//Build a constraint violation on the default path + addresses["home"].country.name
//i.e. property "country.name" on the object stored under "home" in the map
context.buildConstraintViolationWithTemplate( "this detail is wrong" )
            .addPropertyNode( "addresses" )
            .addPropertyNode( "country" )
                .inContainer( Map.class, 1 )
                .inIterable().atKey( "home" )
            .addPropertyNode( "name" )
            .addConstraintViolation();

//From a class level constraint on User
//Build a constraint violation on the default path + addresses["home"].<map key>
//i.e. a container element constraint violation for the map key
context.buildConstraintViolationWithTemplate( "the map key is invalid" )
            .addPropertyNode( "addresses" )
            .addContainerElementNode( "<map key>", Map.class, 0 )
                .inIterable().atKey( "home" )
            .addConstraintViolation();

//To create a subnode representing a method parameter from a cross-parameter constraint violation

//Cross-parameter constraint on method createUser(String password, String passwordRepeat)
//Build a constraint violation on the default path + "passwordRepeat"
context.buildConstraintViolationWithTemplate("Passwords do not match")
            .addParameterNode( 1 )
            .addConstraintViolation();

//Cross-parameter constraint on a method
//mergeAddresses(Map<String,Address> addresses, Map<String,Address> otherAddresses)
//Build a constraint violation on the default path + "otherAddresses["home"]
//i.e. the Address bean hosted in the "home" key of the "otherAddresses" map parameter
context.buildConstraintViolationWithTemplate(
        "Map entry home present in both and does not match" )
            .addParameterNode( 1 )
            .addBeanNode()
                .inContainer( Map.class, 1 )
                .inIterable().atKey( "home" )
            .addConstraintViolation();

//Cross-parameter constraint on a method
//mergeAddresses(Map<String,Address> addresses, Map<String,Address> otherAddresses)
//Build a constraint violation on the default path + "otherAddresses["home"].city
//i.e. on the "city" property of the Address bean hosted in
//the "home" key of the "otherAddresses" map
context.buildConstraintViolationWithTemplate(
        "Map entry home present in both but city does not match" )
            .addParameterNode( 1 )
            .addPropertyNode( "city" )
                .inContainer( Map.class, 1 )
                .inIterable().atKey( "home" )
            .addConstraintViolation();

If disableDefaultConstraintViolation() is called, no custom error is added (using the error builder) and if the constraint is not valid, a ValidationException is raised.

3.4.1. Implementation of temporal constraint validators

Constraint validators for temporal constraints (either the built-in constraints @Past, @PastOrPresent, @Future and @FutureOrPresent or custom temporal constraints) can obtain the current instant from the ClockProvider object exposed by ConstraintValidatorContext#getClockProvider().

Listing 3.9: ClockProvider interface
/**
 * Contract for obtaining the {@link Clock} used as the reference for {@code now} when
 * validating the {@code @Future} and {@code @Past} constraints.
 * <p>
 * The default implementation will return the current system time. Plugging in custom
 * implementations may be useful for instance in batch applications which need to run with a
 * specific logical date, e.g. with yesterday's date when re-running a failed batch job
 * execution.
 * <p>
 * Implementations must be safe for access from several threads at the same time.
 *
 * @author Gunnar Morling
 * @author Guillaume Smet
 * @since 2.0
 */
public interface ClockProvider {

    /**
     * Returns the clock which serves as the reference for {@code now}.
     *
     * @return the clock which serves as the reference for {@code now}; must not be
     * {@code null}
     */
    Clock getClock();
}

The getClock() method returns a java.time.Clock object which represents the current instant, date and time using a time zone. A conforming Bean Validation implementation provides a default clock provider which returns a clock representing the current system time and default time zone. It is recommended that implementations call Clock#systemDefaultZone() to obtain the clock.

When bootstrapping a validator factory or validator, an alternative clock provider can be registered (see Bootstrapping). This can for instance be useful for testing, for applying the time zone of the currently logged in user in a multi-user, multi time zone application or for running batch applications with a logical date and time different from the actual current date and time.

3.4.2. Examples

Example 3.19:20. ConstraintValidator implementation
/**
 * Check that a String begins with one oftext is within  the given prefixes.authorized syntax 
 */
public class BeginsWithValidatorSyntaxValidator  implements ConstraintValidator<BeginsWithSyntax, String> {

    private Set<StringFormat > allowedPrefixesallowedFormats;

    /**
     * Configure the constraint validator based on the elements*  specified at the time it was
     * defined.
     *
     * @param constraint the constraint definition
     */
    @Override
    public void initialize(BeginsWithSyntax  constraint) {
        allowedPrefixesallowedFormats  = new HashSet ( Arrays.streamasList( constraint.value() )
                .collect( collectingAndThen( toSet(), Collections::unmodifiableSet ) );
    }

    /**
     * Validate a specified value.*  returns false if the specified value does not conform to
     * the definition.
     */
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if ( value == null )
            return true;

        return allowedPrefixesallowedFormats.streamsize()== 0 || (! Collections 
                .anyMatchdisjoint(guessFormat( value::startsWith), allowedFormats )  );
    }Set < Format > guessFormats( String text) { [...] 
}} 

This ConstraintValidator checks that a String begins with one oftext is within the accepted prefixessyntax. It also demonstrates an interesting best practice: return true on a null parameter.

The following listing shows a validator implementing the validation logic for a cross-parameter constraint.

Example 3.20:21. Cross-parameter validator implementation
/**
 * Check that two date parameters of a method are in the expected order. Expects the*  2nd and
 * 3rd parameter of the validated method to be of type java.util.Date.
 */
@SupportedValidationTarget(ValidationTarget.PARAMETERS)
public class DateParametersConsistentValidator implements
        ConstraintValidator<DateParametersConsistent, Object[]> {

    /**
     *Configure the constraint validator based on the elements * specified at the time it was defined. * @param constraint the constraint definition */ public void initialize(DateParametersConsistent constraint) { } /** *  Validate a specified value.*  returns false if the specified value does not conform to
     * the definition
     */
    @Override
    public boolean isValid(Object[] value, ConstraintValidatorContext context) {
        if ( value.length != 3 ) {
            throw new IllegalArgumentExceptionIllegalStateException ( "Unexpected method signature" );
        }
        // one or both limits are unbounded => always consistent
        if ( value[1] == null || value[2] == null ) {
            return true;
        }
        return ( (Date) value[1] ).before( (Date) value[2] );
    }
}

The following listing shows a validator implementing the validation logic for a constraint that is both generic and cross-parameter.

Example 3.21:22. Generic and cross-parameter validator implementation
/**
 * Checks that an object passes the Expression Language expression
 * provided by the constraint.
 */
@SupportedValidationTarget({ValidationTarget.ANNOTATED_ELEMENT, ValidationTarget.PARAMETERS})
public class ELScriptValidator implements ConstraintValidator<ELScript, Object> {

    public void initialize(ELScript constraint) {
        [...]
    }

    public boolean isValid(Object value, ConstraintValidatorContext context) {
        [...]
    }
}

The next example shows how to use ConstraintValidatorContext.

Example 3.22:23. Use of ConstraintValidatorContext
/**
 * Check that a String begins with "SN-" and has a specified length.text is within the authorized syntax 
 * <p>
 * Error messages are using either key:
 * <ul>
 *   <li>- com.acme.constraint.SerialNumberSyntax.wrongprefixunknown  if the string doesn't begin withno particular syntax is detected 
 *   "SN-"</li>
 *   <li>- com.acme.constraint.SerialNumberSyntax.wronglengthunauthorized  if the string doesn't have the
 *   specified length</li>
 * </ul>syntax is not allowed 
 */
public class SerialNumberValidatorFineGrainedSyntaxValidator  implements ConstraintValidator<SerialNumberSyntax, String> {

    private intSet  length< Format > allowedFormats;

    /**
     * Configure the constraint validator based on the elements*  specified at the time it was
     * defined.
     *
     * @param constraint the constraint definition
     */
    @Override
    public void initialize(SerialNumberSyntax  constraint) {allowedFormats = 
        thisnew HashSet ( Arrays .length =asList(  constraint.length();value() ) ); 
    }

    /**
     * Validate a specified value.*  returns false if the specified value does not conform to
     * the definition.
     */
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if ( value == null )
            return true;Set < Format > guessedFormats = guessFormats(value); 

        context.disableDefaultConstraintViolation();

        if ( !valueguessedFormats.startsWith(size() ==  0 "SN-" ) ) {
            String wrongPrefixunknown  = "{com.acme.constraint.SerialNumberSyntax.wrongprefixunknown}";
            context.buildConstraintViolationWithTemplate( wrongPrefixunknown )
                    .addConstraintViolation();
            return false;
        }
        if ( valueallowedFormats.lengthsize() != length0 && Collections .disjoint( guessedFormats, allowedFormats  ))  {
            String wrongLengthunauthorized  = "{com.acme.constraint.SerialNumberSyntax.wronglengthunauthorized}";
            context.buildConstraintViolationWithTemplate( wrongLengthunauthorized )
                    .addConstraintViolation();
            return false;
        }
        return true;
    }Set < Format > guessFormats( String text) { [...] 
}} 

The default error message is disabled and replaced by a specific error message depending on the type of constraint violation detected. In this case, only one error report is returned at a given time but a constraint validation implementation can return several error reports.

The following example shows how to obtain the current date and time via the ClockProvider when validating a temporal constraint such as @Past:

Example 3.23: Validation of a temporal constraint
/**
 * Validates that the given {@link ZonedDateTime} is in the past.
 */
public class PastValidatorForZonedDateTime implements ConstraintValidator<Past, ZonedDateTime> {

    @Override
    public boolean isValid(ZonedDateTime value, ConstraintValidatorContext context) {
        if ( value == null ) {
            return true;
        }

        ZonedDateTime now = ZonedDateTime.now( context.getClockProvider().getClock() );

        return value.isBefore( now );
    }
}

3.5. The ConstraintValidatorFactory

Constraint validation implementation instances are created by a ConstraintValidatorFactory.

The lifecycle of ConstraintValidator instances is fully dependent of the Bean Validation provider and piloted by the ConstraintValidatorFactory methods. Therefore, ConstraintValidatorFactory implementations (such as dependency injection frameworks) must consider these instances as belonging to a dependent scope. Bean Validation providers must release each instance retrieved. The ConstraintValidatorFactory instance that has created a ConstraintValidator instance must be the one that releases it. In other words, passing an instance of ConstraintValidator to a ConstraintValidatorFactory that has not created it is an error.

Note

ConstraintValidator instances created by the ValidatorFactory -level ConstraintValidatorFactory can be released when the ValidatorFactory is being closed.

Listing 3.10: Example 24. ConstraintValidatorFactory interface
/**
 * Instantiates a {@link ConstraintValidator} instance based off its class.
 * The {@code ConstraintValidatorFactory} is <b>not</b> responsible
 * for calling {@link ConstraintValidator#initialize(java.lang.annotation.Annotation)}.
 *
 * @author Dhanji R. Prasanna
 * @author Emmanuel Bernard
 * @author Hardy Ferentschik
 */
public interface ConstraintValidatorFactory {

    /**
     * @param key The class of the constraint validator to instantiate
     * @param <T> The type of the constraint validator to instantiate
     *
     * @return A new constraint validator instance of the specified class
     */
    <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> key);

    /**
     * Signals {@code ConstraintValidatorFactory} that the instance is no longer
     * being used by the Bean Validation provider.
     *
     * @param instance validator being released
     *
     * @since 1.1
     */
    void releaseInstance(ConstraintValidator<?, ?> instance);
}

The default ConstraintValidatorFactory provided by the Bean Validation provider implementation uses the public no-arg constructor of the ConstraintValidator class. A custom ConstraintValidatorFactory can be provided; for example it may benefit from dependency injection control in constraint implementations (see Bootstrapping considerations). Any constraint implementation relying on ConstraintValidatorFactory behaviors specific to an implementation (dependency injection, no no-arg constructor and so on) is not portable, hence great care should be given before walking that path. Make sure to configure the Bean Validation provider to honor any specific needs your ConstraintValidator has. As constraint designer and writer, make sure to document any specific non compliant requirements.

ConstraintValidatorFactory should not cache instances as the state of each instance can be altered in the initialize() method.

If an exception occurs in the factory while retrieving the ConstraintValidator instance, the runtime exception is wrapped in a ValidationException. If the instance returned by the factory is null, a ValidationException is raised.

4. Value extractor definition

Validation of container element constraints (see Container element constraints) as well as cascaded validation of generic container types (see Graph validation) requires access to the value(s) stored in the container. The retrieval of values stored in a container is handled via implementations of the ValueExtractor interface:

Listing 4.1: ValueExtractor interface
package javax.validation.valueextraction;

/**
 * Defines the logic used to extract the values from a container object of type {@code T}.
 * <p>
 * A value extractor for a generic type such as {@link Optional}, {@link List} or {@link Map}
 * is tied to one specific type parameter of {@code T}. The {@link ExtractedValue} annotation
 * is used to mark that type parameter. A value extractor for a non-generic type such as
 * {@link OptionalInt} needs to declare the type of the wrapped element(s) using
 * {@link ExtractedValue#type()}.
 * <p>
 * The extracted values are passed to the corresponding method of the {@link ValueReceiver}.
 * <p>
 * A typical value extractor implementation for {@code List} may look like this:
 *
 * <pre>
 * public class ListValueExtractor implements
 *         ValueExtractor&lt;List&lt;&#064;ExtractedValue ?&gt;&gt; {
 *
 *     &#064;Override
 *     public void extractValues(List&lt;?&gt; originalValue, ValueReceiver receiver) {
 *         for ( int i = 0; i &lt; originalValue.size(); i++ ) {
 *             receiver.indexedValue( "&lt;list element&gt;", i, originalValue.get( i ) );
 *         }
 *     }
 * }
 * </pre>
 *
 * @param <T> the container type handled by a specific implementation
 *
 * @author Gunnar Morling
 * @author Guillaume Smet
 * @see ExtractedValue
 * @see UnwrapByDefault
 * @since 2.0
 */
public interface ValueExtractor<T> {

    /**
     * Extracts the values to validate from the original object.
     *
     * @param originalValue the original value from which to extract the values, never
     * {@code null}
     * @param receiver the corresponding {@code ValueReceiver}
     */
    void extractValues(T originalValue, ValueReceiver receiver);

    /**
     * Provides a set of methods receiving value extracted by the {@link ValueExtractor}.
     * <p>
     * The value has to be passed to the method corresponding best to the type of the
     * original value.
     *
     * @since 2.0
     */
    interface ValueReceiver {

        /**
         * Receives the value extracted from an object.
         *
         * @param nodeName the name of the node representing the container element. If not
         * {@code null}, the name will be used when adding a container element node to the
         * {@link Path}
         * @param object the value to validate
         */
        void value(String nodeName, Object object);

        /**
         * Receives the value extracted from an iterable object that is not indexed (e.g.
         * a {@link Iterable}, {@link Set} or a {@link Map}).
         *
         * @param nodeName the name of the node representing the container element. If not
         * {@code null}, the name will be used when adding a container element node to the
         * {@link Path}
         * @param object the value to validate
         */
        void iterableValue(String nodeName, Object object);

        /**
         * Receives the value extracted from an indexed object (e.g. a {@link List}).
         *
         * @param nodeName the name of the node representing the container element. If not
         * {@code null}, the name will be used when adding a container element node to the
         * {@link Path}
         * @param i the index of the value in the original object
         * @param object the value to validate
         */
        void indexedValue(String nodeName, int i, Object object);

        /**
         * Receives the value extracted from a keyed object (e.g. a {@link Map}).
         *
         * @param nodeName the name of the node representing the container element. If not
         * {@code null}, the name will be used when adding a container element node to the
         * {@link Path}
         * @param key the key of the value in the original object
         * @param object the value to validate
         */
        void keyedValue(String nodeName, Object key, Object object);
    }
}

The validation engine passes the container instance and a value receiver object to the extractValues() method. The value extractor is only invoked if the container is not null. Value extractor implementations must invoke one of the ValueReceiver methods for each element contained in the container, passing the element value and, optionally, a node name. When calling

  • value(), the given value will be passed to the validation engine;

  • iterableValue(), the given value will be passed to the validation engine and the corresponding property path node (see ConstraintViolation) will be marked as iterable, i.e. Node#isInIterable() returns true;

  • indexedValue(), the given value will be passed to the validation engine and the corresponding property path node will be marked as iterable and it will have set the given index, i.e. Node#getIndex() returns the given index value;

  • keyedValue(), the given value will be passed to the validation engine and the corresponding property path node will be marked as iterable and it will have set the given key, i.e. Node#getKey() returns the given key value.

When passing a non-null node name to any of the receiver methods, this node name will be used when adding a node of kind CONTAINER_ELEMENT to the property path (see ConstraintViolation for the property path construction rules). If null is passed as node name, no node will be appended to the property path. The resulting property path will then be the same as if the constraint had been given on the container instead of a container element. That is desirable for single-element wrapper types such as Optional, OptionalInt etc.

If an exception occurs during invocation of the extractValues() method, this exception is wrapped into a ValidationException by the Bean Validation engine.

The container value passed to a value extractor is retrieved from the element that hosts the type argument carrying the constraint or @Valid annotation:

public class Orders {

    private Map<String, @Valid @RetailOrder Order> ordersByName;

    public Map<@NotNull String, Order> getOrdersByName() {
        return ordersByName;
    }

    [...]
}

When validating the @NotNull constraint, the map as returned by the getter will be passed to the map key extractor in order to obtain the map keys. When validating the @RetailOrder constraint and performing cascaded validation, the map as obtained directly from the field will be passed to the map value extractor in order to obtain the map values.

4.1. @ExtractedValue

The @ExtractedValue annotation is used to denote the element extracted by a given value extractor:

Listing 4.2: @ExtractedValue annotation
package javax.validation.valueextraction;

/**
 * Marks the type parameter of a generic container type to which a {@link ValueExtractor} is
 * tied or specifies the type of the wrapped element(s) of non-generic container types.
 * <p>
 * Must be given exactly once for a value extractor type.
 *
 * @author Gunnar Morling
 * @author Guillaume Smet
 *
 * @see ValueExtractor
 * @since 2.0
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
@Documented
public @interface ExtractedValue {

    /**
     * The type of the value extracted by the {@link ValueExtractor}. If not set, the type
     * will be automatically inferred from the type argument of the parameterized type.
     * <p>
     * Used to define value extractors for non-generic wrapper types e.g.
     * {@link OptionalInt}.
     * <p>
     * May not be used when {@code ExtractedValue} is defined on the type parameter of
     * a generic container type. A {@code ValueExtractorDefinitionException} will be thrown
     * in this case.
     *
     * @return the type of the value extracted by the value extractor
     */
    Class<?> type() default void.class;
}

The @ExtractedValue annotation must be specified exactly once for a value extractor type.

For generic container types (e.g. java.util.List), @ExtractedValue is to be specified on a type argument of the container type as used in the extractor definition. Only unbounded wildcard type arguments are supported as target for @ExtractedValue in this case.

Note

This implies that only one extractor is supported for a given generic type. I.e. there can be an extractor for List<?>, but not one for List<String> and one for List<Integer>.

For non-generic container types (e.g. java.util.OptionalInt), @ExtractedValue is to be specified on the container type as used in the extractor definition. The type of the wrapped element(s) must be specified using @ExtractedValue#type() in this case.

In case an illegal value extractor definition is detected, a ValueExtractorDefinitionException is raised.

4.2. @UnwrapByDefault

Value extractor definitions can be marked with the @UnwrapByDefault annotation. This causes constraints to be automatically applied to the wrapped value(s) if a constraint is found for an element of a type handled by that extractor (see Implicit unwrapping of containers):

Listing 4.3: @UnwrapByDefault annotation
package javax.validation.valueextraction;

/**
 * Marks a {@link ValueExtractor} definition so that it is applied automatically when
 * detecting constraints declared on the container type supported by the extractor, causing
 * the constraints to be applied to the container's elements instead of the container.
 * <p>
 * If needed, this behavior can be changed per constraint using {@link Unwrapping.Skip},
 * causing the constraints to be applied to the container itself:
 *
 * <pre>
 * &#064;SomeConstraint(payload = Unwrapping.Skip.class)
 * SomeContainerType container;
 * </pre>
 *
 * @author Guillaume Smet
 * @since 2.0
 */
@Target({ TYPE })
@Retention(RUNTIME)
@Documented
public @interface UnwrapByDefault {

}

4.3. Built-in value extractors

Compatible implementations provide value extractors for the following types out of the box:

  • java.util.Iterable; iterableValue() must be invoked for each contained element, passing the string literal <iterable element> as node name

  • java.util.List; indexedValue() must be invoked for each contained element, passing the string literal <list element> as node name

  • java.util.Map; both map keys and map values are to be supported; keyedValue() must be invoked by the map key extractor for each contained key, passing the string literal <map key> as node name; keyedValue() must be invoked by the map value extractor for each contained value, passing the string literal <map value> as node name

  • java.util.Optional; value() must be invoked, passing null as node name and passing the contained object as value or null if none is present

  • java.util.OptionalInt, java.util.OptionalLong and java.util.OptionalDouble; the extracted value types must be java.lang.Integer, java.lang.Long and java.lang.Double, respectively. value() must be invoked, passing null as node name and passing the contained number as value or null if none is present. The extractors must be marked with @UnwrapByDefault.

In environments where JavaFX is present, compatible implementations additionally provide extractors for the following types out of the box:

  • javafx.beans.observable.ObservableValue; value() must be invoked with the observable value, passing null as node name; the extractor must be marked with @UnwrapByDefault

  • javafx.beans.property.ReadOnlyListProperty and javafx.beans.property.ListProperty; indexedValue() must be invoked for each contained element, passing the string literal <list element> as node name

  • javafx.beans.property.ReadOnlySetProperty and javafx.beans.property.SetProperty; iterableValue() must be invoked for each contained element, passing the string literal <iterable element> as node name

  • javafx.beans.property.ReadOnlyMapProperty and javafx.beans.property.MapProperty; both map keys and map values are to be supported; keyedValue() must be invoked by the map key extractor for each contained key, passing the string literal <map key> as node name; keyedValue() must be invoked by the map value extractor for each contained value, passing the string literal <map value> as node name

Additional value extractors (amending or overriding the set of built-in extractors) can be registered when bootstrapping the validation engine (see Registering ValueExtractor implementations).

4.4. Examples

A value extractor for the elements of java.util.List:

class ListValueExtractor implements ValueExtractor<List<@ExtractedValue ?>> {

    @Override
    public void extractValues(List<?> originalValue, ValueReceiver receiver) {
        for ( int i = 0; i < originalValue.size(); i++ ) {
            receiver.indexedValue( "<list element>", i, originalValue.get( i ) );
        }
    }
}

This extractor passes each element contained in the given list to the receiver object, using the literal <list element> as a node name.

A value extractor for java.util.Optional:

public class OptionalValueExtractor implements ValueExtractor<Optional<@ExtractedValue ?>> {

    @Override
    public void extractValues(Optional<?> originalValue, ValueReceiver receiver) {
        receiver.value( null, originalValue.orElse( null ) );
    }
}

This extractor passes the element wrapped by the given Optional to the receiver object, if present. null is passed as a node name, causing no node to be appended to the resulting property path. I.e. when the @Size constraint in Optional<@Size(min=1) String getName() { …​ } is violated, the resulting property path will be the same as if a constraint hosted on the getName getter itself was violated.

A value extractor for java.util.OptionalInt:

@UnwrapByDefault
public class OptionalIntValueExtractor implements ValueExtractor<@ExtractedValue(type = Integer.class) OptionalInt> {

    @Override
    public void extractValues(OptionalInt originalValue, ValueReceiver receiver) {
        receiver.value( null, originalValue.isPresent() ? originalValue.getAsInt() : null );
    }
}

This extractor passes the int value wrapped by the given OptionalInt to the receiver object, if present. null is passed as a node name, causing no node to be appended to the resulting property path. As the extractor is marked with @UnwrapByDefault, any constraint declared on an element of type OptionalInt will implicitly be applied to the wrapped int value instead of the OptionalInt itself. As OptionalInt is a non-generic type (i.e. it has no type parameters), @ExtractedValue is given on the container type as used within the value extractor definition, specifying the type of the wrapped element via type().

The following extractor definition is illegal as it specifies @ExtractedValue more than once:

public class IllegalMapExtractor implements ValueExtractor<Map<@ExtractedValue ?, @ExtractedValue ?>> { ... }

The following extractor definition is unsupported as it specifies @ExtractedValue on a non-wildcard type argument:

public class StringListValueExtractor implements ValueExtractor<List<@ExtractedValue String>> { ... }

5. Constraint declaration and validation process

The Bean Validation specification defines a framework for declaring constraints on JavaBean classes, fields and properties. Constraints are declared on types and evaluated against instances or graphs of instances.

Bean Validation also offers a way to declare constructor and method constraints where parameters and return values are the constrained elements. We will discuss method constraints declaration in detail in Method and constructor constraints.

Furthermore, constraints can be applied to the elements of generic container types such as Map, List or Optional or of non-generic container types such as OptionalInt. Container element constraints are discussed in detail in Container element constraints.

5.14.1. Requirements on classes to be validated

Objects hosting constraints and expecting to be validated by Bean Validation providers must fulfill the following requirements:

  • Properties to be validated must follow the method signature conventions for JavaBeans read properties, as defined by the JavaBeans specification. These properties are commonly referred as getters.

  • Static fields and static methods are excluded from validation.

  • Constraints can be applied to interfaces and superclasses.

Note
What is a getter?

The JavaBeans specification specifies that a getter is a method whose

  • name starts with get and has a return type but no parameter

  • name starts with is, has no parameter and is returning boolean

The target of an annotation definition can be a

  • type

  • field or, property

  • , type, constructor or method return value

  • , constructor or method parameter

  • or constructor or method cross-parameter

  • container element

provided that:

  • the constraint definition supports the specified target (java.lang.annotation.Target)

  • one of the ConstraintValidators declared on the constraint supports the declared type of the target or in the case of cross-parameter, one cross-parameter ConstraintValidator is present (see ConstraintValidator resolution algorithm to learn about ConstraintValidator resolution)).

  • in the case of container element constraints, a corresponding value extractor exists (see ValueExtractor resolution for the details of value extractor resolution)

5.1.14.1.1. Object validation

Constraint declarations can be applied to a class or an interface. Applying a constraint to a class or interface expresses a validation over the state of the class or the class implementing the interface.

5.1.24.1.2. Field and property validation

Constraint declarations can be applied on both fields and properties for the same object type. The same constraint should however not be duplicated between a field and its associated property (the constraint validation would be applied twice). It is recommended for objects holding constraint declarations to adhere to a single state access strategy (either annotated fields or properties).

Note
Java Persistence and Bean Validation

For maximum portability, persistent properties hosting Bean Validation constraints should use the same access strategy used in Java Persistence. In other words, place your Bean Validation constraint annotations on the same element (field or getter) as your Java Persistence annotations.

When a field is annotated with a constraint declaration, field access strategy is used to access the state validated by such constraint.

When a property is annotated with a constraint declaration, property access strategy is used to access the state validated by such constraint.

When using field access strategy, the Bean Validation provider accesses the instance variable directly. When using the property access strategy, the Bean Validation provider accesses the state via the property accessor method. It is required that the class follows the method signature conventions for JavaBeans read properties (as defined by the JavaBeans Introspector class) for constrained properties when constrained properties are used. In this case, for every constraint property of type T, there is a getter method named get<Property-name>. The method must have no parameters. For boolean properties, is<Property-name> is an alternative name for the getter method. Specifically, if getX is the name of the getter method, where X is a string, the name of the persistent property is defined by the result of java.beans.Introspector.decapitalize(X).

The fields or methods visibility are not constrained.

5.1.34.1.3. Graph validation

In addition to supporting instance validation, validation of graphs of objects is also supported. The result of a graph validation is returned as a unified set of constraint violations. @Valid is used to express validation traversal of an association.

Listing 5.1:Example 25. @Valid annotation
/**
 * Marks a property, method parameter or method return type for validation cascading.
 * <p>/> 
 * Constraints defined on the object and its properties are be validated when the
 * property, method parameter or method return type is validated.
 * <p>/> 
 * This behavior is applied recursively.
 *
 * @author Emmanuel Bernard
 * @author Hardy Ferentschik
 */
@Target({ METHOD, FIELD, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Documented
public @interface Valid {
}

Consider the situation where bean X contains a field of type Y. By annotating field Y with the @Valid annotation, the Validator will validate Y (and its properties) when X is validated. The exact type Z of the value contained in the field declared of type Y (subclass, implementation) is determined at runtime. The constraint definitions of Z are used. This ensures proper polymorphic behavior for associations marked with @Valid.

Collection-valued, array-valued and generally Iterable fields and properties may also be decorated with the @Valid annotation. This causes the contents of the iterator to be validated. Any object implementing java.lang.Iterable is supported. This includes specifically:

  • arrays of objects

  • java.util.Collection

  • java.util.Set

  • java.util.List

  • java.util.Map (special treatment see below)

Each object provided by the iterator is validated. For Map, the value (retrieved by getValue) of each Map.Entry is validated (the key is not validated).

Like regular references, its type is determined at runtime and the constraint definitions for this particular type are used.

As of Bean Validation 2.0, @Valid can be applied to the elements of any generic container by putting it to the type argument(s) when using such container (e.g. MultiMap<String, @Valid Address> addressesByType), provided a value extractor implementation (see Value extractor definition) for that container type and the targeted type argument is present. There are built-in value extractors for the generic collection types listed above. In addition, there is a built-in extractor for the key objects of maps. See Built-in value extractors for the complete list of built-in value extractors.

@Valid also allows the validation of the elements of nested generic containers. @Valid must be put to a type argument of that nested container type in order to trigger validation of the elements of all the nested containers.

For a given container, the @Valid annotation should either be put to the container itself or to the type argument(s) of the container, but not both (in order to prevent the container elements from being validated twice).

The @Valid annotation is applied recursively. A conforming implementation avoids infinite loops according to the rules described in Object graph validation.

It is not supported to put @Valid to the type parameters of generic types or methods. It is also not supported to put @Valid to type arguments within the extends or implements clauses of type definitions. A future revision of this specification might define support for such usages of @Valid.

5.1.3.1. Examples
Example 5.1: Making elements of a list subject to graph validation
public class User {

    // preferred style as of Bean Validation 2.0
    private List<@Valid PhoneNumber> phoneNumbers;

    // traditional style; continues to be supported
    @Valid
    private List<PhoneNumber> phoneNumbers;

    // discouraged; either the container or the type argument(s) should be
    // annotated with @Valid, but not both
    @Valid
    private List<@Valid PhoneNumber> phoneNumbers;
}
Example 5.2: Making values of a map subject to graph validation
public class User {

    // preferred style as of Bean Validation 2.0
    private Map<AddressType, @Valid Address> addressesByType;

    // traditional style; continues to be supported
    @Valid
    private Map<AddressType, Address> addressesByType;

    // discouraged; either the map or the map value type argument should be
    // annotated with @Valid, but not both
    @Valid
    private Map<AddressType, @Valid Address> addressesByType;
}
Example 5.3: Making keys and values of a map subject to graph validation
public class User {

    private Map<@Valid AddressType, @Valid Address> addressesByType;
}
Example 5.4: Making elements of a nested list subject to graph validation
public class User {
    private Map<String, List<@Valid Address>> addressesByType;
}

In this example, all Address objects contained in the lists of the addressesByType map will be validated4.2. Two value extractors are invoked for this:

  • the extractor for Map values will be invoked to obtain all map values (lists of Address)

  • for each extracted list of addresses, the extractor for List elements will be invoked, providing the Address objects from each list in the map

Example 5.5: Making keys and values of a nested map subject to graph validation
public class User {
    private Map<String, Map<@Valid AddressType, @Valid Address>> addressesByUserAndType;
}

In this example, all AddressType objects and all Address objects contained in the maps of the addressesByUserAndType map will be validated. The following value extractors are invoked for this:

  • the extractor for Map values will be invoked to obtain all map values (maps of addresses by address type)

  • for each extracted map, the extractor for Map keys will be invoked, providing the AddressType objects from each of the nested maps

  • for each extracted map, the extractor for Map values will be invoked, providing the Address objects from each of the nested maps

5.2. Constraint declaration

Constraint declarations are placed on classes or interfaces primarily through annotations. A constraint annotation (see Constraint annotation), can be applied to a type, on any of the type’s fields or on any of the JavaBeans-compliant properties.

When a constraint is defined on a class, the class instance being validated is passed to the ConstraintValidator. When a constraint is defined on a field, the value of the field is passed to the ConstraintValidator. When a constraint is defined on a getter, the result of the getter invocation is passed to the ConstraintValidator.

Method and constructor constraints discusses in detail constraints on methods and constructors.

Constraints can also be applied to the elements of container types, e.g. to the elements of a List-typed property or to the value wrapped by an Optional object returned by a method. Container element constraints are discussed in Container element constraints.

5.34.3. Inheritance (interface and superclass)

A constraint declaration can be placed on an interface. For a given class, constraint declarations held on superclasses as well as interfaces are evaluated by the Bean Validation provider. Rules are formally described in Formal group definitions.

The effect of constraint declarations is cumulative. Constraints declared on a superclass getter will be validated along with any constraints defined on an overridden version of the getter according to the Java Language Specification visibility rules.

5.44.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. This subset is defined by the group or groups 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 5.6:26. Definition of groups
/**
 * Validation group verifing that a user is billable
 */
public interface Billable {}

/**
 * Customer can buy without any harrassing checking process
 */
public interface BuyInOneClick {
}

A constraint can belong to one or more groups.

Example 5.7:27. 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 groups is evaluated on the object graph. In Assign groups to constraints, @NotNull is checked on defaultCreditCard when either the Billable or BuyInOneClick group is validated. @NotNull on firstname and 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.

Listing 5.2: Default group
package javax.validation.groups;

/**
 * Default Bean Validation group.
 * <p>/> 
 * Unless a list of groups is explicitly defined:
 * <ul>
 *     <li>constraints belong to the {@code Default} group</li>
 *     <li>validation applies to the {@code Default} group</li>
 * </ul>
 * Most structural constraints should belong to the default group.
 *
 * @author Emmanuel Bernard
 */
public interface Default {
}

5.4.14.4.1. Group inheritance

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

Example 5.8:28. 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 (i.e. where the annotation element groups 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 5.9:29. 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 superinterfaces of BuyInOneClick.

5.4.24.4.2. Group sequence

By default, constraints are evaluated in no particular order regardless of which groups they belong to. It is however useful in some situations 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 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 @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 Validation routine ; if one of the groups processed in the sequence generates one or more constraint violations, the groups following in the sequence must not be processed. This ensures that a set of constraints is evaluated only if another set of constraints 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 definitions or group inheritance

If a group containing such a circularity is evaluated, a GroupDefinitionException is raised.

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.

Listing 5.3: @GroupSequence annotation
/**
 * Defines group sequence.
 * <p>/> 
 * The interface hosting {@code @GroupSequence} is representing
 * the group sequence.
 * When hosted on a class, represents the {@link Default} group
 * for that class.
 *
 * @author Emmanuel Bernard
 * @author Hardy Ferentschik
 */
@Target({ TYPE })
@Retention(RUNTIME)
@Documented
public @interface GroupSequence {

    Class<?>[] value();
}

Here is a usage example:

Example 5.10:30. Make use of group sequence
@ZipCodeCoherenceChecker(groups = Address.HighLevelCoherence.class)
public class Address {
    @NotNull @Size(max = 50)
    private String street1;

    @NotNull @ZipCode
    private String zipCode;

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

    /**
     * check coherence 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 checked if basic constraints fail
     */
    @GroupSequence({Default.class, HighLevelCoherence.class})
    public interface Complete {}
}

In 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 or more 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). See Validation routine for more information.

5.4.34.4.3. Redefining the Default group for a class

In Make use of group sequence, validating the Default group does not validate HighLevelCoherence constraints. To ensure a complete validation, a user must use the Complete group. 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 @GroupSequence annotation on the class; this sequence expresses the sequence of groups that does substitute Default for this class.

Example 5.11:31. 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;

    @NotNull @ZipCode
    private String zipCode;

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

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

In 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 (i.e. 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. If a @GroupSequence redefining the Default group for a class A does not contain the group A, a GroupDefinitionException is raised when the class is validated or when its metadata is requested.

5.4.44.4.4. Implicit grouping

It is possible to implicitly group several 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 the Default 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 5.12:32. 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.

5.4.54.4.5. Group conversion

When performing cascading validation, it is possible to use a different group than the one originally requested using the group conversion feature. Group conversions are declared by using the @ConvertGroup annotation.

Listing 5.4:Example 33. @ConvertGroup annotation
package javax.validation.groups;

/**
 * Converts group {@code from} to group {@code to} during cascading.
 * <p>
 * Can be used everywhere {@link Valid} is used and must be on an element
 * annotated with {@code Valid}.
 *
 * @author Emmanuel Bernard
 * @since 1.1
 */
@Target({TYPE,  METHOD, FIELD, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Repeatable(List.class)
@Documented
public @interface ConvertGroup {

    /**
     * The source group of this conversion.
     * @return the source group of this conversion
     */
    Class<?> from()();  default Default.class;

    /**
     * The target group of this conversion.
     * @return the target group of this conversion
     */
    Class<?> to();

    /**
     * Defines several {@link ConvertGroup} annotations
     * on the same element.
     */
    @Target({TYPE,  METHOD, FIELD, CONSTRUCTOR, PARAMETER, TYPE_USE })
    @Retention(RUNTIME)
    @Documented
    public @interface List {

        ConvertGroup[] value();
    }
}

@ConvertGroup and @ConvertGroup.List can be used everywhere @Valid can be used (associations, method/constructor parameters and return value). If these annotations are used without @Valid, a ConstraintDeclarationException is raised.

When an element is annotated with @Valid, validation is propagated. Groups are passed as is to the nested elements unless the @ConvertGroup annotation is used.

If the group expected to be passed to the nested element validation is defined as the from attribute of a @ConvertGroup annotation, the group used to effectively validate the nested element is the corresponding group defined in the to attribute.

If no value for the from attribute is specified, Default.class will be used as the source group of the conversion.

Rules are not executed recursively. If a rule is found matching, subsequent rules are no longer evaluated. In particular, if a set of @ConvertGroup declaration chains group A to B and B to C, the group A will be converted to B and not to C. This both makes rules clearer and let you switch two groups.

It is not legal to have more than one conversion rule containing the same from value. In this case, a ConstraintDeclarationException is raised.

Like regular constraint declarations, the from attribute cannot refer to a group sequence. A ConstraintDeclarationException is raised in this situation. The to attribute can. The group sequence will then be expanded before validating the associated object.

Note

When validation is done, group sequences are expanded before validating the object and its cascaded objects with the expected groups. Group conversion on an associated object happens on the already expanded groups.

The group referred to in @ConvertGroup.from works on expanded groups (i.e., after the group sequence has been expanded), not necessarily groups passed to the various validate methods.

The group referred to in @ConvertGroup.to will be expanded before validating the cascaded object just like a call to the various validate method would have done.

Note

Like most Bean Validation error cases, an illegal set of rules can be discovered statically (at compile time). For example, an annotation processor could detect such errors.

Note

Group circularity in a group conversion are not problematic because:

  • only one rule is applied for a given cascade (rules are not applied recursively)

  • validation cascading is stopped when the same instance / property is validated with the same group in a given path (existing rule)

@ConvertGroup and @ConvertGroup.List can only be placed where @Valid is present to ensure proper respect of the Liskov substitution principle: if rules were to be defined on an overriding method of a method marked as cascading validation, the rules could end up altering the list of constraints validated by the super type and thus violating the Liskov substitution principle.

Likewise, if a sub type overrides/implements a method originally defined in several parallel types of the hierarchy (e.g. two interfaces not extending each other, or a class and an interface not implemented by said class) and if that method’s return value has been marked for cascading validation in one of the parallel types, no group conversion rule may be declared for that method’s return value in the parallel types of the hierarchy. This again is to avoid an unexpected altering of the post conditions to be guaranteed to the caller.

If any of these rules is violated, a ConstraintDeclarationException is raised by default as defined in Method constraints in inheritance hierarchies.

Group conversion is quite useful to facilitate object graph reuse without spreading the validation group definitions across several layers. Let’s look at an example.

5.4.5.1. Group conversion examples

In this example we will reuse the Address group split and match it to the User group split.

Example 5.13:34. Example of group conversion
public interface Complete extends Default {}
public interface BasicPostal {}
public interface FullPostal extends BasicPostal {}

public class Address {
    @NotNull(groups=BasicPostal.class)
    String street1;

    String street2;

    @ZipCode(groups=BasicPostal.class)
    String zipCode;

    @CodeChecker(groups=FullPostal.class)
    String doorCode;
}

public class User {
    @Valid
    @ConvertGroup.List({ @ConvertGroup (from=Default.class, to=BasicPostal.class)), 
    @ConvertGroup(from=Complete.class, to=FullPostal.class)} ) 
    Set<Address> getAddresses() { [...] }
}

When validating an instance of User with the Default group, the associated addresses are validated with the BasicPostal group. When validating an instance of User with the Complete group, the associated addresses are validated with the FullPostal group.

Group conversions can also be applied during container element validation:

Example 5.14: Example of container element validation with group conversion
public class User {
    Set<
        @Valid
        @ConvertGroup(from=Default.class, to=BasicPostal.class)
        @ConvertGroup(from=Complete.class, to=FullPostal.class)
        Address
    > getAddresses() { [...] }
}

The following example shows an illegal declaration of a group conversion rule on a method’s return value:

Example 5.15:35. Example of an illegal group conversion
public interface BasicPostal {}

public class Order { [...] }

public interface RetailOrderService {

    @Valid
    Order placeOrder(String itemNo, int quantity);
}

public interface B2BOrderService {

    @Valid
    @ConvertGroup(from=Default.class, to=BasicPostal.class)
    Order placeOrder(String itemNo, int quantity);
}

public class OrderService implements RetailOrderService, B2BOrderService {

    @Override
    public Order placeOrder(String itemNo, int quantity) {
        [...]
    }
}

Here the class OrderService implements the two unrelated interfaces RetailOrderService and B2BOrderService, which both define a method placeOrder(), marking the return value as cascaded.

The group conversion declared in B2BOrderService is illegal as per the rules defined in the previous section, since the set of applied validation groups might be altered unexpectedly for a client of the RetailOrderService interface.

5.4.64.4.6. Formal group definitions

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

For every class X:

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

  1. The group X contains the following constraints: group X is a group used on sequences redefining the default group on a class (see Redefining the Default group for a class)

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

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

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

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

    1. every constraint in the group X

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

  1. 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 Redefining the Default group for a class)

    • the @GroupSequence annotation must declare the group X

  1. For every interface Z, the group Z contains the following constraints: this rule defines how non Default groups are defined

    1. 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 Implicit grouping)

    2. every constraint (which does not explicitly declare a group) declared by any superinterface not annotated @GroupSequence of the interface Z all Default constraints hosted on interfaces of Z: groups can be inherited (see Group inheritance)

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

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

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

  1. 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 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, a GroupDefinitionException is raised.

Note

A group G sequenced (directly or indirectly) to be executed before itself is not considered a circular reference.

5.54.5. Container element constraints

Constraints can be applied to the elements of generic containers, e.g. List, Map or Optional. This is done by putting constraint annotations to the type arguments of such containers.

Container element constraints can be used within the following declarations:

  • fields,

  • properties,

  • method or constructor parameters or

  • method return values.

Example 5.16: Container element constraints
private List<@Email String> emails;

public Optional<@Email String> getEmail() {
    [...]
}

public Map<@NotNull String, @ValidAddress Address> getAddressesByType() {
    [...]
}

public List<@NotBlank String> getMatchingRecords(List<@NotNull @Size(max=20) String> searchTerms) {
    [...]
}

When a field, property, executable parameter or method return value which is of a container type gets validated, then all values contained in the container will be validated provided that their container element type is constrained. Any container element constraints of that element will be validated alongside any other constraints hosted by that element. For container element constraints, the same rules for validation groups and group sequences apply as for any other constraint on the same element.

When a container element is constrained, the validation engine invokes a value extractor (see Value extractor definition) which retrieves the value(s) from the container so they can be validated. This may be a single value - e.g. in the case of Optional which wraps exactly one value if the Optional is not empty - or multiple values in the case of collection types.

Container element constraints can be applied to nested container types:

Example 5.17: Container element constraints on nested containers
private Map<String, @NotEmpty List<@ValidAddress Address>> addressesByType;

In such case multiple value extractors will be invoked.

In the example above,

  • the extractor for Map values will be invoked to obtain all map values (lists of Address)

  • for each extracted list of addresses, the @NotEmpty constraint will be validated and the extractor for List elements will be invoked, providing the Address objects from each list in the map

  • the @ValidAddress constraint will be applied to all elements of all lists stored in the map

It is not supported to declare container element constraints on the type parameters of generic types or methods. It is also not supported to declare container element constraints on type arguments within the extends or implements clauses of type definitions. I.e. the following usages are unsupported:

Example 5.18: Unsupported usage of container element constraints on generic types and methods
public class NonNullList<@NotNull T> {
    [...]
}

public class ContainerFactory {
    <@NotNull T> Container<T> instantiateContainer(T wrapped) { [...] }
}

public class NonNullSet<T> extends Set<@NotNull T> {
    [...]
}

A future revision of this specification might define support for such usages of container element constraints.

5.5.1. Implicit unwrapping of containers

Besides specifying container element constraints on type arguments, it is also possible to declare container element constraints on non-generic container types. This is done by means of implicit unwrapping, i.e. a constraint doesnt apply to the annotated container itself but to its element(s). Examples for types being subject to implicit unwrapping are java.util.OptionalInt, OptionalLong and OptionalDouble as well as JavaFXs non-generic property types such as StringProperty or IntegerProperty:

Example 5.19: Implicit unwrapping of container elements
@Min(1)
private OptionalInt optionalNumber;

@Negative
private LongProperty negativeLong;

@Positive
private IntegerProperty positiveInt;

private final ListProperty<@NotBlank StringProperty> notBlankStrings;

Here the @Min, @Negative, @Positive and @NotBlank constraints dont apply to the annotated OptionalInt, LongProperty, IntegerProperty and StringProperty objects themselves, but rather to the wrapped numeric and string values, respectively.

For this to work, an unambiguously resolvable value extractor (see ValueExtractor resolution algorithm for applying container-level constraints to container elements) must be defined which carries the @UnwrapByDefault annotation (see @UnwrapByDefault).

If needed, the target (container or container element) of a constraint declared on a container can be explicitly specified via the Unwrap and Skip payload definitions:

Listing 5.5: Payload types for unwrapping control
package javax.validation.valueextraction;

/**
 * Set of interfaces used in the {@code payload()} of a constraint to indicate if a value
 * should be unwrapped before validation.
 * <p>
 * This is used to overwrite the default configuration defined on the {@link ValueExtractor}.
 *
 * @author Guillaume Smet
 * @since 2.0
 */
public interface Unwrapping {

    /**
     * Unwrap the value before validation.
     *
     * @since 2.0
     */
    public interface Unwrap extends Payload {
    }

    /**
     * Skip the unwrapping if it has been enabled on the {@link ValueExtractor} by the
     * {@link UnwrapByDefault}
     * annotation.
     *
     * @since 2.0
     */
    public interface Skip extends Payload {
    }

}

This is useful for applying a constraint given on a non-generic container to

  • the container element(s) if there is no value extractor marked with @UnwrapByDefault (by using Unwrap)

  • the container itself in case there is a value extractor marked with @UnwrapByDefault (by using Skip)

For instance the @NotNull constraint is applied to the StringProperty container:

@NotNull(payload = Unwrapping.Skip.class)
private StringProperty name;

If both Unwrap and Skip are present in the definition of a payload, a ConstraintDeclarationException is raised.

Note

For the sake of readability, when applying constraints to the elements of a generic container, it is strongly recommended to put the constraints to the type argument instead of the container itself in conjunction with Unwrapping.Unwrap. I.e. you should prefer

List<@Email String> emails;

over

@Email(payload = Unwrapping.Unwrap.class)
List<String> emails;

5.6. Method and constructor constraints

Note

In the following, the term "method constraint" refers to constraints declared on methods as well as constructors.

Method constraints are declared by adding constraint annotations directly to methods or constructors and/or their parameters. In the former case, all the parameters of an executable (cross-parameter constraint) or the return value is constrained, in the latter individual parameters are constrained. As with bean constraints, this can be done using either actual Java annotations or using an XML constraint mapping file (see Method-level overriding). Bean Validation providers are free to provide additional means of defining method constraints such as an API-based approach.

Getters are not considered constrained methods by default (see Method and constructor validation).

5.6.14.5.1. Requirements on methods to be validated

Static methods are ignored by validation. Putting constraints on a static method is not portable. No other restrictions exist from the perspective of this specification, however it is possible that technologies integrating with method validation impose further restrictions to methods for which a validation shall be applied. For instance certain integration technologies might require that methods to be validated must have public visibility and/or must not be final.

5.6.24.5.2. Declaring parameter constraints

Parameter constraints are declared by putting constraint annotations on method or constructor parameters.

Example 5.20:36. Declaring parameter constraints
public class OrderService {

    public OrderService(@NotNull CreditCardProcessor creditCardProcessor) {
        [...]
    }

    public void placeOrder(
        @NotNull @Size(min=3, max=20) String customerCode,
        @NotNull Item item,
        @Min(1) int quantity) {
        [...]
    }
}

Using constraint annotations, several preconditions are defined here. These preconditions which must be satisfied in order to legally invoke the methods of OrderService are:

  • The CreditCardProcessor passed to the constructor must not be null.

  • The customer code passed to the placeOrder() method must not be null and must be between 3 and 20 characters long.

  • The Item passed to the placeOrder() method must not be null.

  • The quantity value passed to the placeOrder() method must be 1 at least.

Note that declaring these constraints does not automatically cause their validation when the concerned methods are invoked. It’s the responsibility of an integration layer to trigger the validation of the constraints using a method interceptor, dynamic proxy or similar. See section Triggering method validation for more details.

Tip

In order to use constraint annotations for method or constructor parameters, their element type must be ElementType.PARAMETER. In order to use constraint annotations for cross-parameter validation or on the return values of methods or constructors (see the following sections), their element type must be ElementType.METHOD respectively ElementType.CONSTRUCTOR. All built-in constraints support these element types and it is considered a best practice to do the same for custom constraints.

5.6.2.1. Cross-parameter constraints

Cross-parameter constraints allow to express constraints based on the value of several method parameters, similar to class-level constraints which are based on several properties of a given class. Cross-parameter constraints are declared by putting cross-parameter constraint annotations on methods or constructors as shown in the following example.

Example 5.21:37. Declaring cross-parameter constraints
public class CalendarService {

    @ConsistentDateParameters
    public void createEvent(
        String title,
        @NotNull Date startDate,
        @NotNull Date endDate) {
        [...]
    }
}

The cross-parameter constraint annotation expresses here that the given start date must be before the passed end date in order to legally invoke the createEvent() method. The example also shows that it is often useful to combine constraints directly placed on individual parameters (e.g. @NotNull) and cross-parameter constraints.

Tip

Cross-parameter constraints as well as return value constraints are declared directly on a method or a constructor. To make it obvious for a reader that an annotation refers to the parameters of a method or constructor and not its return value, it is recommended to choose a name which clearly expresses this intention.

It is not legal to declare a cross-parameter constraint on a method or constructor which has no parameters. A ConstraintDeclarationException is raised in this case.

Some constraints can target an executable’s return value as well as its array of parameters. They are known to be both generic and cross-parameter constraints. When using such a constraint on an executable to target the parameters, one must set validationAppliesTo if there is an ambiguity. The set of ambiguities is described in validationAppliesTo. Even without ambiguity, it is recommended to explicitly set validationAppliesTo to ConstraintTarget.PARAMETERS as it improves readability.

5.6.2.2. Naming parameters

In case the validation of a parameter constraint fails, the concerned parameter needs to be identified in the resulting ConstraintViolation (see section ConstraintViolation).As of version 7, Java doesnt provide a portable way to retrieve parameter names. Bean Validationtherefore defines the javax.validation.ParameterNameProvider API to which the retrieval of parameter names is delegated:

Listing 5.6: ParameterNameProvider interface
/**
 * Provides names for method and constructor parameters.
 * <p>/> 
 * Used by the Bean Validation runtime when creating constraint violation
 * objects for violated method constraints.
 * <p>/> 
 * Implementations must be thread-safe.
 *
 * @author Gunnar Morling
 * @since 1.1
 */
public interface ParameterNameProvider {

    /**
     * Returns the names of the parameters of the given constructor.
     *
     * @param constructor the constructor for which the parameter names shall be
     *        retrieved; never {@code null}
     * @return a list containing the names of the parameters of the given
     *         constructor; may be empty but never {@code null}
     */
    List<String> getParameterNames(Constructor<?> constructor);

    /**
     * Returns the names of the parameters of the given method.
     *
     * @param method the method for which the parameter names shall be retrieved;
     *        never {@code null}
     * @return a list containing the names of the parameters of the given method;
     *         may be empty but never {@code null}
     */
    List<String> getParameterNames(Method method);
}

A conforming Bean Validation implementation provides a default ParameterNameProvider implementation which returns parameter names as stored in the class file containing the validated executable, if present. A conforming implementation must either use the Java reflection API or ensure behavioral compatibility to using the reflection API in the following way:

  • Obtain the methods or constructors parameters via java.lang.reflect.Executable.getParameters()

  • Obtain each parameters name via java.lang.reflect.Parameter.getName()

Depending on whether the class file of the validated executable contains parameter name information or not, the actual parameter names as provided in the executables definition will be returned or synthetic names in the form argPARAMETER_INDEX, where PARAMETER_INDEX starts at 0 for the first parameter, e.g. arg0, arg1 etc.

Bean Validation providers and integrators are free to provide additional implementations (e.g. based on annotations specifying parameter names, debug symbols etc.). If a user wishes to use another parameter name provider than the default implementation, they may specify the provider to use with help of the bootstrap API (see Bootstrapping) or the XML configuration (see XML configuration: META-INF/validation.xml).

If an exception occurs during invocation of the getParameterNames() methods, this exception is wrapped into a ValidationException by the Bean Validation engine.

5.6.34.5.3. Declaring return value constraints

Return value constraints are declared by putting constraint annotations directly on a method or constructor.

Some constraints can target both the return value and the array of parameters of an executable. They are known to be both generic and cross-parameter constraints. When using such constraint on an executable to target the return value, one must set validationAppliesTo in case there is an ambiguity. The set of ambiguities is described in validationAppliesTo. Even without ambiguity, it is recommended to explicitly set validationAppliesTo to ConstraintTarget.RETURN_VALUE as it improves readability.

Example 5.22:38. Declaring return value constraints
public class OrderService {

    private CreditCardProcessor creditCardProcessor;

    @ValidOnlineOrderService
    public OrderService(OnlineCreditCardProcessor creditCardProcessor) {
        this.creditCardProcessor = creditCardProcessor;
    }

    @ValidBatchOrderService
    public OrderService(BatchCreditCardProcessor creditCardProcessor) {
        this.creditCardProcessor = creditCardProcessor;
    }

    @NotNull
    @Size(min=1)
    public Set<CreditCardProcessor> getCreditCardProcessors() { [...] }

    @NotNull
    @Future
    public Date getNextAvailableDeliveryDate() { [...] }
}

Here the following postconditions are defined which are guaranteed to the caller of the methods and constructors of the OrderService class:

  • The newly created OrderService object returned by the first constructor satisfies the conditions of the custom @ValidOnlineOrderService constraint.

  • The newly created OrderService object returned by the second constructor satisfies the conditions of the custom @ValidBatchOrderService constraint.

  • The set of CreditCardProcessor objects returned by getCreditCardProcessors() will neither be null nor be empty.

  • The Date object returned by getNextAvailableDeliveryDate() will not be null and will be in the future.

Like parameter constraints, these return value constraints are not per-se validated upon method invocation, but instead an integration layer invoking the validation is required.

5.6.44.5.4. Marking parameters and return values for cascaded validation

The @Valid annotation is used to declare that a cascaded validation of the given method/constructor parameters or return values is performed by the Bean Validation provider. When marked, the parameter or return value is considered a bean object to validate. The same rules as for standard object graph validation (see Object graph validation) apply, in particular

  • null arguments and null return values are ignored

  • The validation is recursive; that is, if validated parameter or return value objects have references marked with @Valid themselves, these references will also be validated

  • Bean Validation providers must guarantee the prevention of infinite loops during cascaded validation

Example 5.23:39. Marking parameters and return values for cascaded validation
public class OrderService {

    @NotNull @Valid
    private CreditCardProcessor creditCardProcessor;

    @Valid
    public OrderService(@NotNull @Valid CreditCardProcessor creditCardProcessor) {
        this.creditCardProcessor = creditCardProcessor;
    }

    @NotNull @Valid
    public Order getOrderByPk(@NotNull @Valid OrderPK orderPk) { [...] }

    @NotNull
    @Valid public Set<@Valid Order> getOrdersByCustomer(@NotNull @Valid CustomerPK customerPk) { [...] }
}

Here the following recursive validations will happen when validating the methods of the OrderService class:

  • Validation of the constraints on the object passed for the creditCardProcessor parameter of the constructor

  • Validation of the constraints on the newly created OrderService instance returned by the constructor, i.e. the @NotNull constraint on the field creditCardProcessor and the constraints on the referenced CreditCardProcessor instance (as the field is annotated with @Valid).

  • Validation of the constraints on the object passed for the orderPk parameter and the returned Order object of the getOrderByPk() method

  • Validation of the constraints on the object passed for the customerPk parameter and the constraints on each object contained within the returned Set<Order> of the getOrdersByCustomer() method

Again, solely marking parameters and return values for cascaded validation does not trigger the actual validation.

5.6.54.5.5. Method constraints in inheritance hierarchies

When defining method constraints within inheritance hierarchies (that is, class inheritance by extending base classes and interface inheritance by implementing or extending interfaces) one has to obey the Liskov substitution principle which mandates that:

  • a method’s preconditions (as represented by parameter constraints) must not be strengthened in sub types

  • a method’s postconditions (as represented by return value constraints) must not be weakened in sub types

Tip

Very informally speaking, the Liskov substitution principle says that where a given type T is used, it should be possible to replace T with a sub-type S of T ("Behavioral subtyping"). If S overrides/implements a method from T and S would strengthen the method’s preconditions (e.g. by adding parameter constraints) this principle would be violated as client code working correctly against T might fail when working against S. Also if S overrides/implements a method from T and S weakens the method’s postconditions this principle would be violated. However S may strengthen the method’s postconditions (by adding return value constraints), as client code working against T still will work against S.

Therefore the following rules with respect to the definition of method level constraints in inheritance hierarchies apply:

  • In sub types (be it sub classes/interfaces or interface implementations), no parameter constraints may be declared on overridden or implemented methods, nor may parameters be marked for cascaded validation. This would pose a strengthening of preconditions to be fulfilled by the caller.

  • If a sub type overrides/implements a method originally defined in several parallel types of the hierarchy (e.g. two interfaces not extending each other, or a class and an interface not implemented by said class), no parameter constraints may be declared for that method at all nor parameters be marked for cascaded validation. This again is to avoid an unexpected strengthening of preconditions to be fulfilled by the caller.

  • In sub types (be it sub classes/interfaces or interface implementations), return value constraints may be declared on overridden or implemented methods and the return value may be marked for cascaded validation. Upon validation, all return value constraints of the method in question are validated, wherever they are declared in the hierarchy. This only poses possibly a strengthening but no weakening of the method’s postconditions guaranteed to the caller.

  • One must not mark a method return value for cascaded validation more than once in a line of a class hierarchy. In other words, overriding methods on sub types (be it sub classes/interfaces or interface implementations) cannot mark the return value for cascaded validation if the return value has already been marked on the overridden method of the super type or interface.

Out of the box, a conforming Bean Validation provider must throw a ConstraintDeclarationException when discovering that any of these rules are violated. In addition providers may implement alternative, potentially more liberal, approaches for handling constrained methods in inheritance hierarchies. Possible means for activating such alternative behavior include provider-specific configuration properties or annotations. Note that client code relying on such alternative behavior is not portable between Bean Validation providers.

The above rules do not apply when validating constructor constraints as constructors do not override one another. Parameter and return value constraints can be applied to any constructor in the type hierarchy, but only the constraints defined directly on the validated constructor are evaluated.

5.6.5.1. Examples

This sections provides some examples of illegal constraint definitions which violate the rules stated above in one way or another.

Example 5.24:40. Illegally declared parameter constraints on interface implementation
public interface OrderService {

    void placeOrder(String customerCode, Item item, int quantity);
}

public class SimpleOrderService implements OrderService {

    @Override
    public void placeOrder(
        @NotNull @Size(min=3, max=20) String customerCode,
        @NotNull Item item,
        @Min(1) int quantity) {
        [...]
    }
}

The constraints in SimpleOrderService are illegal, as they strengthen the preconditions of the placeOrder() method as constituted by the interface OrderService.

Example 5.25:41. Illegally declared parameter constraints on sub class
public class OrderService {

    void placeOrder(String customerCode, Item item, int quantity) { [...] }
}

public class SimpleOrderService extends OrderService {

    @Override
    public void placeOrder(
        @NotNull @Size(min=3, max=20) String customerCode,
        @NotNull Item item,
        @Min(1) int quantity) {
        [...]
    }
}

The constraints in SimpleOrderService are illegal, as they strengthen the preconditions of the placeOrder() method as constituted by the super class OrderService.

Example 5.26:42. Illegally declared parameter constraints on parallel types
public interface OrderService {

    void placeOrder(String customerCode, Item item, int quantity);
}

public interface OrderPlacementService {

    public void placeOrder(
        @NotNull @Size(min=3, max=20) String customerCode,
        @NotNull Item item,
        @Min(1) int quantity);
}

public class SimpleOrderService implements OrderService, OrderPlacementService {

    @Override
    public void placeOrder(String customerCode, Item item, int quantity) {
        [...]
    }
}

Here the class SimpleOrderService implements the interfaces OrderService and OrderPlacementService, which themselves are unrelated to each other but both define a method placeOrder() with an identical signature. This hierarchy is illegal with respect to the parameter constraints as a client of SimpleOrderService would have to fulfill the constraints defined on the interface OrderPlacementService even if the client only expects OrderService.

Example 5.27:43. Correctly declared return value constraints on sub class
public class OrderService {

    Order placeOrder(String customerCode, Item item, int quantity) {
        [...]
    }
}

public class SimpleOrderService extends OrderService {

    @Override
    @NotNull
    @Valid
    public Order placeOrder(String customerCode, Item item, int quantity) {
        [...]
    }
}

The return value constraints in DefaultOrderService are legal, as they strengthen the postconditions of the placeOrder() method as constituted by the super class OrderService but don’t weaken them.

5.74.6. Validation routine

For a given group, the validation routine applied on a given bean instance is expected to execute the following constraint validations in no particular order:

  • for all reachable fields, execute all field level validations (including the ones expressed on superclasses) matching the targeted group unless the given validation constraint has already been processed during this validation routine for a given navigation path (see Object graph validation) as part of a previous group match.

  • for all reachable getters, execute all getter level validations (including the ones expressed on interfaces and superclasses) matching the targeted group unless the given validation constraint has already been processed during this validation routine for a given navigation path (see Object graph validation) as part of a previous group match.

  • execute all class level validations (including the ones expressed on interfaces and superclasses) matching the targeted group unless the given validation constraint has already been processed during this validation routine for a given navigation path (see Object graph validation) as part of a previous group match.

  • for all reachable and cascadable associations, execute all cascading validations (see Object graph validation) including the ones expressed on interfaces and superclasses (see Formal group definitions). Note that group conversion can apply (see Group conversion).

Reachable fields, getters and associations as well as cascadable associations are defined in Traversable property.

Note that this implies that a given validation constraint will not be processed more than once per validation per path. Some implementations might even process a single constraint only once across paths provided that they return the expected set of ConstraintViolation.

Unless ordered by group sequences, groups can be validated in no particular order. This implies that the validation routine can be run for several groups in the same pass.

The object validation routine is described as such. For each constraint declaration:

  • determine for the constraint declaration, the appropriate ConstraintValidator to use (see ConstraintValidator resolution algorithm).

  • execute the isValid operation (from the constraint validation implementation) on the appropriate data (see Constraint validation implementation)

  • if isValid() returns true, continue to the next constraint,

  • if isValid() returns false, the Bean Validation provider populates ConstraintViolation object(s) according to the rules defined in Constraint validation implementation and appends these objects to the list of constraint violations.

5.7.14.6.1. Object graph validation

The @Valid annotation on a given association (i.e. object reference or collection, array, Iterable of objects), dictates the Bean Validator implementation to apply recursively the Bean Validation routine on (each of) the associated object(s). This mechanism is recursive: an associated object can itself contain cascaded references.

Null references are ignored.

To prevent infinite loops, the Bean Validation implementation must ignore the cascading operation if the associated object instance has already been validated in the current navigation path (starting from the root object). See Object graph limits for an example. A navigation path is defined as a set of @Valid associations starting from the root object instance and reaching the associated instance. A given navigation path cannot contain the same instance multiple times (the complete validated object graph can though). See Object graph limits for an example.

Note

This object graph navigation can lead to multiple validations of the same constraint and the same object instance but the set of constraint validation is deterministic and the algorithm prevents infinite loops.

Example 5.28:44. Object graph limits
#assuming the following object graph

Order -(lines)→ Orderline1
Order -(lines)→ Orderline2
Orderline1 -(order)→ Order
Orderline2 -(order)→ Order
Order -(customer)→ User
Order -(shippingAddress)→ Address1
Order -(billingAddress)→ Address2
Address1 -(inhabitant)→ User
Address2 -(inhabitant)→ User
User -(addresses)→ Address1
User -(addresses)→ Address2

#validation branches are as followed
Order -(lines)→ Orderline1
  - order is ignored: Order is already present in the branch

Order -(lines)→ Orderline2
  - order is ignored: Order is already present in the branch

Order -(customer)→ User -(addresses)→ Address1
  - inhabitant is ignored: User is already present in the branch

Order -(customer)→ User -(addresses)→ Address2
  - inhabitant is ignored: User is already present in the branch

Order -(shippingAddress)→ Address1 -(inhabitant)→ User
  - addresses to Address1 is ignored: Address1 is already present in the branch

Order -(shippingAddress)→ Address1 -(inhabitant)→ User -(addresses)→ Address2
  - inhabitant is ignored: User is already present in the branch

Order -(billingAddress)→ Address2 -(inhabitant)→ User
  - addresses to Address2 is ignored: Address2 is already present in the branch

Order -(billingAddress)→ Address2 -(inhabitant)→ User -(addresses)→ Address1
  - inhabitant is ignored: User is already present in the branch

The ConstraintViolation objects are built when a failing constraint on an associated object is found. They reflect the path to reach the object from the root validated object (See ConstraintViolation).

@Valid is an orthogonal concept to the notion of group. If two groups are in sequence, the first group must pass for all associated objects before the second group is evaluated. Note however that the Default group sequence overriding is local to the class it is defined on and is not propagated to the associated objects. The following example illustrates this:

Example 5.29:45. Class Driver with redefined default group
@GroupSequence({ Minimal.class, Driver.class })
public class Driver {
  @Min(value = 18, groups = Minimal.class)
  int age;

  @AssertTrue
  Boolean passedDrivingTest;

  @Valid
  Car car;

  // setter/getters
}
Example 5.30:46. Class Car with redefined default group
@GroupSequence({ Car.class, Later.class })
public class Car {
  @NotNull
  String type;

  @AssertTrue(groups = Later.class)
  Boolean roadWorthy;

  // setter/getters
}
Example 5.31:47. Defining a group sequence
@GroupSequence({ Minimal.class, Later.class })
public interface SequencedGroups {
}
Example 5.32:48. Group sequence overriding is not propagated to associated objects
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();

Driver driver = new Driver();
driver.setAge(16);
Car porsche = new Car();
driver.setCar(porsche);


Set<ConstraintViolation<Driver>> violations = validator.validate( driver );

assert violations.size() == 2;

violations = validator.validate( driver, SequencedGroups.class );

assert violations.size() == 1;

The default group sequence is redefined for the Driver as well as Car. When the default group is requested via validator.validate( driver ) the group Minimal gets validated in class Driver. The constraint will fail since the driver’s age in the example is only 16. The constraint on passedDrivingTest will not be evaluated due to the redefined default sequence of Driver. However, there is one more constraint violation, namely the @NotNull on Car.type. The reason for this is that the group Default gets propagated to Car (not Minimal). Class Driver defines its own group sequence which means that only @NotNull on type gets evaluated.

In the second call to validate the group SequencedGroups is requested which defines a sequence of Minimal followed by Later. In this case there is only one constraint violation. Again @Min on age fails, but in this case the group Minimal gets propagated to Car which does not have any constraints defined against this group. Constraints belonging to the group Later won’t get validated until all constraints belonging to Minimal pass.

5.7.24.6.2. Method and constructor validation

For a given group, the validation routine applied to validate parameters of a method or constructor is expected to execute the following constraint validations in no particular order:

  • execute all parameter validations (in case of overriding method validation, including the ones expressed on overridden methods of the interfaces and superclasses) matching the targeted group unless the given validation constraint has already been processed during this validation routine for a given navigation path (see Object graph validation) as part of a previous group match.

  • execute all cross parameter validations (in case of overriding method validation, including the ones expressed on overridden methods of the interfaces and superclasses) matching the targeted group unless the given validation constraint has already been processed during this validation routine for a given navigation path (see Object graph validation) as part of a previous group match.

  • for all parameters marked for cascaded validation, execute all cascading validations (see Object graph validation), in case of overriding method validation including the ones expressed on overridden methods of the interfaces and superclasses (see Formal group definitions). Note that group conversion can apply (see Group conversion).

For a given group, the validation routine applied to validate the return value of a method or constructor is expected to execute the following constraint validations in no particular order:

  • execute all return value validations (including the ones expressed on interfaces and superclasses) matching the targeted group unless the given validation constraint has already been processed during this validation routine for a given navigation path (see Object graph validation) as part of a previous group match.

  • if the return value is marked for cascaded validation, execute all cascading validations (see Object graph validation) including the ones expressed on interfaces and superclasses (see Formal group definitions). Note that group conversion can apply (see Group conversion).

Note that this implies that a given validation constraint will not be processed more than once per validation per path. Some implementations might even process a single constraint only once across paths provided that they return the expected set of ConstraintViolation.

Unless ordered by group sequences, groups can be validated in no particular order. This implies that the validation routine can be run for several groups in the same pass.

The object validation routine is as defined in described in Validation routine.

5.7.34.6.3. Traversable property

In some cases, the state of some properties should not be accessed. For example, if a property loaded by a Java Persistence provider is a lazy property or a lazy association, accessing its state would trigger a load from the database. An undesired behavior.

Bean Validation offers a way to control which property can and cannot be accessed via the TraversableResolver.isReachable() contract.

Likewise, it is sometimes undesirable to cascade validation despite the use of @Valid. Java Persistence 2 for example does not cascade to associated entities during flush. You can control this behavior by implementing TraversableResolver.isCascadable().

Listing 5.7: TraversableResolver interface
/**
 * Contract determining if a property can be accessed by the Bean Validation provider.
 * This contract is called for each property that is being either validated or cascaded.
 * <p>/> 
 * A traversable resolver implementation must be thread-safe.
 *
 * @author Emmanuel Bernard
 */
public interface TraversableResolver {
    /**
     * Determines if the Bean Validation provider is allowed to reach the property state.
     *
     * @param traversableObject object hosting {@code traversableProperty}
     *        or {@code null} if {@code validateValue} is called
     * @param traversableProperty the traversable property
     * @param rootBeanType type of the root object passed to the Validator
     *        or hosting the method or constructor validated
     * @param pathToTraversableObject path from the root object to
     *        {@code traversableObject}
     *        (using the path specification defined by Bean Validator)
     * @param elementType either {@code FIELD} or {@code METHOD}
     * @return {@code true} if the Bean Validation provider is allowed to
     *         reach the property state, {@code false} otherwise
     */
    boolean isReachable(Object traversableObject,
                        Node traversableProperty,
                        Class<?> rootBeanType,
                        Path pathToTraversableObject,
                        ElementType elementType);

    /**
     * Determines if the Bean Validation provider is allowed to cascade validation on
     * the bean instance returned by the property value
     * marked as {@code @Valid}.
     * <p>/> 
     * Note that this method is called only if
     * {@link #isReachable(Object, javax.validation.Path.Node, Class, Path, java.lang.annotation.ElementType)}
     * returns {@code true} for the same set of arguments and if the property
     * is marked as {@link Valid}.
     *
     * @param traversableObject object hosting {@code traversableProperty}
     *        or {@code null} if {@code validateValue} is called
     * @param traversableProperty the traversable property
     * @param rootBeanType type of the root object passed to the Validator
     *        or hosting the method or constructor validated
     * @param pathToTraversableObject path from the root object to
     *        {@code traversableObject}
     *        (using the path specification defined by Bean Validator)
     * @param elementType either {@code FIELD} or {@code METHOD}
     * @return {@code true} if the Bean Validation provider is allowed to
     *         cascade validation, {@code false} otherwise
     */
    boolean isCascadable(Object traversableObject,
                         Node traversableProperty,
                         Class<?> rootBeanType,
                         Path pathToTraversableObject,
                         ElementType elementType);
}

isReachable() is called for every property about to be accessed either for validation or for cascading. A property is reachable if this method returns true.

isCascadable() is called for every property about to be cascaded (i.e. marked as @Valid). A property is cascadable if it is reachable and if the isCascadable method returns true.

Note

isCascadable() for a given property is only called if isReachable() returns true. In other words, isReachable() is always called before isCascadable() for a given property.

traversableObject is the object instance being evaluated. null if the check is triggered as part of a validateValue() call.

traversableProperty is the Node representing the property hosted by the traversableObject being considered for traversal. The name of a property is defined in Field and property validation.

rootBeanType is the class of the root being validated, i.e. either the type of the object passed to the validate method or the type declaring the validated method/constructor in case of method validation.

pathToTraversableObject is the Path from the rootBeanType down to the traversableObject. If the root object is traversableObject, pathToTraversableObject is composed of a single Node whose name is null. The path is described following the conventions described in ConstraintViolation (getPropertyPath).

elementType is the java.lang.annotation.ElementType the annotation is placed on. It can be either FIELD or METHOD. Any other value is not expected.

The Bean Validation provider must not access the state of a property, nor validate its constraints if the property is not traversable. A property is traversable if TraversableResolver returns true for this property.

If an exception occurs when the TraversableResolver is called, the exception is wrapped into a ValidationException.

The following elements are not passed through the traversable resolver filter:

  • the bean instance validated

  • the method and constructor parameter values being validated

  • the method and constructor return value being validated

But the properties of these elements (if validated) are. In this case the complete path is provided via pathToTraversableObject.

The traversable resolver used by default by a Bean Validation provider behaves as followed:

  • if Java Persistence is available in the runtime environment, a property is considered reachable if Java Persistence considers the property as loaded. A typical implementation will use Persistence.getPersistenceUtil().isLoaded(Object, String) to implement such contract.

  • if Java Persistence is not available in the runtime environment, all properties are considered reachable.

  • all properties are considered cascadable.

An example implementation of such a resolver is shown in Java Persistence aware TraversableResolver.

Example 5.33:49. Java Persistence aware TraversableResolver
public class JPATraversableResolver implements TraversableResolver {

    public boolean isReachable(Object traversableObject,
                               Path.Node traversableProperty,
                               Class<?> rootBeanType,
                               Path pathToTraversableObject,
                               ElementType elementType) {
        return traversableObject == null ||
                Persistence.getPersistenceUtil().isLoaded(
                        traversableObject,
                        traversableProperty.getName() );
    }

    public boolean isCascadable(Object traversableObject,
                               Path.Node traversableProperty,
                               Class<?> rootBeanType,
                               Path pathToTraversableObject,
                               ElementType elementType) {
        return true;
    }
}

See Bootstrapping to learn how to pass a custom TraversableResolver.

5.7.3.1. Examples

The following example assumes the object graph defined in Definitions used in the example and assumes the validation operation is applied on an address object.

Example 5.34:50. Definitions used in the example
public class Country {
    @NotNull
    private String name;
    @Size(max=2)
    private String ISO2Code;
    @Size(max=3)
    private String ISO3Code;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getISO2Code() {
        return ISO2Code;
    }

    public void setISO2Code(String ISO2Code) {
        this.ISO2Code = ISO2Code;
    }

    public String getISO3Code() {
        return ISO3Code;
    }

    public void setISO3Code(String ISO3Code) {
        this.ISO3Code = ISO3Code;
    }
}

public class Address {
    @NotNull @Size(max=30)
    private String addressline1;
    @Size(max=30)
    private String addressline2;
    @Size(max=11)
    private String zipCode;
    @Valid
    private Country country;

    private String city;

    public String getAddressline1() {
        return addressline1;
    }

    public void setAddressline1(String addressline1) {
        this.addressline1 = addressline1;
    }

    public String getAddressline2() {
        return addressline2;
    }

    public void setAddressline2(String addressline2) {
        this.addressline2 = addressline2;
    }

    public String getZipCode() {
        return zipCode;
    }

    public void setZipCode(String zipCode) {
        this.zipCode = zipCode;
    }

    @Size(max=30) @NotNull
    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public Country getCountry() {
        return country;
    }

    public void setCountry(Country country) {
        this.country = country;
    }
}

When the Bean Validation provider is about to check constraints of ISO3Code, it calls the TraversableResolver.isReachable() method to ensure that the ISO3Code property is reachable with the following parameter values:

  • traversableObject: country. The instance returned by address.getCountry().

  • traversableProperty: a PropertyNode whose name is "ISO3Code". Represents the property of traversableObject being verified.

  • rootBeanType: Address.class. The type of the root object being validated.

  • pathtoTraversableObject: a Path containing a single PropertyNode whose name is "country". The path from address to the country instance.

  • elementType: ElementType.FIELD. The ISO3Code property is annotated on its field.

When the Bean Validation provider is about to cascade validation on country (Address object), it calls the TraversableResolver.isReachable() method to ensure that the country property is reachable and if this method returns true, it calls TraversableResolver.isCascadable() with the following parameter values:

  • traversableObject: address. The address instance.

  • traversableProperty: a PropertyNode whose name is "country". Represents the property of traversableObject being verified.

  • rootBeanType: Address.class. The type of the root object being validated.

  • pathtoTraversableObject: a Path containing a single BeanNode whose name is null.

  • elementType: ElementType.FIELD. The country property is annotated on its field.

The following example shows invocations of the TraversableResolver as to be performed by the Bean Validation provider during method validation. The example is based on the object graph defined in Definitions used in the example and the AddressService class shown in Examplary class AddressService. It assumes that a call of persistAddress() is subject to method parameter validation.

Example 5.35:51. Examplary class AddressService
public class AddressService {
    public void persistAddress(@NotNull @Valid Address address) {
        [...]
    }
}

When the Bean Validation provider is about to validate the @NotNull constraint on the address parameter, no call to isReachable() is expected, since parameters are assumed to always be reachable. Similarly, no call to isCascadable() is expected when performing cascaded validation of the address parameter, since parameters are assumed to always be cascadable.

When the Bean Validation provider is about to validate constraints on the field addressline1 of the passed Address object, it calls the isReachable() method to ensure that the property is reachable with the following parameter values:

  • traversableObject: address. The instance passed to persistAddress().

  • traversableProperty: a PropertyNode whose name is "addressline1". Represents the property of traversableObject being verified.

  • rootBeanType: AddressService.class. The type of the root object being validated.

  • pathtoTraversableObject: a Path comprising a MethodNode (named "persistService") and a ParameterNode (with parameter index 0). The path from AddressService to the Address instance.

  • elementType: ElementType.FIELD. The addressline1 property is annotated on its field.

When the Bean Validation provider is about to perform a cascaded validation of the country property of the passed Address object, it calls the isReachable() method to ensure that the property is reachable. If this method returns true, it calls TraversableResolver.isCascadable() with the following parameter values:

  • traversableObject: address. The instance passed to persistAddress().

  • traversableProperty: a PropertyNode whose name is "country". Represents the property of traversableObject being verified.

  • rootBeanType: AddressService.class. The type of the root object being validated.

  • pathtoTraversableObject: a Path comprising a MethodNode (named "persistService") and a ParameterNode (with parameter index 0). The path from AddressService to the Address instance.

  • elementType: ElementType.FIELD. The country property is annotated on its field.

5.7.44.6.4. 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 hosting the constraint. For a given constraint evaluation, a single ConstraintValidator is considered.

The list of ConstraintValidators can contain at most one which targets cross-parameter. If the constraint targets the parameters of an executable either implicitly or by the use of validationAppliesTo in the constraint - see validationAppliesTo, then the cross-parameter ConstraintValidator is used. If none is present, a ConstraintDefinitionException is raised. If more than one cross-parameter ConstraintValidator is present, a ConstraintDefinitionException is raised.

If the constraint is a generic constraint, the following rules apply:

  • . If the constraint declaration 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 method (getter or non-getter) or constructor, the return type is the targeted type.

  • If the constraint is hosted on a method or constructor parameter, the parameter type is the targeted type.

  • If the constraint is hosted on a type argument of a parameterized type (i.e. a container element constraint, see Container element constraints), the type arguments typegetter is the targeted type.

  • If the constraint is subject to implicit unwrapping (see Implicit unwrapping of containers) and the applicable value extractor is defined for a generic type (e.g. javafx.beans.value.ObservableValue), the targeted type is the type captured for the type parameter handled by the value extractor (e.g. String if the constraint is placed on a StringProperty).

  • If the constraint is subject to implicit unwrapping and the applicable value extractor is defined for a non-generic type, the targeted type is the type defined by the extractor via @ExtractedValue#type() (e.g. Integer if the constraint is placed on a java.util.OptionalInt).

In other words, the resolution algorithm considers the type as defined in the source codemethod signature and not the runtime type of the value.

The rules written below describe formally the following statement: the ConstraintValidator chosen to validate the generic constraint on a declared type T is the one where the ConstraintValidator targets the annotated element, where the type supported by the ConstraintValidator 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 chosen ConstraintValidator supported type.

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

  • Only ConstraintValidator implementations targeting annotated elements are considered.

  • 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, Java SE 8 Edition,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 amongamongst the ConstraintValidators listed by the constraint A, an UnexpectedTypeException 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 the Java Language Specification3rd 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 than ConstraintValidator<A, U>.

  • If more than one maximally specific ConstraintValidator is found, an UnexpectedTypeException is raised.

Note

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

Let’s see a couple of declarations and their respective ConstraintValidator resolution. Assuming the definitions shown in ConstraintValidator and type resolution:

Example 5.36:52. ConstraintValidator and type resolution
[...]
@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 {
}
Table 5.1: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() { […​] }

UnexpectedTypeException: SerializableCollection is a subtype of both Collection and Serializable and neither Collection nor Serializable are subtypes of each other.

@Size String getName() { […​] }

UnexpectedTypeException: none of the ConstraintValidator types are supertypes of String.

5.7.5. ValueExtractor resolution

When detecting a container element constraint or a container element marked with @Valid, a value extractor must be determined so that the elements can be obtained from that container.

A value extractor handles one container type and - in the case of a generic container type - one type parameter of that container type. The applicable extractor is identified based on the container type and - in the case of a generic container type - the type argument hosting the container element constraint or @Valid4.7.

Exactly one value extractor must be identified when processing a container element constraint or container element marked with @Valid.

5.7.5.1. Registering ValueExtractor implementations

Value extractors can be registered with the validation engine in the following ways (in increasing order of priority):

  • Provided by the validation engine itself (see Built-in value extractors)

  • Via the Java service loader mechanism; for this the file META-INF/services/javax.validation.valueextraction.ValueExtractor must be provided, with the fully-qualified name(s) of one or more extractor implementations as its contents. It is undefined which value extractor will be selected if multiple extractors for the same type and type parameter are registered via the service loader mechanism.

  • By specifying the fully-qualified class name of one or several extractors in META-INF/validation.xml (see XML configuration: META-INF/validation.xml). Not more than one extractor for the same type and type parameter may be given.

  • By invoking the method Configuration#addValueExtractor(ValueExtractor<?>) (to apply it at the validator factory level). Not more than one extractor for the same type and type parameter may be passed.

  • By invoking the method ValidatorContext#addValueExtractor(ValueExtractor<?>) (to apply it for a single Validator instance). Not more than one extractor for the same type and type parameter may be passed.

A value extractor for a given type and type parameter specified at a higher priority overrides any other extractors for the same type and type parameter given at lower priorities. If e.g. a value extractor defined as class MyListValueExtractor implements ValueExtractor<List<@ExtractedValue ?>> { …​ } is given via ValidatorContext#addValueExtractor(ValueExtractor<?>), it will take precedence over any other value extractor implementing List<@ExtractedValue ?> given via Configuration#addValueExtractor(ValueExtractor<?>), META-INF/validation.xml or the service loader mechanism as well as the built-in extractor for List values.

5.7.5.2. ValueExtractor resolution algorithm for container element constraints

For a container with the declared type C whose element type is hosting a constraint, the following resolution rules apply for identifying the value extractor:

  • A ValueExtractor<T> is said to be type-compliant with C, if C is a subtype of T (according to the Java Language Specification, Java SE 8 Edition, chapter 4.10, "Subtyping"). Note that C is a subtype of T if C = T.

  • A ValueExtractor implementation is said to be container-element-compliant with C, if C is a generic container type and the value extractor implementation handles a type parameter that maps to the constrained type argument.

  • If no ValueExtractor type-compliant and container-element-compliant with C is found among the available value extractors, a ConstraintDeclarationException is raised.

  • A ValueExtractor<U> type-compliant with C is considered strictly more specific than a ValueExtractor<V> compliant with C 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.

  • A ValueExtractor<U> type-compliant with C is considered maximally specific if no other ValueExtractor<V> type-compliant with C is strictly more specific than ValueExtractor<U>.

  • If more than one maximally specific and container-element-compliant ValueExtractor is found, a ConstraintDeclarationException is raised.

Note
Implementation note

Extractor retrieval for container element constraints is based on the declared type of constrained elements, hence it is recommended that implementations perform the resolution once and then cache the value extractor for a given constraint.

5.7.5.3. ValueExtractor resolution algorithm for cascaded validation

For a container with the declared type C and the runtime type C' whose element type is marked for cascaded validation, the following resolution rules apply for identifying the value extractor:

  • A ValueExtractor<T> is said to be type-compliant with C', if C' is a subtype of T. Note that C' is a subtype of T if C' = T.

  • A ValueExtractor implementation is said to be container-element-compliant with C, if it handles a type parameter that maps to the type argument marked with @Valid.

  • If no ValueExtractor type-compliant with C' and container-element-compliant with C is found among the available value extractors, a ConstraintDeclarationException is raised.

  • A ValueExtractor<U> type-compliant with C' is considered strictly more specific than a ValueExtractor<V> compliant with C' 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.

  • A ValueExtractor<U> type-compliant with C' is considered maximally specific if no other ValueExtractor<V> type-compliant with C' is strictly more specific than ValueExtractor<U>.

  • If more than one maximally specific and container-element-compliant ValueExtractor is found, a ConstraintDeclarationException is raised.

Note
Implementation note

Extractor retrieval for cascaded validation is based on the runtime type of the container hosting @Valid (this is to be consistent with the semantics of property paths for cascaded validation), hence it is not possible to perform the extractor resolution only once per usage of @Valid and cache the result.

5.7.5.4. ValueExtractor resolution algorithm for applying container-level constraints to container elements

When detecting a constraint given as a declaration annotation and not as a type annotation (i.e. it is given on field, parameter etc. and not given on an a type argument of a parameterized type), the applicable value extractor, if any, is determined as follows:

  • If the constraint carries the Unwrapping.Skip payload, no value extractor is applied.

  • If the constraint carries the Unwrapping.Unwrap payload and there is exactly one maximally-specific type-compliant value extractor, this extractor is applied; if no type-compliant extractor or multiple maximally-specific type-compliant extractors exist, a ConstraintDeclarationException is raised.

  • If the constraint carries neither the Unwrapping.Unwrap nor the Unwrapping.Skip payload:

    • If there is exactly one maximally-specific type-compliant value extractor and this extractor is marked with @UnwrapByDefault, this extractor is applied;

    • Otherwise, no value extractor is applied.

5.7.5.5. Examples

Lets consider a couple of value extractor definitions and their respective ValueExtractor resolution against container element constraint declarations:

Example 5.37: ValueExtractor resolution
public interface ConcurrentList<T> {
    [...]
}

public class MyList<T> implements List<T>, ConcurrentList<T> {
    [...]
}

public interface Table<R, C, V> {
    [...]
}

interface ConfusingMap<K, V> extends Map<V, K> {
    [...]
}

interface SingleTypeMap<T> extends Map<T, T> {
    [...]
}

interface StringMap extends Map<String, String> {
    [...]
}

interface Property<T> {
    [...]
}

class StringProperty implements Property<String> {
    [...]
}

public class IterableValueExtractor implements ValueExtractor<Iterable<@ExtractedValue ?>> {
    [...]
}

public class ListValueExtractor implements ValueExtractor<List<@ExtractedValue ?>> {
    [...]
}

public class ConcurrentListValueExtractor implements ValueExtractor<
        ConcurrentList<@ExtractedValue ?>> {

    [...]
}

public class MapKeyExtractor implements ValueExtractor<Map<@ExtractedValue ?, ?>> {
    [...]
}

public class MapValueExtractor implements ValueExtractor<Map<?, @ExtractedValue ?>> {
    [...]
}

public class TableValueExtractor implements ValueExtractor<Table<?, ?, @ExtractedValue ?>> {
    [...]
}

@UnwrapByDefault
public class PropertyValueExtractor implements ValueExtractor<Property<@ExtractedValue ?>> {
    [...]
}

@UnwrapByDefault
public class OptionalIntValueExtractor implements ValueExtractor<
        @ExtractedValue(type = Integer.class) OptionalInt> {

    [...]
}

The following value extractor resolutions occur:

Table 5.2: Resolution of ValueExtractor for various container element constraints
Declaration Resolution

List<@Email String> emails

ListValueExtractor; strictly more specific than IterableValueExtractor

Iterable<@Valid Address> addresses = new ArrayList<>()

ListValueExtractor (the runtime type ArrayList is used for resolving value extractors for cascaded validation)

Map<@Email String, String> emails

MapKeyExtractor; equally specific as MapValueExtractor, but the latter isnt container-element-compliant

ConfusingMap<@Email String, String> map

MapValueExtractor; the constrained type argument maps to the type parameter V of Map

@Email StringProperty

PropertyValueExtractor; the extractor is marked with @UnwrapByDefault, i.e. implicit unwrapping is performed; String will be used for validator resolution as thats the type captured for the type parameter handled by the applied value extractor

@Min(1) OptionalInt

OptionalIntValueExtractor; the extractor is marked with @UnwrapByDefault, i.e. implicit unwrapping is performed; Integer as given via @ExtractedValue#type() will be used for validator resolution

Optional<@Email String> getEmail() {…​}

ConstraintDeclarationException; no compliant extractor exists

Table<@Min(1) String, String, String> table

ConstraintDeclarationException; TableValueExtractor is type-compliant but not container-element-compliant

MyList<@Email String> emails

ConstraintDeclarationException; ListValueExtractor and ConcurrentListValueExtractor are equally specific

SingleTypeMap<@NotEmpty String> map

ConstraintDeclarationException; MapKeyExtractor and MapValueExtractor are equally specific and both container-element-compliant

@NotEmpty(payload=Unwrapping.Unwrap.class) StringMap

ConstraintDeclarationException; more than one maximally-specific extractor is found (MapKeyExtractor and MapValueExtractor)

5.8. Examples

The first example demonstrates how beans, fields and getters are annotated to express some constraints.

Example 5.38:53. Place constraint declarations on the element to validate
@ZipCodeCityCoherenceChecker
public class Address {
    @NotNull @Size(max=30)
    private String addressline1;

    @Size(max=30)
    private String addressline2;

    private String zipCode;

    private String city;

    public String getAddressline1() {
        return addressline1;
    }

    public void setAddressline1(String addressline1) {
        this.addressline1 = addressline1;
    }

    public String getAddressline2() {
        return addressline2;
    }

    public void setAddressline2(String addressline2) {
        this.addressline2 = addressline2;
    }

    public String getZipCode() {
        return zipCode;
    }

    public void setZipCode(String zipCode) {
        this.zipCode = zipCode;
    }

    @Size(max=30) @NotNull
    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }
}

During the validation routine execution on an Address object,

  • addressline1 field value is passed to the @NotNull as well as @Size constraint validation implementations.

  • addressline2 field value is passed to the @Size constraint validation implementation.

  • getCity value is passed to the @Size and @NotNull constraint validation implementations.

  • @ZipCodeCoherenceChecker is a constraint whose validation implementation’s isValid method receives the Address object.

The second example demonstrates object graph validation.

Example 5.39:54. Define object graph validation
public class Country {
    @NotNull
    private String name;
    @Size(max=2)
    private String ISO2Code;
    @Size(max=3)
    private String ISO3Code;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getISO2Code() {
        return ISO2Code;
    }

    public void setISO2Code(String ISO2Code) {
        this.ISO2Code = ISO2Code;
    }

    public String getISO3Code() {
        return ISO3Code;
    }

    public void setISO3Code(String ISO3Code) {
        this.ISO3Code = ISO3Code;
    }
}

public class Address {
    @NotNull @Size(max=30)
    private String addressline1;
    @Size(max=30)
    private String addressline2;
    @Size(max=11)
    private String zipCode;
    @NotNull @Valid
    private Country country;

    private String city;

    public String getAddressline1() {
        return addressline1;
    }

    public void setAddressline1(String addressline1) {
        this.addressline1 = addressline1;
    }

    public String getAddressline2() {
        return addressline2;
    }

    public void setAddressline2(String addressline2) {
        this.addressline2 = addressline2;
    }

    public String getZipCode() {
        return zipCode;
    }

    public void setZipCode(String zipCode) {
        this.zipCode = zipCode;
    }

    @Size(max=30) @NotNull
    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public Country getCountry() {
        return country;
    }

    public void setCountry(Country country) {
        this.country = country;
    }
}

During the validation routine execution on an Address object, constraints on addressLine1, addressLine2, zipCode, getCity and country are processed as well as the validation of the Country object itself, more specifically country.name is checked for @NotNull, ISO2Code and ISO3Code are checked for @Size.

Assuming that @NonEmptyNotEmpty is defined as such:

@Documented
@NotNull
@Size(min = 1)
@ReportAsSingleViolation
@Constraint(validatedBy = NonEmptyNotEmpty.NonEmptyValidatorNotEmptyValidator.class)
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface NonEmptyNotEmpty  {

    String message() default "{com.acme.constraint.NonEmptyNotEmpty.message}";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
    @Retention(RUNTIME)
    @Documented
    @interface List {
        NonEmptyNotEmpty [] value();
    }

    class NonEmptyValidatorNotEmptyValidator  implements ConstraintValidator<NonEmptyNotEmpty, String> {

        @Override
        public void initialize(NotEmpty constraintAnnotation) { } @Override public boolean isValid(String value, ConstraintValidatorContext context) {
            return true;
        }
    }
}

The third example demonstrates superclass, inheritance and composite constraints.

Example 5.40:55. Use inheritance, constraints on superclasses and composite constraints
public interface Person {
    @NonEmptyNotEmpty 
    String getFirstName();

    String getMiddleName();

    @NonEmptyNotEmpty 
    String getLastName();
}

public class Customer implements Person {
    private String firstName;
    private String middleName;
    private String lastName;
    @NotNull
    private String customerId;
    @Password(robustness=5)
    private String password;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getMiddleName() {
        return middleName;
    }

    public void setMiddleName(String middleName) {
        this.middleName = middleName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getCustomerId() {
        return customerId;
    }

    public void setCustomerId(String customerId) {
        this.customerId = customerId;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

public class PreferredGuest extends Customer {
    @CreditCard
    private String guestCreditCardNumber;

    public String getGuestCreditCardNumber() {
        return guestCreditCardNumber;
    }

    public void setGuestCreditCardNumber(String guestCreditCardNumber) {
        this.guestCreditCardNumber = guestCreditCardNumber;
    }
}

public class CommonGuest extends customer {}

When validating a PreferredGuest the following constraints are processed:

  • @NonEmptyNotEmpty , @NotNull and @Size(min=1) on firstName

  • @NonEmptyNotEmpty , @NotNull and @Size(min=1) on lastName

  • @NotNull on customerId, @Password on password

  • @CreditCard on guestCreditCardNumber

When validating CommonGuest, the following constraints are processed:

  • @NonEmptyNotEmpty , @NotNull and @Size(min=1) on firstName

  • @NonEmptyNotEmpty , @NotNull and @Size(min=1) on lastName

  • @NotNull on customerId, @Password on password

The fourth example demonstrates the influence of group sequence.

Example 5.41:56. Use groups and group sequence to define constraint ordering
@GroupSequence({First.class, Second.class, Last.class})
public interface Complete {}

public class Book {
    @NonEmptyNotEmpty (groups=First.class)
    private String title;

    @Size(max=30, groups=Second.class)
    private String subtitle;

    @Valid
    @NotNull(groups=First.class)
    private Author author;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getSubtitle() {
        return subtitle;
    }

    public void setSubtitle(String subtitle) {
        this.subtitle = subtitle;
    }

    public Author getAuthor() {
        return author;
    }

    public void setAuthor(Author author) {
        this.author = author;
    }
}

public class Author {
    @NonEmptyNotEmpty (groups=Last.class)
    private String firstName;

    @NonEmptyNotEmpty (groups=First.class)
    private String lastName;

    @Size(max=30, groups=Last.class)
    private String company;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getCompany() {
        return company;
    }

    public void setCompany(String company) {
        this.company = company;
    }
}

Assuming the validation of the Complete group on the following book instance:

Author author = new Author();
author.setLastName( "Baudelaire" );
author.setFirstName( "" );
Book book = new Book();
book.setAuthor( author );

the validation routine will return the following failure:

  • @NotNull failure (from @NonEmptyNotEmpty ) on the title field

As both title and author.lastname are checked as part of the First group. If the instance is updated:

book.setTitle( "Les fleurs du mal" );
author.setCompany( "Some random publisher with a very very very long name" );

the validation routine will return the following failures:

  • author.firstName fails to pass the @Size(min=1) (from @NonEmptyNotEmpty ) constraint

  • author.company fails to pass the @Size constraint

As the First and Second groups pass without failure, the Last group is going through validation.

65. Validation APIs

The default package for the Bean Validation APIs is javax.validation.

6.15.1. Validator API

The main Bean Validation API is the javax.validation.Validator interface.

A Validator instance is able to validate instances of beans and their associated objects if any. It is recommended to leave the caching of Validator instances to the ValidatorFactory. Validator implementations must be thread-safe.

Listing 6.1: Example 57. Validator interface
/**
 * Validates bean instances. Implementations of this interface must be thread-safe.
 *
 * @author Emmanuel Bernard
 * @author Hardy Ferentschik
 * @author Gunnar Morling
 */
public interface Validator {

    /**
     * Validates all constraints on {@code object}.
     *
     * @param object object to validate
     * @param groups the group or list of groups targeted for validation (defaults to
     *        {@link Default})
     * @param <T> the type of the object to validate
     * @return constraint violations or an empty set if none
     * @throws IllegalArgumentException if object is {@code null}
     *         or if {@code null} is passed to the varargs groups
     * @throws ValidationException if a non recoverable error happens
     *         during the validation process
     */
    <T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups);

    /**
     * Validates all constraints placed on the property of {@code object}
     * named {@code propertyName}.
     *
     * @param object object to validate
     * @param propertyName property to validate (i.e. field and getter constraints)
     * @param groups the group or list of groups targeted for validation (defaults to
     *        {@link Default})
     * @param <T> the type of the object to validate
     * @return constraint violations or an empty set if none
     * @throws IllegalArgumentException if {@code object} is {@code null},
     *         if {@code propertyName} is {@code null}, empty or not a valid object property
     *         or if {@code null} is passed to the varargs groups
     * @throws ValidationException if a non recoverable error happens
     *         during the validation process
     */
    <T> Set<ConstraintViolation<T>> validateProperty(T object,
                                                     String propertyName,
                                                     Class<?>... groups);

    /**
     * Validates all constraints placed on the property named {@code propertyName}
     * of the class {@code beanType} would the property value be {@code value}.
     * <p>
     * {@link ConstraintViolation} objects return {@code null} for
     * {@link ConstraintViolation#getRootBean()} and
     * {@link ConstraintViolation#getLeafBean()}.
     *
     * @param beanType the bean type
     * @param propertyName property to validate
     * @param value property value to validate
     * @param groups the group or list of groups targeted for validation (defaults to
     *        {@link Default}).
     * @param <T> the type of the object to validate
     * @return constraint violations or an empty set if none
     * @throws IllegalArgumentException if {@code beanType} is {@code null},
     *         if {@code propertyName} is {@code null}, empty or not a valid object property
     *         or if {@code null} is passed to the varargs groups
     * @throws ValidationException if a non recoverable error happens
     *         during the validation process
     */
    <T> Set<ConstraintViolation<T>> validateValue(Class<T> beanType,
                                                  String propertyName,
                                                  Object value,
                                                  Class<?>... groups);

    /**
     * Returns the descriptor object describing bean constraints.
     * <p>
     * The returned object (and associated objects including
     * {@link ConstraintDescriptor}s) are immutable.
     *
     * @param clazz class or interface type evaluated
     * @return the bean descriptor for the specified class
     * @throws IllegalArgumentException if clazz is {@code null}
     * @throws ValidationException if a non recoverable error happens
     *         during the metadata discovery or if some
     *         constraints are invalid.
     */
    BeanDescriptor getConstraintsForClass(Class<?> clazz);

    /**
     * Returns an instance of the specified type allowing access to
     * provider-specific APIs.
     * <p>
     * If the Bean Validation provider implementation does not support
     * the specified class, {@link ValidationException} is thrown.
     *
     * @param type the class of the object to be returned
     * @param <T> the type of the object to be returned
     * @return an instance of the specified class
     * @throws ValidationException if the provider does not support the call
     */
    <T> T unwrap(Class<T> type);

    /**
     * Returns the contract for validating parameters and return values of methods
     * and constructors.
     *
     * @return contract for method and constructor validation
     *
     * @since 1.1
     */
    ExecutableValidator forExecutables();
}

The methods validate(), validateProperty() and validateValue() are used for the validation of Java beans respectively single bean properties. See the next section for more details.

forExecutables() provides access to the contract for validating method and constructor parameters and return values. The individual methods for method and constructor validation are described in Methods for validating method and constructor constraints.

getConstraintsForClass() returns constraint-related metadata for given types and is described in detail in Constraint metadata request APIs.

unwrap() is provided as a way to access objects of a given type specific to a Bean Validation provider typically as a complement to the Validator contract. Using this method makes your code non portable.

Example 6.1:58. Using unwrap to access a provider specific contract
//if using the ACME provider
ACMEValidator acmeValidator = factory.unwrap(ACMEValidator.class);
acmeValidator.setSpecificConfiguration( [...] );

6.1.15.1.1. Validation methods

<T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups) is used to validate a given object. This method implements the logic described in Validation routine. An IllegalArgumentException is thrown when null is passed for the object parameter or the varargs groups parameter. A Set containing all ConstraintViolation objects representing the failing constraints is returned, an empty Set is returned otherwise.

<T> Set<ConstraintViolation<T>> validateProperty(T object, String propertyName, Class<?>... groups) validates a given field or property of an object. An IllegalArgumentException is thrown when validateProperty() is called and object is null or propertyName is null, empty or invalid or null is passed to the varargs groups parameter. The property name is the JavaBeans property name (as defined by the JavaBeans Introspector class). This method implements the logic described in Validation routine but only to the given property. @Valid is not honored by this method. This method is useful for partial object validation.

<T> Set<ConstraintViolation<T>> validateValue(Class<T> beanType, String propertyName, Object value, Class<?>... groups) validates the property referenced by propertyName present on beanType or any of its superclasses, if the property value were value. An IllegalArgumentException is thrown when validateValue() is called and beanTypeobject is null or propertyName is null, empty or invalid or null is passed to the varargs groups parameter. This method implements the logic described in Validation routine and apply it only to the given property and for the given value. @Valid is not honored by this method. This method is useful for ahead of time validation (i.e. before the JavaBean is populated or updated).

Note

If multiple constrained fields or getters share the same name and hide one another in the class hierarchy according to the Java visibility rules, the list of constraints evaluated is unspecified. This will be clarified in a later version of this specification. Note that method overriding is not impacted.

If getters and fields share the same name and are present at different levels of the hierarchy, the list of constraints evaluated is unspecified. This will be clarified in a later version of this specification.

However, constraints hosted on the most specific (hierarchy wise) element type are always evaluated.

Note

validateProperty() and validateValue() accept property names and not full paths. Bean Validation implementations might accept string representations of paths but this behavior is not portable.

If some unrecoverable failure happens during validation, a ValidationException is raised. This exception can be specialized in some situations (invalid group definition, invalid constraint definition, invalid constraint declaration). See Exception model or the relative sections for more information.

6.1.1.1. Examples

All the examples will be based on the following class definition, constraint declarations and address instance.

public class Address {
    @NotNull @Size(max=30)
    private String addressline1;

    @Size(max=30)
    private String addressline2;

    private String zipCode;

    private String city;

    public String getAddressline1() {
        return addressline1;
    }

    public void setAddressline1(String addressline1) {
        this.addressline1 = addressline1;
    }

    public String getAddressline2() {
        return addressline2;
    }

    public void setAddressline2(String addressline2) {
        this.addressline2 = addressline2;
    }

    public String getZipCode() {
        return zipCode;
    }

    public void setZipCode(String zipCode) {
        this.zipCode = zipCode;
    }

    @Size(max=30) @NotNull
    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }
}

Address address = new Address();
address.setAddressline1( null );
address.setAddressline2( null );
address.setCity("Llanfairpwllgwyngyllgogerychwyrndrobwyll-llantysiliogogogoch");
//town in North Wales

The following code will return two ConstraintViolation objects. One for addressline1 violating @NotNull and one for city violating @Size.

validator.validate(address).size() == 2

The following code will return one ConstraintViolation since city violates @Size and only city is validated.

validator.validateProperty(address, "city").size() == 1

The following code will return no ConstraintViolation objects because the value "Paris" for city would not raise any constraint failures.

validator.validateValue(Address.class, "city", "Paris").size() == 0

6.1.25.1.2. Methods for validating method and constructor constraints

The methods for the validation of parameters and return values of methods and constructors can be found on the interface javax.validation.executable.ExecutableValidator.

Listing 6.2: Example 59. ExecutableValidator interface
package javax.validation.executable;

/**
 * Validates parameters and return values of methods and constructors.
 * Implementations of this interface must be thread-safe.
 *
 * @author Gunnar Morling
 * @since 1.1
 */
public interface ExecutableValidator {

    /**
     * Validates all constraints placed on the parameters of the given method.
     *
     * @param <T> the type hosting the method to validate
     * @param object the object on which the method to validate is invoked
     * @param method the method for which the parameter constraints is validated
     * @param parameterValues the values provided by the caller for the given method's
     *        parameters
     * @param groups the group or list of groups targeted for validation (defaults to
     *        {@link Default})
     * @return a set with the constraint violations caused by this validation;
     *         will be empty if no error occurs, but never {@code null}
     * @throws IllegalArgumentException if {@code null} is passed for any of the parameters
     *         or if parameters don't match with each other
     * @throws ValidationException if a non recoverable error happens during the
     *         validation process
     */
    <T> Set<ConstraintViolation<T>> validateParameters(T object,
                                                       Method method,
                                                       Object[] parameterValues,
                                                       Class<?>... groups);

    /**
     * Validates all return value constraints of the given method.
     *
     * @param <T> the type hosting the method to validate
     * @param object the object on which the method to validate is invoked
     * @param method the method for which the return value constraints is validated
     * @param returnValue the value returned by the given method
     * @param groups the group or list of groups targeted for validation (defaults to
     *        {@link Default})
     * @return a set with the constraint violations caused by this validation;
     *         will be empty if no error occurs, but never {@code null}
     * @throws IllegalArgumentException if {@code null} is passed for any of the object,
     *         method or groups parameters or if parameters don't match with each other
     * @throws ValidationException if a non recoverable error happens during the
     *         validation process
     */
    <T> Set<ConstraintViolation<T>> validateReturnValue(T object,
                                                        Method method,
                                                        Object returnValue,
                                                        Class<?>... groups);

    /**
     * Validates all constraints placed on the parameters of the given constructor.
     *
     * @param <T> the type hosting the constructor to validate
     * @param constructor the constructor for which the parameter constraints is validated
     * @param parameterValues the values provided by the caller for the given constructor's
     *        parameters
     * @param groups the group or list of groups targeted for validation (defaults to
     *        {@link Default})
     * @return a set with the constraint violations caused by this validation;
     *         Will be empty if no error occurs, but never {@code null}
     * @throws IllegalArgumentException if {@code null} is passed for any of the parameters
     *         or if parameters don't match with each other
     * @throws ValidationException if a non recoverable error happens during the
     *         validation process
     */
    <T> Set<ConstraintViolation<T>> validateConstructorParameters(Constructor<? extends T> constructor,
                                                                  Object[] parameterValues,
                                                                  Class<?>... groups);

    /**
     * Validates all return value constraints of the given constructor.
     *
     * @param <T> the type hosting the constructor to validate
     * @param constructor the constructor for which the return value constraints is validated
     * @param createdObject the object instantiated by the given method
     * @param groups the group or list of groups targeted for validation (defaults to
     *        {@link Default})
     * @return a set with the constraint violations caused by this validation;
     *         will be empty, if no error occurs, but never {@code null}
     * @throws IllegalArgumentException if {@code null} is passed for any of the parameters
     *         or if parameters don't match with each other
     * @throws ValidationException if a non recoverable error happens during the
     *         validation process
     */
    <T> Set<ConstraintViolation<T>> validateConstructorReturnValue(Constructor<? extends T> constructor,
                                                                   T createdObject,
                                                                   Class<?>... groups);
}

<T> Set<ConstraintViolation<T>> validateParameters(T object, Method method, Object[] parameterValues, Class<?>... groups) validates the arguments (as given in parameterValues) for the parameters of a given method (identified by method). Cross-parameter constraints are also validated. A set containing all ConstraintViolation objects representing the failing constraints is returned, an empty set is returned if no constraint violations occurred. An IllegalArgumentException will be thrown if null is passed for any of the parameters or if the parameters don’t match with each other (i.e. object and method don’t match, parameterValues and method don’t match).

<T> Set<ConstraintViolation<T>> validateReturnValue(T object, Method method, Object returnValue, Class<?>... groups) validates the return value (specified by returnValue) of a given method (identified by method). A set containing all ConstraintViolation objects representing the failing constraints is returned, an empty set is returned if no constraint violations occurred. An IllegalArgumentException will be thrown if null is passed for any of the parameters object, method and groups or if the parameters don’t match with each other (i.e. object and method don’t match, returnValue and method don’t match).

<T> Set<ConstraintViolation<T>> validateConstructorParameters(Constructor<T> constructor, Object[] parameterValues, Class<?>... groups) validates the arguments (as given in parameterValues) for the parameters of a given constructor (identified by constructor). Cross-parameter constraints are also validated. A set containing all ConstraintViolation objects representing the failing constraints is returned, an empty set is returned if no constraint violations occurred. An IllegalArgumentException will be thrown if null is passed for any of the parameters or if the parameters don’t match with each other (i.e. parameterValues and constructor don’t match).

<T> Set<ConstraintViolation<T>> validateConstructorReturnValue(Constructor<T> constructor, T createdObject, Class<?>... groups) validates the object (specified by createdObject) of a given constructor (identified by constructor). A set containing all ConstraintViolation objects representing the failing constraints is returned, an empty set is returned if no constraint violations occurred. An IllegalArgumentException will be thrown if null is passed for any of the parameters or if the parameters don’t match with each other (i.e. createdObject and constructor don’t match).

None of those methods honor the XML configuration around executable validation nor the presence of @ValidateOnExecution. In other words, elements will be validated regardless of these settings when explicitly calling the validation methods.

6.1.2.1. Examples

All the examples will be based on the following class definitions, constraint declarations and instances.

public class OrderService {

    @NotNull
    private CreditCardProcessor creditCardProcessor;

    @Valid
    public OrderService(@NotNull CreditCardProcessor creditCardProcessor) {
        [...]
    }

    @NotNull
    public Order placeOrder(
        @NotNull @Size(min=3, max=20) String customerCode,
        @NotNull @Valid Item item,
        @Min(1) int quantity) {

        [...]
    }
}

public class Item {

    @NotNull;
    private String name;

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
}

Item item1 = new Item();
item1.setName("Kiwi");

Item item2 = new Item();
item2.setName(null);

Constructor<OrderService> constructor = [...]; //get constructor object
Method placeOrder = [...]; //get method object

OrderService orderService = new OrderService(new DefaultCreditCardProcessor());

ExecutableValidator executableValidator = Validation
    .buildDefaultValidatorFactory().getValidator().forExecutables();

The following method parameter validation will return one ConstraintViolation object as the customer code is null:

//orderService.placeOrder(null, item1, 1);
executableValidator.validateParameters(
    orderService, placeOrder, new Object[] { null, item1, 1 }).size() == 1;

The following method parameter validation will return one ConstraintViolation object as the item parameter is marked for cascaded validation and the given Item instance is not valid (its name is null):

//orderService.placeOrder("CUST-123", item2, 1);
executableValidator.validateParameters(
    orderService, placeOrder, new Object[] { "CUST-123", item2, 1 }).size() == 1;

The following constructor parameter validation will return one ConstraintViolation object as null is passed for the creditCardProcessor parameter:

//new OrderService(null);
executableValidator.validateConstructorParameters(constructor, new Object[] { null })
    .size() == 1;

Assuming the placeOrder() method returned null, the following return value validation will return one ConstraintViolation:

executableValidator.validateReturnValue(orderService, placeOrder, null).size() == 1;

Assuming the constructor of OrderService failed to store the given credit card processor into the creditCardProcessor field, the following validation of the constructor return value would fail as the constructor is marked with @Valid and the @NotNull constraint of the OrderService class would be violated:

executableValidator.validateConstructorReturnValue(constructor, orderService).size() == 1;

Let’s now look at how a validation interceptor would use these methods.

@Interceptor
public class SampleMethodInterceptor {
    @Inject
    private Validator validator;

    @AroundInvoke
    public Object validateMethodInvocation(InvocationContext ctx) throws Exception {
        //validate parameters
        Set<ConstraintViolation<Object>> violations;
        violations = validator.forExecutables().validateParameters(
                ctx.getTarget(),
                ctx.getMethod(),
                ctx.getParameters()
        );

        //if a violation occurs for parameters, raise an exception
        if ( !violations.isEmpty() ) {
            throw new ConstraintViolationException(
                    buildMessage( ctx.getMethod(), ctx.getParameters(), violations ),
                    violations
            );
        }

        //execute the method proper
        Object result = ctx.proceed();

        //validate the return type
        violations = validator.forExecutables().validateReturnValue(
                ctx.getTarget(),
                ctx.getMethod(),
                result
        );

        //if a violation occurs for the return type, raise an exception
        if ( !violations.isEmpty() ) {
            throw new ConstraintViolationException(
                    buildMessage( ctx.getMethod(), ctx.getParameters(), violations ),
                    violations
            );
        }

        //return the result
        return result;
    }
}

6.1.35.1.3. groups

Groups allow you to restrict the set of constraints applied during validation. Groups targeted are passed as parameters to the validate(), validateProperty() and validateValue() methods as well as the methods to validate method/constructor constraints (see Methods for validating method and constructor constraints). All constraints belonging to the targeted group(s) are applied during the Validation routine. If no group is passed, the Default group is assumed. groups describes how to define groups on constraints.

When more than one group is evaluated and passed to the various validate methods, order is not constrained. It is equivalent to the validation of a group G inheriting all groups (i.e. implementing all interfaces) passed to the validation method.

6.1.3.1. Examples
/** Validates a minimal set of constraints */
public interface Minimal {}

public class Address {

    @NonEmptyNotEmpty (groups = Minimal.class)
    @Size(max=50)
    private String street1;

    @NonEmptyNotEmpty 
    private String city;

    @NonEmptyNotEmpty (groups = {Minimal.class, Default.class})
    private String zipCode;

    [...]
}

In the previous example, @NonEmptyNotEmpty (and its composing constraints, assuming the definition given in chapter Constraint declaration and validation process) on street1 applies to the group Minimal, @Size on street1 applies to the group Default and @NonEmptyNotEmpty (and its composing constraints) on zipCode applies to the groups Default and Minimal.

validator.validate(address);

validates the group Default (implicitly) and applies @Size on street1, @NonEmptyNotEmpty (and its composing constraints) on city, @NonEmptyNotEmpty (and its composing constraints) on zipCode. Particularly, @NonEmptyNotEmpty (and its composing constraints) on street1 are not applied.

validator.validate(address, Minimal.class);

applies @NonEmptyNotEmpty (and its composing constraints) on street1 and @NonEmptyNotEmpty (and its composing constraints) on zipCode because they belong to the Minimal group.

validator.validate(address, Minimal.class, Default.class);

validates both Default and Minimal groups. The routine applies @NonEmptyNotEmpty (and its composing constraints) and @Size on street1, @NonEmptyNotEmpty (and its composing constraints) on city, @NonEmptyNotEmpty (and its composing constraints) on zipCode. Note that if zipCode is empty, only one ConstraintViolation object will represent the failure and the not empty validation will only be executed once.

Let’s look at a more complex example involving group sequence.

public class Address {
    @NonEmptyNotEmpty (groups = Minimal.class)
    @Size(max=50, groups=FirstStep.class)
    private String street1;

    @NonEmptyNotEmpty (groups=SecondStep.class)
    private String city;

    @NonEmptyNotEmpty (groups = {Minimal.class, SecondStep.class})
    private String zipCode;

    [...]

    public interface FirstStep {}

    public interface SecondStep {}

    @GroupSequence({Firststep.class, SecondStep.class})
    public interface Total {}
}

When running:

validator.validate(address, Minimal.class, Total.class);

the validation process will process @NonEmptyNotEmpty (and its composing constraints) and @Size from street1 and @NonEmptyNotEmpty (and its composing constraints) from zipCode. If @Size from street1 does not generate a failure, then @NonEmptyNotEmpty (and its composing constraints) from city will be processed as part of SecondStep. Note that @NonEmptyNotEmpty (and its composing constraints) from zipCode are not reprocessed as they have already been processed before.

When running:

validator.validate(address, Total.class, SecondStep.class);

@NonEmptyNotEmpty (and its composing constraints) from city and @NonEmptyNotEmpty (and its composing constraints) from zipCode will be processed even if @Size from street1 fails: while SecondStep is in the Total group sequence and hence should not be triggered if FirstStep has a failure, it also has been requested outside the sequence (in this case explicitly).

Note

If the group definition is invalid, a GroupDefinitionException is raised.

6.25.2. ConstraintViolation

ConstraintViolation is the class describing a single constraint failure. A set of ConstraintViolation is returned for an object validation.

Listing 6.3: ConstraintViolation interface
/**
 * Describes a constraint violation. This object exposes the constraint
 * violation context as well as the message describing the violation.
 *
 * @param <T> the type of the root bean
 *
 * @author Emmanuel Bernard
 */
public interface ConstraintViolation<T> {

    /**
     * @return the interpolated error message for this constraint violation
     */
    String getMessage();

    /**
     * @return the non-interpolated error message for this constraint violation
     */
    String getMessageTemplate();

    /**
     * Returns the root bean being validated. For method validation, returns
     * the object the method is executed on.
     * <p>/> 
     * Returns {@code null} when:
     * <ul>
     *     <li>the {@code ConstraintViolation} is returned after calling
     *     {@link Validator#validateValue(Class, String, Object, Class[])}</li>
     *     <li>the {@code ConstraintViolation} is returned after validating a
     *     constructor.</li>
     * </ul>
     *
     * @return the validated object, the object hosting the validated element or {@code null}
     */
    T getRootBean();

    /**
     * Returns the class of the root bean being validated.
     * For method validation, this is the object class the
     * method is executed on.
     * For constructor validation, this is the class the constructor
     * is declared on.
     *
     * @return the class of the root bean or of the object hosting the validated element
     */
    Class<T> getRootBeanClass();

    /**
     * Returns:
     * <ul>
     *     <li>the bean instance the constraint is applied on if it is
     *     a bean constraint</li>
     *     <li>the bean instance hosting the property the constraint
     *     is applied on if it is a property constraint or a container element constraint
     *     hosted on a property</li>
     *     <li>{@code null} when the {@code ConstraintViolation} is returned
     *     after calling {@link Validator#validateValue(Class, String, Object, Class[])}
     *     </li>
     *     <li>the object the method is executed on if it is
     *     a method parameter, cross-parameter or return value constraint or a
     *     container element constraint hosted on a method parameter or return value</li>
     *     <li>{@code null} if it is a constructor parameter or
     *     cross-parameter constraint or a container element constraint hosted on a
     *     constructor parameter</li>
     *     <li>the object the constructor has created if it is a
     *     constructor return value constraint</li>
     * </ul>
     *
     * @return the leaf bean
     */
    Object getLeafBean();

    /**
     * Returns an {@code Object[]} representing the constructor or method invocation
     * arguments if the {@code ConstraintViolation} is returned after validating the
     * method or constructor parameters.
     * Returns {@code null} otherwise.
     *
     * @return parameters of the method or constructor invocation or {@code null}
     *
     * @since 1.1
     */
    Object[] getExecutableParameters();

    /**
     * Returns the return value of the constructor or method invocation
     * if the {@code ConstraintViolation} is returned after validating the method
     * or constructor return value.
     * <p>/> 
     * Returns {@code null} if the method has no return value.
     * Returns {@code null} otherwise.
     *
     * @return the method or constructor return value or {@code null}
     *
     * @since 1.1
     */
    Object getExecutableReturnValue();

    /**
     * @return the property path to the value from {@code rootBean}
     */
    Path getPropertyPath();

    /**
     * Returns the value failing to pass the constraint.
     * For cross-parameter constraints, an {@code Object[]} representing
     * the method invocation arguments is returned.
     *
     * @return the value failing to pass the constraint
     */
    Object getInvalidValue();

    /**
     * Returns the constraint metadata reported to fail.
     * The returned instance is immutable.
     *
     * @return constraint metadata
     */
    ConstraintDescriptor<?> getConstraintDescriptor();

    /**
     * Returns an instance of the specified type allowing access to
     * provider-specific APIs. If the Bean Validation provider
     * implementation does not support the specified class,
     * {@link ValidationException} is thrown.
     *
     * @param type the class of the object to be returned
     * @param <U> the type of the object to be returned
     * @return an instance of the specified class
     * @throws ValidationException if the provider does not support the call
     *
     * @since 1.1
     */
    <U> U unwrap(Class<U> type);
}

The getMessage() method returns the interpolated (localized) message for the failing constraint (see Message interpolation for more information on message interpolator). This can be used by clients to expose user friendly messages.

The getMessageTemplate() method returns the non-interpolated error message (usually the message attribute on the constraint declaration). Frameworks can use this as an error code key.

The getRootBean() method returns the root object being validated that led to the failing constraint (i.e. the object the client code passes to the Validator.validate() method). For method validation, returns the object the method is executed on. For constructors or when Validator.validateValue() is used, returns null.

The getRootBeanClass() method returns the class of the root bean being validated. For method validation, this is the object class the method is executed on. For constructor validation, this is the class the constructor is declared on.

The getLeafBean() method returns the following object:

  • If a bean constraint, the bean instance the constraint is applied on.

  • If a property constraint or a container element constraint hosted on a property, the bean instance hosting the property the constraint is applied on.

  • If a property constraint, null when the ConstraintViolation is returned after calling Validator.validateValue().

  • If a method parameter, cross-parameter or return value constraint or a container element constraint hosted on a method parameter or return value, the object the method is executed on.

  • If a constructor parameter or cross-parameter constraint or a container element constraint hosted on a constructor parameter, null.

  • If a constructor return value constraint, the object the constructor has created.

The getExecutableParameters() returns the parameters provided to the method or constructor invocation or null if not validating athe method or constructor parameters.

The getExecutableReturnValue() returns the return value of the method or constructor invocation or null if the method has no return value or if not validating athe method or constructor return value.

The getInvalidValue() method returns the value (field, property, method/constructor parameter, method/constructor return value, container element or validated object) being passed to isValid(). For a cross-parameter constraint failure, an Object[] representing the method/constructor invocation arguments is returned. In case a constraint given on a container is subject to implicit application to the container element(s) (see Implicit unwrapping of containers), getInvalidValue() returns the invalid container element value.

getConstraintDescriptor() provides access to the failing constraint metadata (see ConstraintDescriptor).

The getPropertyPath() method returns the Path object representing the navigation path from the root object to the failing object.

unwrap() is provided as a way to access objects of a given type specific to a Bean Validation provider typically as a complement to the ConstraintViolation contract. Using this method makes your code non portable.

Listing 6.4: Example 60. Path and , Node interfaces and ElementKind enuminterfaces
/**
 * Represents the navigation path from an object to another
 * in an object graph.
 * Each path element is represented by a {@code Node}.
 * <p>/> 
 * The path corresponds to the succession of nodes
 * in the order they are returned by the {@code Iterator}.
 *
 * @author Emmanuel Bernard
 * @author Gunnar Morling
 * @author Guillaume Smet
 */
public interface Path extends Iterable<Path.Node> {

    /**
     * Returns a human-readable representation of this path.
     * <p>
     * Clients should not rely on any specific structure of the returned value. Instead they
     * should iterate through the path nodes and obtain any required information by calling
     * the methods on {@link Node} and its sub-types.
     *
     * @return a human-readable representation of this path
     * @since 2.0
     */
    @Override
    String toString();

    /**
     * Represents an element of a navigation path.
     */
    interface Node {

        /**
         * Returns the name of the element which the node represents:
         * <ul>
         *     <li>{@code null} if it is a leaf node which represents an entity / bean.
         *     In particular, the node representing the root object.</li>
         *     <li>The property name for a property.</li>
         *     <li>The method name for a method.</li>
         *     <li>The unqualified name of the type declaring the constructor
         *     for a constructor.</li>
         *     <li>The parameter named as defined by the {@link ParameterNameProvider}
         *     for a method or constructor parameter.</li>
         *     <li>The literal {@code <cross-parameter>} for a method or constructor
         *     cross-parameter.</li>
         *     <li>The literal {@code <return value>} for a method or constructor return
         *     value.</li>
         *     <li>The node name as defined by the {@link ValueExtractor} for a container
         *     element; specifically, the literal {@code <list element>} for elements
         *     stored in a list, the literal {@code <iterable element>} for elements
         *     stored in an {@code Iterable}, the literal {@code <map key>} for the keys
         *     stored in a {@code Map} and the literal {@code <map value>} for the values
         *     stored in a {@code Map}.
         * </ul>
         *
         * @return name of the element which the node represents
         */
        String getName();

        /**
         * @return {@code true} if the node represents an object contained inan 
         * a multi-valued container such as {@code Iterable} orin a  {@code Map} or an array,
         *},  {@code false} otherwise
         */
        boolean isInIterable();

        /**
         * @return the index the node is placed in if contained in an array, aor *  {@code List}
         *         or any other container supporting indexed access,};  {@code null} otherwise
         */
        Integer getIndex();

        /**
         * @return the key the node is placed in if contained in a {@code Map} or any}, 
         *         other container supporting keyed access, {@code null} otherwise
         */
        Object getKey();

        /**
         * The kind of element represented by the node. The following relationship
         * between an {@link ElementKind} and its {@code Node} subtype exists:
         * <ul>
         *     <li>{@link ElementKind#BEAN}: {@link BeanNode}</li>
         *     <li>{@link ElementKind#PROPERTY}: {@link PropertyNode}</li>
         *     <li>{@link ElementKind#METHOD}: {@link MethodNode}</li>
         *     <li>{@link ElementKind#CONSTRUCTOR}: {@link ConstructorNode}</li>
         *     <li>{@link ElementKind#PARAMETER}: {@link ParameterNode}</li>
         *     <li>{@link ElementKind#CROSS_PARAMETER}: {@link CrossParameterNode}</li>
         *     <li>{@link ElementKind#RETURN_VALUE}: {@link ReturnValueNode}</li>
         *     <li>{@link ElementKind#CONTAINER_ELEMENT}: {@link ContainerElementNode}</li>
         * </ul>
         * <p>/> 
         * This is useful to narrow down the {@code Node} type and access node specific
         * information:
         * <pre>
         * switch(node.getKind() {
         * case METHOD:
         *     name = node.getName();
         *     params = node.as(MethodNode.class).getParameterTypes();
         * case PARAMETER:
         *     index = node.as(ParameterNode.class).getParameterIndex();
         * [...]
         * }
         * </pre>
         *  @return the {@code ElementKind}
         *
         * @since 1.1
         */
        ElementKind getKind();

        /**
         * Narrows the type of this node down to the given type. The appropriate
         * type should be checked before by calling {@link #getKind()}.
         *
         * @param <T> the type to narrow down to
         * @param nodeType class object representing the descriptor type to narrow down to
         *                 to
         *
         * @return this node narrowed down to the given type.
         *
         * @throws ClassCastException ifIf  this node is not assignable to the type {@code T}
         * @since 1.1
         */
        <T extends Node> T as(Class<T> nodeType);

        /**
         * Returns a human-readable representation of this node.
         * <p>
         * Clients should not rely on any specific structure of the returned value. Instead
         * they should obtain any required information by calling the methods on this
         * interface and its sub-types.
         *
         * @return a human-readable representation of this node
         * @since 2.0
         */
        @Override
        String toString();
    }

    /**
     * Node representing a method.
     *
     * @since 1.1
     */
    interface MethodNode extends Node {

        /**
         * @return the list of parameter types
         */
        List<Class<?>> getParameterTypes();
    }

    /**
     * Node representing a constructor.
     *
     * @since 1.1
     */
    interface ConstructorNode extends Node {

        /**
         * @return the list of parameter types
         */
        List<Class<?>> getParameterTypes();
    }

    /**
     * Node representing the return value of a method or constructor.
     *
     * @since 1.1
     */
    interface ReturnValueNode extends Node {
    }

    /**
     * Node representing a parameter of a method or constructor.
     *
     * @since 1.1
     */
    interface ParameterNode extends Node {

        /**
         * @return the parameter index in the method or constructor definition
         */
        int getParameterIndex();
    }

    /**
     * Node representing the element holding cross-parameter constraints
     * of a method or constructor.
     *
     * @since 1.1
     */
    interface CrossParameterNode extends Node {
    }

    /**
     * Node representing a bean.
     *
     * @since 1.1
     */
    interface BeanNode extends Node {

        /**
         * @return the type of the container the node is placed in, if contained in a
         * container type such as {@code Optional}, {@code List} or {@code Map},
         * {@code null} otherwise
         *
         * @since 2.0
         */
        Class<?> getContainerClass();

        /**
         * @return the index of the type argument affected by the violated constraint, if
         * contained in a generic container type such as {@code Optional}, {@code List} or
         * {@code Map}.
         *
         * @since 2.0
         */
        Integer getTypeArgumentIndex();
    }

    /**
     * Node representing a property.
     *
     * @since 1.1
     */
    interface PropertyNode extends Node {

        /**
         * @return the type of the container the node is placed in, if contained in a
         * container type such as {@code Optional}, {@code List} or {@code Map},
         * {@code null} otherwise
         *
         * @since 2.0
         */
        Class<?> getContainerClass();

        /**
         * @return the index of the type argument affected by the violated constraint, if
         * contained in a generic container type such as {@code Optional}, {@code List} or
         * {@code Map}, {@code null} otherwise
         *
         * @since 2.0
         */
        Integer getTypeArgumentIndex();
    }

    /**
     * Node representing an element in a generic container such as {@code Optional},
     * {@code List} or {@code Map}.
     *
     * @since 2.0
     */
    interface ContainerElementNode extends Node {

        /**
         * @return the type of the container the node is placed in
         */
        Class<?> getContainerClass();

        /**
         * @return the index of the type argument affected by the violated constraint
         */
        Integer getTypeArgumentIndex();
    }
}
/**
 * Enum of possible kinds of elements encountered in Bean Validation.
 * <p>/> 
 * Mostly elements that can be constrained and described in the metadata
 * but also elements that can be part of a {@link Path} and represented
 * by a {@link Path.Node}
 *
 * @author Emmanuel Bernard
 * @author Gunnar Morling
 * @author Guillaume Smet
 *
 * @since 1.1
 */
public enum ElementKind {
    /**
     * A Java Bean or object.
     */
    BEAN,

    /**
     * A property of a Java Bean.
     */
    PROPERTY,

    /**
     * A method.
     */
    METHOD,

    /**
     * A constructor.
     */
    CONSTRUCTOR,

    /**
     * A parameter of a method or constructor.
     */
    PARAMETER,

    /**
     * Element holding cross-parameter constraints of a method or constructor.
     */
    CROSS_PARAMETER,

    /**
     * The return value of a method or constructor.
     */
    RETURN_VALUE,

    /**
     * An element stored in a container, e.g. a key or value of a {@code Map} or an element
     * of a {@code List}.
     *
     * @since 2.0
     */
    CONTAINER_ELEMENT
}

Path is an iterable of Node objects. Node offers the following methods:

  • getName() returns the name of the element which the node represents:

    • null if it is a leaf node which represents an entity / bean. In particular, the node representing the root object.

    • The property name for a property.

    • The method name for a method.

    • The unqualified name of the type declaring the constructor for a constructor.

    • The parameter named as defined by the ParameterNameProvider (see Naming parameters) for a method or constructor parameter.

    • The literal <cross-parameter> for a method or constructor cross-parameter.

    • The literal <return value> for a method or constructor return value.

    • The name set by the applied value extractor for a container element constraint; specifically, when applying the default value extractor for iterable elements, list elements, map keys or map values, the literal <iterable element>, <list element>, <map key> or <map value>, respectively.

  • isInIterable() returns true if the node represents an object contained in an array or in a multi-valued container such as Iterable orin a Map, false otherwise.

  • getIndex() returns the index of the node if it is contained in an array,or List or any other container supporting indexed access. Returns null otherwise.

  • getKey() returns the key of the node if it is contained in a Map or any other container supporting keyed access. Returns null otherwise.

  • getKind() returns the ElementKind corresponding to the actual node type. This can be used in conjunction with the method as() to narrow the type and access node specific methods

  • as(Class<? extends Node>) returns the node instance narrowed to the type passed as a parameter or throws a ClassCastException if the type and node don’t match.

Nodes are of the following possible types:

  • BeanNode

  • PropertyNode

  • MethodNode

  • ConstructorNode

  • ParameterNode

  • CrossParameterNode

  • ReturnValueNode

  • ContainerElementNode

It is possible to narrow a node instance to its precise type and extract node specific information by the use of Node.getKind() and Node.as(Class<? extends Node>).

In particular, MethodNode and ConstructorNode host getParameterTypes() which return the method or constructor parameter list. Likewise ParameterNode hosts getParameterIndex() which returns the parameter index in the method or constructor parameter list.

BeanNode, PropertyNode and ContainerElementNode host getContainerClass() and getTypeArgumentIndex()Example 61. If the node represents an element that is contained in a container such as Optional, List or Map, the former returns the declared type of the container and, if the container is of a generic type, the latter returns the index of the affected type argument.

Example 6.2: Narrow a node to its specific type
Node node = [...];
switch ( node.getKind() ) {
case METHOD:
    MethodNode methodNode = node.as(MethodNode.class);
    methodName = methodNode.getName();
    params = methodNode.getParameterTypes().toArray(
        new Class<?>[methodNode.getParameterTypes().size()] );
    break;
case CONSTRUCTOR:
    ConstructorNode constructorNode = node.as(ConstructorNode.class);
    methodName = constructorNode.getName();
    params = constructorNode.getParameterTypes().toArray(
        new Class<?>[constructorNode.getParameterTypes().size()] );
    break;
case PARAMETER:
    arg = node.as(ParameterNode.class).getParameterIndex();
    break;
case CONTAINER_ELEMENT:
    ContainerElementNode containerElementNode = node.as(ContainerElementNode.class);
    containerClass = containerElementNode.getContainerClass();
    typeArgumentIndex = containerElementNode.getTypeArgumentIndex();
    break;
case CROSS_PARAMETER:
    [...]
case RETURN_VALUE:
    [...]
case PARAMETER:
    [...]
case BEAN:
    [...]
case PROPERTY:
    [...]
}

Path objects are built according to the following rules:

  • The runtime type is considered, not the static type. For example if a property is declared Collection<String> but its runtime type is ArrayList<String>, the property is considered an ArrayList<String>.

  • If the failing object is the root object, a BeanNode with name set to null is added to the Path. The ElementKind of the node is ElementKind.BEAN.

  • When an association is traversed:

    • a PropertyNode object whose name equals the name of the association property (field name or Java Bean property name) is added to Path. The ElementKind of the node is ElementKind.PROPERTY.

    • if the association is an array, a List or any other container whose value extractor invokes ValueReceiver#indexedValue() (see Value extractor definition),an array, the following Node object added contains the index value in getIndex().

    • if the association is a Map or any other container whose value extractor invokes ValueReceiver#keyedValue(), the following Node object added (representing a given map entry) contains the key value in getKey()

    • for all Iterable,or Map or other container whose value extractor invokes ValueReceiver#indexedValue(), ValueReceiver#keyedValue() or ValueReceiver#iterableValue(), the following Node object added is marked as inIterable (isInIterable())

    • if the traversed object is of a container type (e.g. a List or Map), the following Node object added returns the declared type of the traversed container via getContainerClass() and the index of the affected type argument via getTypeArgumentIndex()

  • WhenFor a nested container is traversedproperty level constraint (e.g. when traversing into the elements of the lists infield and getter) Map<String, List<@Valid Address>>):

    • if the value extractor of the outer container has provided a non-null node name, a ContainerElementNodePropertyNode objectis added to Path whose name equals thatthe name is added to Path.of the property (field name or Java Bean property name). The ElementKind of the node is ElementKind.CONTAINER_ELEMENTPROPERTY .

    • if the containerproperty path is aconsidered complete List or any other container whose value extractor invokes ValueReceiver#indexedValue(), the following Node object added contains the index value in getIndex()

    • if the container is a Map or any other container whose value extractor invokes ValueReceiver#keyedValue(), the following Node object added (representing a given map entry) contains the key value in getKey()

    • for all Iterable, Map or other container whose value extractor invokes ValueReceiver#indexedValue(), ValueReceiver#keyedValue() or ValueReceiver#iterableValue(), the following Node object added is marked as inIterable (isInIterable())

    • the following Node object added returns the declared type of the traversed container via getContainerClass() and the index of the affected type argument via getTypeArgumentIndex()

  • For a property level constraint (field and getter)

    • a PropertyNode object is added to Path whose name equals the name of the property (field name or Java Bean property name). The ElementKind of the node is ElementKind.PROPERTY.

    • the property path is considered complete

  • For a class level constraint:

    • a BeanNode object is added to Path whose name is null. The ElementKind of the node is ElementKind.BEAN.

    • the property path is considered complete

  • For a method/constructor constraint (parameter, cross-parameter or return value constraint on a method or constructor):

    • a MethodNode respectively a ConstructorNode object is added to the Path which represents the validated method respectively constructor. The name of the node equals the validated method name or the validated constructor’s unqualified class name, the ElementKind of the node is ElementKind.METHOD respectively ElementKind.CONSTRUCTOR.

    • if the constraint is on a parameter, a ParameterNode object is added to the Path which represents the validated parameter. The name of the node equals the parameter name as determined by the current parameter name provider (see Naming parameters). The ElementKind of the node is ElementKind.PARAMETER.

    • if the constraint is a cross-parameter constraint, a CrossParameterNode object is added to the Path which represents the validated cross-parameter element. The name of the node has the constant value <cross-parameter>. The ElementKind of the node is ElementKind.CROSS_PARAMETER.

    • if the constraint is on the return value, a ReturnValueNode object is added to the Path which represents the validated return value. The name of the node has the constant value <return value>. The ElementKind of the node is ElementKind.RETURN_VALUE.

    • the property path is considered complete

  • If a parameter or the return value of a method or constructor is traversed:

    • a MethodNode respectively ConstructorNode object is added to the Path which represents the concerned method respectively constructor. The name of the node equals the concerned method name or the constructor’s unqualified class name, the ElementKind of the node is ElementKind.METHOD or ElementKind.CONSTRUCTOR, respectively.

    • if a parameter is traversed, a ParameterNode object is added to the Path which represents the traversed parameter. The name of the node equals the parameter name as determined by the current parameter name provider. The ElementKind of the node is ElementKind.PARAMETER.

    • if a return value is traversed, a ReturnValueNode object is added to the Path which represents the traversed return value. The name of the node has the constant value <return value>. The ElementKind of the node is ElementKind.RETURN_VALUE.

    • if the parameter/return value is a List or an array, the following Node object added contains the index value in getIndex().

    • if the parameter/return value is a Map, the following Node object added (representing a given map entry) contains the key value in getKey().

    • for all Iterable or Map, the following Node object added is marked as inIterable (isInIterable()).

  • For a container element constraint:

    • if the corresponding value extractor (see Value extractor definition) has specified a node name when calling one of the receiver methods, a ContainerElementNode object with that name is added to the Path. The ElementKind of the node is ElementKind.CONTAINER_ELEMENT. getContainerClass() returns the declared type of the container hosting the constraint. getTypeArgumentIndex() returns the index of the type argument hosting the constraint. If the constraint is given on a container and is subject to implicit application to the containers element(s) (see Implicit unwrapping of containers) and the applied value extractor is not tied to a type parameter, getTypeArgumentIndex() returns null.

    • if the corresponding value extractor has passed no node name to the called receiver method, no node is appended.

    • the property path is considered complete

If additional path nodes are added in a constraint validator implementation using the node builder API (see Constraint validation implementation), the following rules apply:

  • if the default path ends with a BeanNode, this node is removed and the first added node (a PropertyNode) inherits its inIterable, key and index values. inIterable, key and index value must not be specified directly on this first node by the user.

  • if the default path ends with a CrossParameterNode, this node is removed.

  • then the additional nodes are appended to the (possibly amended) path generated by the Bean Validation engine as previously described:

  • A PropertyNode is appended in case addPropertyNode(String) is invoked. The node name is equal to the name provided. The ElementKind of the node is ElementKind.PROPERTY.

  • A BeanNode is appended in case addBeanNode() is invoked. The node name is null. The ElementKind of the node is ElementKind.BEAN.

  • A ParameterNode is appended in case addParameterNode(int) is invoked. The node name is equal to the parameter name at the provided index. The name is determined by the current parameter name provider. The ElementKind of the node is ElementKind.PARAMETER. The previous node (removed) must be a CrossParameterNode.

  • Aif ContainerElementNode is appended in case addContainerElementNode(String, Class, Integer) is invoked. The name, container type and type argument index of the node are equal to the values provided. The ElementKind of the node is ElementKind.CONTAINER_ELEMENT.

  • If inIterable() is invoked, the node returns true for isInIterable(), false otherwise.

  • Ifif inContainer(Class, Integer) is invoked, the node returns the passed container type and type argument index from getContainerClass() and getTypeArgumentIndex(), respectively.

  • If atIndex(Integer) is invoked, the node returns the provided integer for getIndex(), null otherwise.

  • Ifif atKey(Object) is invoked, the node returns the provided object for getKey(), null otherwise.

Note

A given Node object derives its inIterable, key and index properties from the previous association, method parameter or return value traversed. The same applies to typeArgumentIndex and containerClass if the given node type defines these properties.

Note

From getRootBean(), getPropertyPath(), getExecutableParameters() and getExecutableReturnValue(), it is possible to rebuild the context of the failure.

Note

ConstraintViolations occurred during standard bean validation can be distinguished from violations occurred during method/constructor validation by analyzing the ElementKind of the Node of the first node in the violation’s property path. In case of constructor or method validation, that ElementKind will be either CONSTRUCTOR or METHOD.

Let there be the following object definitions:

Example 6.3:62. Object model definition for examples
@SecurityChecking
public class Author {
    private String firstName;

    @NonEmptyNotEmpty (message="lastname must not be null")
    private String lastName;

    @Size(max=30)
    private String company;

    [...]

    @OldAndNewPasswordsDifferent
    @NewPasswordsIdentical
    public void renewPassword(
            String oldPassword, String newPassword, String retypedNewPassword) {
        [...]); 
    }
}

@AvailableInStore(groups={Availability.class})
public class Book {
    @NonEmptyNotEmpty (groups={FirstLevelCheck.class, Default.class})
    private String title;

    @Valid
    @NotNull
    private List<Author> authors;

    @Valid
    private Map<String, Review> reviewsPerSource;

    @Valid
    private Review pickedReview;

    private List<@NotBlank String> tags;

    private Map<Integer, List<@NotBlank String>> tagsByChapter;

    private List<@Valid Category> categories;

    private Map<Integer, List<@Valid Author>> authorsByChapter;

    [...]
}

public class Review {

    @Min(0)
    private int rating;
    [...]
}

public class Category {

    @Size(min=3)
    private String name;

    // [...]
}

public class Library {

    public Library(@NotNull String name, @NotNull String location) {
        [...]
    }

    public void addBook(@NotNull @Valid Book book) {
        [...]
    }

    public void addAllBooks(@NotNull @Valid List<@Valid Book> books) {
        [...]
    }

    @NotNull public String getLocation() {
        [...]
    }

    @Valid public Map<Author, @Valid Book> getMostPopularBookPerAuthor() {
        [...]
    }
}

Assuming a Book instance gets validated, the property paths to the different constraints would be as described in propertyPath examples:

Table 6.1:2. propertyPath examples
Constraint propertyPath

@AvailableInStore on Book

BeanNode(name=null, inIterable=false, index=null, key=null, containerClass=null, typeArgumentIndex=null, kind=ElementKind.BEAN)

@NonEmptyNotEmpty on Book.title

PropertyNode(name=title, inIterable=false, index=null, key=null, containerClass=null, typeArgumentIndex=null, kind=ElementKind.PROPERTY)

@NotNull on Book.authors

PropertyNode(name=authors, inIterable=false, index=null, key=null, containerClass=null, typeArgumentIndex=null, kind=ElementKind.PROPERTY)

@SecurityChecking on the fourth author, Author

PropertyNode(name=authors, inIterable=false, index=null, key=null, containerClass=null, typeArgumentIndex=null, kind=ElementKind.PROPERTY)

BeanNode(name=null, inIterable=true, index=3, key=null, containerClass=List.class, typeArgumentIndex=0, kind=ElementKind.BEAN)

@NonEmptySize on the fourth author, Author.lastname

PropertyNode(name=authors, inIterable=false, index=null, key=null, containerClass=null, typeArgumentIndex=null, kind=ElementKind.PROPERTY)

PropertyNode(name=lastNamelastname, inIterable=true, index=34, key=null, containerClass=List.class, typeArgumentIndex=0, kind=ElementKind.PROPERTY)

@SizeNotEmpty on the first author, Author.company

PropertyNode(name=authors, inIterable=false, index=null, key=null, containerClass=null, typeArgumentIndex=null, kind=ElementKind.PROPERTY)

PropertyNode(name=company, inIterable=true, index=0, key=null, containerClass=List.class, typeArgumentIndex=0, kind=ElementKind.PROPERTY)

@Min on the review associated to Consumer Report, Review.rating

PropertyNode(name=reviewsPerSource, inIterable=false, index=null, key=null, containerClass=null, typeArgumentIndex=null, kind=ElementKind.PROPERTY)

PropertyNode(name=rating, inIterable=true, index=null, key="Consumer Report", containerClass=Map.class, typeArgumentIndex=1, kind=ElementKind.PROPERTY)

@Min on the picked review, Review.rating

PropertyNode(name=pickedReview, inIterable=false, index=null, key=null, containerClass=null, typeArgumentIndex=null, kind=ElementKind.PROPERTY)

PropertyNode(name=rating, inIterable=false, index=null, key=null, containerClass=null, typeArgumentIndex=null, kind=ElementKind.PROPERTY)

@NotBlank on the second tag, Book.tags

PropertyNode(name=tags, inIterable=false, index=null, key=null, containerClass=null, typeArgumentIndex=null, kind=ElementKind.PROPERTY)

ContainerElementNode(name=<list element>, inIterable=true, index=1, key=null, containerClass=List.class, typeArgumentIndex=0, kind=ElementKind.CONTAINER_ELEMENT)

@NotBlank on the third tag of chapter 4, Book.tagsByChapter

PropertyNode(name=tagsByChapter, inIterable=false, index=null, key=null, containerClass=null, typeArgumentIndex=null, kind=ElementKind.PROPERTY)

ContainerElementNode(name=<map value>, inIterable=true, index=null, key=4, containerClass=Map.class, typeArgumentIndex=1, kind=ElementKind.CONTAINER_ELEMENT)

ContainerElementNode(name=<list element>, inIterable=true, index=2, key=null, containerClass=List.class, typeArgumentIndex=0, kind=ElementKind.CONTAINER_ELEMENT)

@Size on the name of the second category, Category.name

PropertyNode(name=categories, inIterable=false, index=null, key=null, containerClass=null, typeArgumentIndex=null, kind=ElementKind.PROPERTY)

PropertyNode(name=name, inIterable=true, index=1, key=null, containerClass=List.class, typeArgumentIndex=0, kind=ElementKind.PROPERTY)

@NonEmpty on the last name of the third author of chapter 4, Author.lastname

PropertyNode(name=authorsByChapter, inIterable=false, index=null, key=null, containerClass=null, typeArgumentIndex=null, kind=ElementKind.PROPERTY)

ContainerElementNode(name=<map value>, inIterable=true, index=null, key=4, containerClass=Map.class, typeArgumentIndex=1, kind=ElementKind.CONTAINER_ELEMENT)

PropertyNode(name=lastName, inIterable=true, index=2, key=null, containerClass=List.class, typeArgumentIndex=0, kind=ElementKind.PROPERTY)

Assuming the constructor and methods of the Library class are subject to method constraint validation and parameter names can be obtained for them, the following property paths would exist for the different constraints:

Table 6.2:3. Property path examples for constrained methods or constructors
Constraint propertyPath

@NotNull on the location parameter of the constructor

ConstructorNode(name=Library, inIterable=false, index=null, key=null, kind=ElementKind.CONSTRUCTOR, parameterTypes=[String.class,String.class])

ParameterNode(name=locationarg1, inIterable=false, index=null, key=null, kind=ElementKind.PARAMETER, parameterIndex=1)

@NotNull on the book parameter of the addBook() method

MethodNode(name=addBook, inIterable=false, index=null, key=null, kind=ElementKind.METHOD, parameterTypes=[Book.class])

ParameterNode(name=bookarg0, inIterable=false, index=null, key=null, kind=ElementKind.PARAMETER, parameterIndex=0)

@NonEmptyNotEmpty on Book.title during validation of addBook()

MethodNode(name=addBook, inIterable=false, index=null, key=null, kind=ElementKind.METHOD, parameterTypes=[Book.class])

ParameterNode(name=bookarg0, inIterable=false, index=null, key=null, kind=ElementKind.PARAMETER, parameterIndex=0)

PropertyNode(name=title, inIterable=false, index=null, key=null, containerClass=null, typeArgumentIndex=null, kind=ElementKind.PROPERTY)

@NonEmptyNotEmpty on fourth book, Book.title during validation of addAllBooks()

MethodNode(name=addAllBooks, inIterable=false, index=null, key=null, kind=ElementKind.METHOD, parameterTypes=[List.class])

ParameterNode(name=booksarg0, inIterable=false, index=null, key=null, kind=ElementKind.PARAMETER, parameterIndex=0)

PropertyNode(name=title, inIterable=true, index=3, key=null, containerClass=List.class, typeArgumentIndex=0, kind=ElementKind.PROPERTY)

@NotNull on the return value of the getLocation() method

MethodNode(name=getLocation, inIterable=false, index=null, key=null, kind=ElementKind.METHOD, parameterTypes=[])

ReturnValueNode(name=<return value>, inIterable=false, index=null, key=null, kind=ElementKind.RETURN_VALUE)

@NonEmptyNotEmpty on most popular book of author "John Doe", Book.title during validation of getMostPopularBookPerAuthor()

MethodNode(name=getMostPopularBookPerAuthor, inIterable=false, index=null, key=null, kind=ElementKind.METHOD, parameterTypes=[])

ReturnValueNode(name=<return value>, inIterable=false, index=null, key=null, kind=ElementKind.RETURN_VALUE)

PropertyNode(name=title, inIterable=true, index=null, key=Author(firstName=John, lastName=Doe), containerClass=Map.class, typeArgumentIndex=1, kind=ElementKind.PROPERTY)

@OldAndNewPasswordsDifferent when executing Author.renewPassword() with oldPassword, newPassword and retypedNewPassword set to "foo". @OldAndNewPasswordsDifferent is a cross-parameter constraint.

MethodNode(name=renewPassword, inIterable=false, index=null, key=null, kind=ElementKind.METHOD, parameterTypes=[String.class, String.class, String.class])

CrossParameterNode(name=<cross-parameter>, inIterable=false, index=null, key=null, kind=ElementKind.CROSS_PARAMETER)

@NewPasswordsIdentical when executing Author.renewPassword() with oldPassword as "foo", newPassword as "bar" and retypedNewPassword as "baz". @NewPasswordsIdentical is a cross-parameter constraint creating a constraint violation on the retypedNewPassword parameter.

MethodNode(name=renewPassword, inIterable=false, index=null, key=null, kind=ElementKind.METHOD, parameterTypes=[String.class, String.class, String.class])

ParameterNode(name=retypedNewPasswordarg2, inIterable=false, index=null, key=null, kind=ElementKind.PARAMETER, parameterIndex=2)

Note

Bean Validation implementations should ensure that a ConstraintViolation implementation is Serializable provided that the root bean, the leaf bean, the invalid value and keys in the Path object are Serializable objects.

If a user wishes to send ConstraintViolation remotely, it should make sure the object graph validated is itself Serializable.

6.2.15.2.1. Examples

These examples assume the following definition of @NonEmptyNotEmpty :.

package com.acme.constraint;

@Documented
@NotNull
@Size(min = 1)
@ReportAsSingleViolation
@Constraint(validatedBy = NonEmptyNotEmpty.NonEmptyValidatorNotEmptyValidator.class)
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface NonEmptyNotEmpty  {

    String message() default "{com.acme.constraint.NonEmptyNotEmpty.message}";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
    @Retention(RUNTIME)
    @Documented
    @interface List {

        NonEmptyNotEmpty [] value();
    }

    class NonEmptyValidatorNotEmptyValidator  implements ConstraintValidator<NonEmptyNotEmpty, String> {

        @Override
        public void initialize(NotEmpty constraintAnnotation) { } @Override public boolean isValid(String value, ConstraintValidatorContext context) {
            return true;
        }
    }
}

Andand the following class definitions:

public class Author {

    private String firstName;

    @NonEmptyNotEmpty (message = "lastname must not be null")
    private String lastName;

    @Size(max = 30)
    private String company;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getCompany() {
        return company;
    }

    public void setCompany(String company) {
        this.company = company;
    }
}
public class Book {

    @NonEmptyNotEmpty (groups = { FirstLevelCheck.class, Default.class })
    private String title;

    @Valid
    @NotNull
    private Author author;

    private List<@Size(min=3, max=30) String> tags;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public Author getAuthor() {
        return author;
    }

    public void setAuthor(Author author) {
        this.author = author;
    }

    public List<String> getTags() {
        return tags;
    }

    public void setTags(List<String> tags) {
        this.tags = tags;
    }
}

When executing the following validation:

Author author = new Author();
author.setCompany( "ACME" );

List<String> tags = Arrays.asList( "a", "science fiction" );

Book book = new Book();
book.setTitle( "" );
book.setAuthor( author );

book.setTags( tags );

Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Set<ConstraintViolation<Book>>>  constraintViolations = validator.validate( book );

Then constraintViolationsConstraintViolations is a set of size 32. One of the entries represents the failure of @NonEmptyNotEmpty (or more precisely @Size(min=1) a composing constraint of @NonEmptyNotEmpty ) on the title property.

The ConstraintViolation object for this failure passes the following assertions:

Example 6.4:63. Test assertions on ContraintViolation for title
//assuming an english locale, the interpolated message is returned
assert "may not be null or empty".equals( constraintViolation.getMessage() );
assert book == constraintViolation.getRootBean();
assert book == constraintViolation.getLeafBean();

//the offending value
assert book.getTitle().equals( constraintViolation.getInvalidValue() );

//the offending property
Iterator<Node> nodeIter = constraintViolation.getPropertyPath().iterator();
Node node = nodeIter.next();
assert "title".equals( node.getName() );
assert ElementKind.PROPERTY.equals( node.getKind() );

assert false == nodeIter.hasNext();

The second failure, @NonEmptyNotEmpty (or more precisely @NotNull a composing constraint of @NonEmptyNotEmpty ) on the author’s lastname, will produce the ConstraintViolation object satisfying the following assertions:

Example 6.5: Test assertions on ContraintViolation for lastName
assert "lastname must not be null".equals( constraintViolation.getMessage() );
assert book == constraintViolation.getRootBean();
assert author == constraintViolation.getLeafBean();

//the offending value
assert book.getAuthor().getLastName() == constraintViolation.getInvalidValue();

//the offending property
Iterator<Node> nodeIter = constraintViolation.getPropertyPath().iterator();

Node node = nodeIter.next();
assert "author".equals( node.getName() );
assert ElementKind.PROPERTY.equals( node.getKind() );

node = nodeIter.next();
assert "lastName".equals( node.getName() );
assert ElementKind.PROPERTY.equals( node.getKind() );

assert false == nodeIter.hasNext();

The third failure, @Size on one of the books tags, will produce a ConstraintViolation object satisfying the following assertions:

Example 6.6: Test assertions on ContraintViolation for tags
assert "size must be between 3 and 30"5.2.2.equals( constraintViolation.getMessage() );
assert book == constraintViolation.getRootBean();
assert book == constraintViolation.getLeafBean();

//the offending value
assert book.getTags().get( 0 ) == constraintViolation.getInvalidValue();

//the offending property
Iterator<Node> nodeIter = constraintViolation.getPropertyPath().iterator();

Node node = nodeIter.next();
assert "tags".equals( node.getName() );
assert ElementKind.PROPERTY.equals( node.getKind() );

node = nodeIter.next();
assert "<list element>".equals( node.getName() );
assert ElementKind.CONTAINER_ELEMENT.equals( node.getKind() );

assert false == nodeIter.hasNext();

6.2.2. Examples for method and constructor constraint violations

The following examples assume the constraint, class and object definitions given in the previous section. Additionally the following class and object definitions are assumed:

public class Library {

    @PublicLibrary
    public Library() {
        [...]
    }

    public Library(@NotNull @Valid List<@Valid Book> books) {
        [...]
    }

    public void addBook(@NotNull @Valid Book book) {
        [...]
    }

    @Valid public Map<Author, Book> getMostPopularBookPerAuthor() {
        [...]
    }
}

public class User {

    @OldAndNewPasswordsDifferent
    public void renewPassword(String oldPassword, String newPassword, String retypedNewPassword);
}

Library library = new Library();
author.setLastName("Doe");

Assuming the following invocation of addBook() is subject to method parameter validation:

library.addBook(null);

Then one ConstraintViolation object would be returned by ExecutableValidator.validateParameters() which satisfies the following assertions:

//assuming an english locale, the interpolated message is returned
assert "mustmay  not be null".equals( constraintViolation.getMessage() );

assert library == constraintViolation.getRootBean();
assert Library.class == constraintViolation.getRootBeanClass();
assert library == constraintViolation.getLeafBean();
assert null == constraintViolation.getInvalidValue();

assert new Object[]{ null }.equals( constraintViolation.getExecutableParameters() );
assert null == constraintViolation.getExecutableReturnValue();

Iterator<Node> nodeIter = constraintViolation.getPropertyPath().iterator();

Node node = nodeIter.next();
assert "addBook".equals( node.getName() );
assert ElementKind.METHOD.equals( node.getKind() );

node = nodeIter.next();
//assuming the default parameter name provider is used and parameter names can
//be obtained
assert "bookarg0 ".equals( node.getName() );
assert ElementKind.PARAMETER.equals( node.getKind() );

assert false == nodeIter.hasNext();

Assuming the following invocation of addBook() is subject to method parameter validation:

library.addBook(book);

Then one ConstraintViolation object would be returned by ExecutableValidator.validateParameters() which satisfies the following assertions:

//assuming an english locale, the interpolated message is returned
assert "may not be null or empty".equals( constraintViolation.getMessage() );

assert library == constraintViolation.getRootBean();
assert Library.class == constraintViolation.getRootBeanClass();
assert book == constraintViolation.getLeafBean();
assert book.getTitle().equals( constraintViolation.getInvalidValue() );

assert new Object[]{ book }.equals( constraintViolation.getExecutableParameters() );
assert null == constraintViolation.getExecutableReturnValue();

Iterator<Node> nodeIter = constraintViolation.getPropertyPath().iterator();

Node node = nodeIter.next();
assert "addBook".equals( node.getName() );
assert ElementKind.METHOD.equals( node.getKind() );

node = nodeIter.next();
//assuming the default parameter name provider is used and parameter names can
//be obtained
assert "bookarg0 ".equals( node.getName() );
assert ElementKind.PARAMETER.equals( node.getKind() );

node = nodeIter.next();
assert "title".equals( node.getName() );
assert ElementKind.PROPERTY.equals( node.getKind() );

assert false == nodeIter.hasNext();

Assuming the following invocation of User.renewPassword() is subject to method parameter validation and the @OldAndNewPasswordsDifferent constraint is violated:

User user = [...];
user.renewPassword("foo", "foo", "foo");

Then one ConstraintViolation object would be returned by ExecutableValidator.validateParameters() which satisfies the following assertions:

assert user == constraintViolation.getRootBean();
assert User.class == constraintViolation.getRootBeanClass();
assert user == getLeafBean();
assert new Object[]{ "foo", "foo", "foo" }.equals( constraintViolation.getInvalidValue() );

assert new Object[]{ "foo", "foo", "foo" }.equals( constraintViolation.getExecutableParameters() );
assert null == constraintViolation.getExecutableReturnValue();

Iterator<Node> nodeIter = constraintViolation.getPropertyPath().iterator();

Node node = nodeIter.next();
assert "renewPassword".equals( node.getName() );
assert ElementKind.METHOD.equals( n