Bean Validation

The Bean Validation API is introduced with the Java™ Enterprise Edition 6 platform as a standard mechanism to validate JavaBeans in all layers of an application, including presentation, business, and data access.

Before the Bean Validation specification, JavaBeans were validated in each layer. To prevent the re-implementation of validations at each layer, developers bundled validations directly into their classes or copied validation code, which was often cluttered. Having one implementation that is common to all layers of the application simplifies the developers work and saves time.

The Bean Validation specification defines a metadata model and an API that are used to validate JavaBeans for data integrity. The metadata source is the constraint annotations defined that can be overridden and extended using XML validation descriptors. The set of APIs provides an ease of use programming model allowing any application layer to use the same set of validation constraints. Validation constraints are used to check the value of annotated fields, methods, and types to ensure that they adhere to the defined constraint.

Constraints can be built in or user-defined. Several built-in annotations are available in the javax.validation.constraints package. They are used to define regular constraint definitions and for composing constraints. See the information on Bean Validation built-in constraints for a list of the built-in constraints. For more details about the Bean Validation metadata model and APIs, see the JSR 349 Bean Validation specification document.

The following example is a simple Enterprise JavaBeans (EJB) class that is decorated with built-in constraint annotations.

public class Home  {
   @Size(Max=20)
    String builder; 
    @NotNull @Size(Max=20)
    String address;

    public String getAddress() {
         return address;
    }

    public String getBuilder() {
         return address;
    }
    public String setAddress(String newAddress) {
         return address = newAddress;
    }

    public String setBuilder(String newBuilder) {
         return builder = newBuilder; 
    }
}

The @Size annotations on builder and address specify that the string value assigned should not be greater 20 characters. The @NotNull annotation on address indicates that it cannot be null. When the Home object is validated, the builder and address values are passed to the validator class defined for the @Size annotation. The address value is also be passed to the @NotNull validator class. The validator classes handle checking the values for the proper constraints and if any constraint fails validation, a ConstraintViolation object is created, and is returned in a set to the caller validating the Home object.

Validation APIs

The javax.validation package contains the bean validation APIs that describe how to programmatically validate JavaBeans.

ConstraintViolation is the class describing a single constraint failure. A set of ConstraintViolation classes is returned for an object validation. The constraint violation also exposes a human readable message describing the violation.

ValidationException are raised if a failure happens during validation.

The Validator interface is the main validation API and a Validator instance is the object that is able to validate the values of the Java object fields, methods, and types. The bootstrapping API is the mechanism used to get access to a ValidatorFactory that is used to create a Validator instance. For applications deployed on the product, bootstrapping is done automatically. There are two ways for applications to get the validator or the ValidatorFactory. One way is injection, for example, using the @Resource annotation, and the other way is the java: lookup.

The following example uses injection to obtain a ValidatorFactory and a Validator:
@Resource ValidatorFactory _validatorFactory;
@Resource Validator _validator;    
Attention: When using @Resource to obtain a Validator or ValidatorFactory, the authenticationType and shareable elements must not be specified.
The following example uses JNDI to obtain a ValidatorFactory and a Validator:
ValidatorFactory validatorFactory = (ValidatorFactory)context.lookup("java:comp/ValidatorFactory");
Validator validator = (Validator)context.lookup("java:comp/Validator");

Constraint metadata request APIs

The metadata APIs support tool providers, provides integration with other frameworks, libraries, and Java Platform, Enterprise Edition technologies. The metadata repository of object constraints is accessed through the Validator instance of a given class.

XML deployment descriptors

Besides declaring constraints in annotations, support exists for using XML to declare your constraints.

The validation XML description is composed of two kinds of xml files. The META-INF/validation.xml file describes the bean validation configuration for the module. The other XML file type describes constraints declarations and closely matches the annotations declaration method. By default, all constraint declarations expressed through annotations are ignored for classes described in XML. It is possible to force validation to use both the annotations and the XML constraint declarations by using the ignore-annotation="false" setting on the bean. The product ensures that application modules deployed containing a validation.xml file and constraints defined in XML files are isolated from other module validation.xml and constraint files by creating validator instances specific to the module containing the XML descriptors.

Advanced bean validation concepts

