Sunday, May 8, 2011

Bookmark and Share

Note: This article originally appeared on in.relation.to as part of the Seam Module Spotlight series. In order to spread the word on Seam Validation I thought it might be a good idea to post it in this blog, too.

Seam Validation is a relatively new Seam module which provides an integration between Hibernate Validator which is the reference implementation of the Bean Validation standard (JSR 303) and CDI. The module's functionality falls into two areas:

  • Enhanced dependency injection services for validators, validator factories and constraint validators
  • Automatic validation of method parameters and return values based on Hibernate Validator's method validation API

Let's take a closer look at these features in the following.

Dependency injection services

The Bean Validation API is an integral part of the Java EE platform. So when running on a Java EE 6 compatible application server such as JBoss AS 6 you can easily retrieve Validator and ValidatorFactory instances via dependency injection.

Unfortunately this is not working out of the box when running in non-EE environments such as plain servlet containers. But don't worry, Seam Validation comes to the rescue. By adding the module as dependency to your project, validators and validator factories can be obtained using @Inject:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Foo {

    @Inject
    private Validator validator;

    public void doSomething() {

        Bar bar = new Bar();

        Set<ConstraintViolation<Bar>> constraintViolations = validator.validate(bar);
        //examine the violations ...
    }
}

But Seam Validation goes one step further and enables dependency injection also within constraint validator implementations. For instance we might have a custom constraint @ValidLicensePlate which is used to ensure that a car has a license plate which is properly registered, still valid etc.:

1
2
3
4
5
6
7
public class Car {

    @ValidLicensePlate
    private String licensePlate;

    //constructor, getter, setter etc.
}

In order to perform the required checks the constraint's validator invokes some sort of registration service (note: accessing external services can potentially be an expensive operation, so it may be a good idea to put the constraint into a separate validation group, but that's out of scope for this article). The Seam Validation module allows to inject this service into the validator as into any other CDI bean:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ValidLicensePlateValidator implements ConstraintValidator<ValidLicensePlate, String> {

    @Inject
    private RegistrationService registrationService;

    @Override
    public void initialize(ValidLicensePlate constraintAnnotation) {
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {

        if (value == null) {
            return true;
        }

        //delegate to registration service
        return registrationService.isValid(value);
    }
}

There is just on more step required before the @ValidLicensePlate constraint can be used: We have to register Seam Validation's constraint validator factory with the Bean Validation provider. This happens by putting a file named validation.xml with the following content into the META-INF folder of the application:

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="UTF-8"?>
<validation-config 
    xmlns="http://jboss.org/xml/ns/javax/validation/configuration"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://jboss.org/xml/ns/javax/validation/configuration validation-configuration-1.0.xsd">

    <constraint-validator-factory>
        org.jboss.seam.validation.InjectingConstraintValidatorFactory
    </constraint-validator-factory>
</validation-config>

Method validation

The other main feature of Seam Validation besides dependency injection is the automatic validation of method parameters and return values based on the API for method validation introduced with Hibernate Validator 4.2 (note: at the time of writing Hibernate Validator 4.2 is released as Beta version, but no major changes are expected until the final release).

This API provides an easy-to-use facility for the "Programming by Contract" approach to program design based on the concepts defined by the Bean Validation API. That means that any Bean Validation constraint can be used to describe

  • the preconditions that must be met before a method may be invoked (by annotating method parameters with constraints) and
  • the postconditions that are guaranteed after a method invocation returns (by annotating methods).

Things should become clearer with an example:

1
2
3
4
5
6
7
8
9
@AutoValidating
public class RentalStation {

    @Valid
    public Car rentCar(@NotNull CustomerPK customerPK, @NotNull @Future Date startDate, @Min(1) int durationInDays) {

        //highly complex car rental business logic ...
    }
}

Here the following pre- and postconditions are applied onto the rentCar() method:

  • The customer's primary key may not be null
  • The rental start date must not be null and must be in the future
  • The rental duration must be at least one day
  • The returned object must be valid with respect to all constraints of the Car class

By annotating the class RentalStation with @AutoValidating, all these constraints are automatically validated by a method interceptor whenever the rentCar() method is invoked. If any of the constraints can't be validated successfully, a runtime exception describing the occurred constraint violation(s) is thrown.

This approach has several advantages over traditional parameter and return value checking:

  • The checks don't have to be performed by hand. This results in less boiler-plate code to write (and to read).
  • A method's pre- and postconditions are part of its API and generated documentation. It's not necessary to add comments such as "returns never null", as this information will automatically be put into the generated JavaDoc. This results in less redundancies and hence in less potential inconsistencies between implementation and documentation.

Future steps

This concludes our short tour through the Seam Validation module. If you feel like trying out the module yourself, the module home page is the right starting point. You might also be interested in the module's reference guide, and in case you found a bug or want to create a feature request the module's JIRA instance is the place to go.

Possible future developments of Seam Validation include a closer integration with other Seam 3 modules, the possibility to chose the validation groups to be used for method validation and the evaluation of standard bean constraints upon method validation.