Sunday, January 16, 2011

Bookmark and Share

Earlier this week the first beta version of Hibernate Validator 4.2 was released. Hardy's announcement on in.relation.to describes in detail what's new in this release, so be sure to check it out.

One of the new features I'm really excited about is method validation, which I therefore would like to discuss in more detail in the following.

What's it about?

Up to now constraint annotations were only supported at fields or at getter methods for property constraints and at type declarations for class-level constraints.

With HV 4.2 it is now also possible to use constraint annotations for a programming style known as "Programming by Contract". More specifically this means that any Bean Validation constraints (built-in as well as custom constraints) can be used to specify

  • any preconditions that must be met before a method invocation (by annotating method parameters with constraints) and
  • any postconditions that are guaranteed after a method invocation (by annotating methods)

As example let's consider the following class from a fictitious video rental application:

1
2
3
4
5
6
7
8
9
public class MovieRepository {

    public @NotNull Set<Movie> findMoviesByTitle( 
        @NotNull @Size(min = 3) String title) {

        //search movie in database ...
    }   

}

The following pre- and postconditions will hold here (provided the method validation is triggered automatically by some integration layer, see below):

  • The title parameter is guaranteed to be not null and at least 3 characters long
  • The return set of movies is guaranteed to be not null

Compared to traditional ways of parameter or result value checking this approach has two big advantages:

  • These checks don't have to be performed manually, which results in less code to write and maintain. You'll never again have to throw IllegalArgumentExceptions or similar by hand.
  • The pre- and postconditions of a method don't have to be expressed again in the method's JavaDoc, since any of it's annotations will automatically be included in the generated JavaDoc. This means less redundancy which reduces the chance of inconsistencies between implementation and comments.

Method validation is not restricted to constraints related to parameters or to return values themselves. Using the @Valid annotation it is also possible to recursively validate parameters or return values:

1
2
3
4
5
6
7
8
public class UserRepository {

    public void persistUser(@NotNull @Valid User user) {

        //persist user in database ...
    }   

}

In this example not only the @NotNull constraint at the user parameter would be checked, but also any constraints defined at the User type. As known from standard Bean Validation the @Valid annotation could also be applied to array-, Iterable- and Map-typed parameters or return values.

The MethodValidator interface

For the purposes of method validation Hibernate Validator defines the new interface MethodValidator (any type or method names are working titles and might change until HV 4.2 is final), which provides methods for parameter and return value validation:

1
2
3
4
5
6
7
8
9
10
11
12
13
public interface MethodValidator {

    public <T> Set<MethodConstraintViolation<T>> validateParameter(
        T object, Method method, Object parameterValue, 
        int parameterIndex, Class<?>... groups);

    public <T> Set<MethodConstraintViolation<T>> validateParameters(
        T object, Method method, Object[] parameterValues, Class<?>... groups);

    public <T> Set<MethodConstraintViolation<T>> validateReturnValue(
        T object, Method method, Object returnValue, Class<?>... groups);

}

To get hold of a method validator, unwrap an instance of HV's Validator implementation as follows:

1
2
3
4
5
MethodValidator validator = Validation.byProvider( HibernateValidator.class )
    .configure()
    .buildValidatorFactory()
    .getValidator()
    .unwrap( MethodValidator.class );

All of MethodValidator's methods return a set of MethodConstraintValidations which describe in detail any constraint violations that occurred during validation. MethodConstraintValidation extends the standard ConstraintViolation and provides additional information such as method and parameter causing a constraint violation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public interface MethodConstraintViolation<T> extends ConstraintViolation<T> {

    public static enum Kind {

        PARAMETER,

        RETURN_VALUE
    }

    Method getMethod();

    Integer getParameterIndex();

    String getParameterName();

    Kind getKind();
}

Unfortunately the byte code of a Java program normally doesn't contain any information on parameter names, therefore getParameterName() for now returns synthetic names such as arg0 etc. For a future release we are investigating approaches for parameter naming, e.g. by leveraging JSR 330's @Named annotation. I created a JIRA issue for this, so if you have any feedback on this just add a comment to the ticket.

Triggering method validation

Hibernate Validator itself only provides the engine for method validation but it does not deal with triggering such a validation.

Instead this is typically done using frameworks/APIs which provide AOP or method interception services such as AspectJ or CDI. But also plain Java dynamic proxies can basically do the trick.

Typically such a validation interceptor does the following:

  • intercept each method call to be validated,
  • validate the invocation's parameter values,
  • proceed with the method invocation and
  • finally validate the invocation's return value.

If any of the two validation steps yields one ore more constraint violations the interceptor typically throws a MethodConstraintViolationException containing the constraint violations that have occurred.

Trying it out yourself

To see whether method validation works out as intended I started a project at GitHub called methodvalidation-integration which provides validation handlers for some interception technologies popular these days.

Currently there are implementations for CDI as well as Google Guice and an InvocationHandler which can be used with JDK dynamic proxies. I also plan to add support for Spring AOP/AspectJ when time permits.

To try method validation out yourself just get the bits from GitHub and have a look at the test in each of the sub-projects. Just give it a try and let me know how everything works.

Future steps

This post introduced the new method validation feature which will be part of Hibernate Validator 4.2. The first beta release contains the basic implementations of this, so we are more than interested in you feedback on this. Please report any bugs in HV's JIRA instance, any questions or ideas for improvement are also highly welcome in HV's forum.

For HV 4.2 Beta2 we plan to add documentation on the method validation feature to HV's reference guide which will also describe some advanced topics such as using method validation with group sequences etc. We will also add support for method level constraints to the constraint meta-data API.

Two further ideas scheduled for after 4.2 are providing the ability to specify method level constraints using XML and using the programmatic constraint configuration API.