The Bean Validation API provides a set of built-in constraints and an interface that enables you to declare custom constraints. This is accomplished by creating constraint annotations and declaring an annotation on a bean type, field, or property. Composing constraints is also done by declaring the constraint on another constraint definition.

The following example shows creating a CommentChecker constraint that is defined to ensure a comment string field is not null. The comment text is enclosed by brackets, such as [text].

package com.my.company;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import javax.validation.Constraint;
import javax.validation.Payload;
    
@Documented
@Constraint(validatedBy = CommentValidator.class)
@Target({ METHOD, FIELD })
@Retention(RUNTIME)
public @interface CommentChecker {
       String message() default "The comment is not valid.";
       Class<?>[] groups() default {};
       Class<? extends Payload>[] payload() default {};
     	…} 
The next example shows the constraint validator that handles validating elements with the @CommentChecker annotation. The constraint validator implements the ConstraintValidator interface provided by the Bean Validation API.
package com.my.company;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class CommentValidator implements ConstraintValidator<CommentChecker, String> {
    public void initialize(CommentChecker arg0) {       
    }
    public boolean isValid(String comment, ConstraintValidatorContext context) {
        if (comment == null) {
            // Null comment is not allowed, fail the constraint. 
            return false;
        }
        if (!comment.contains("[") && !comment.contains("]")) {
            // Can't find any open or close brackets, fail the constraint
            return false;
        }
        // Ignore leading and trailing spaces
        String trimmedComment = comment.trim();
        return // validate '[' prefix condition
               trimmedComment.charAt(0) == '[' && 
               // validate ']' suffix condition
               trimmedComment.charAt(trimmedComment.length()!-1) == ']';
    }
}
After the @CommentChecker is defined, it can be used to ensure that the comment string field is a valid comment based on the CommentValidator isValid() implementation. The following example shows the use of the @CommentChecker constraint. When the myChecker bean is validated, the comment string is validated by the CommentValidator class to ensure the constraints defined are met.
package com.my.company;
public myChecker {  

    @CommentChecker
    String comment = null; 
    ...
}

The product provides a default bean validation provider, but you might want to use an alternate provider in your application. You can choose an alternate provider programmatically by calling the javax.validation.Validation.byProvider() method to create a validation factory. Alternatively, you can choose an alternate provider declaratively by specifying the <default-provider/> element in the validation.xml file. If you want to ensure that the alternate provider classes do not conflict with the default provider, set the server or application class loader order to the value, Classes loaded with local class loader first (parent last). See the class loading documentation for additional information on how to set this setting.

The Bean Validation specification indicates that if more than one validation.xml file is found in the class path, a ValidationException occurs. However, WebSphere® Application Server supports an environment where multiple teams develop modules that are assembled and deployed into the Application Server together. In this environment, all EJB modules within an application are loaded with the same class loader and it is possible to configure the application class loaders so that all EJB and web archive (WAR) modules are loaded by a single class loader. Because of this, the product provides support for multiple validation.xml files in the same class path.

When an application using bean validation and XML descriptors contains multiple EJB modules and web modules, each validation.xml file is associated with a validation factory that is specific to that module. In this environment, any constraint-mapping elements that are defined are only looked up in the module where the validation.xml file is defined. For example, if an EJB module building.jar contains a META-INF/validation.xml file and the validation.xml file defined the following constraints, both the META-INF/constraints-house.xml and META-INF/constraints-rooms.xml files must also be located in the building.jar file:
<constraint-mapping>META-INF/constraints-house.xml</constaint-mapping>
<constraint-mapping>META-INF/constraints-rooms.xml</constraint-mapping>

The exception to this behavior is when all bean validation constraints classes and configuration are visible to all application modules. In a case where a single validation.xml file is defined in an EAR file, and no other validation.xml files are visible in a module's class path, any module that creates a validator factory or validator will use the validation.xml file that is defined in the EAR file. This makes it possible for other modules to create a validator factory that uses the validation.xml file of another module, if the class path has been configured so that both modules are visible on the same class path and only one validation.xml file is visible to those modules.

For a more detailed understanding about the Bean Validation APIs and metadata see the JSR 349 Bean Validation specification document.