About a year ago we decided to give the Bean Validation API a try and in the beginning we were really exited about the easy to use, annotation based interface. But soon we had to do cross-field validation which lead us to revise our first impression. Unfortunately the Bean validation API offers no out of the box way of validating a field based on the value of another field and since we wanted to stay consistent with the API design we decided to go down the rocky road of writing our own custom class-level validation annotations and custom validators which really is a boring, repetitive chore and involves heavy use of reflection.

Of course boring, repetitive chores really cry for simplification and automation and therefore I soon started doing research on how others solved this problem. In this article I will outline and analyze some of the solutions I came across.

The standard approach

Creating new validation logic involves two steps. A constraint annotation and a validator which contains the validation logic have to be created. To keep things simple we will implement a class-level constraint which can be used to assert that the value of one field is greater than the value of another field of the object to be validated.

This is the annotation definition:

/**
* Compares two of the fields of an object.
*/
@Target({ TYPE, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = GreaterThanValidator.class)
public @interface GreaterThan {

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

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

    String field();

    String greaterThan();

    /**field > greaterThan*/
    Class<? extends Comparator<Object>> comparator();

    @Target({ TYPE, ANNOTATION_TYPE })
    @Retention(RUNTIME)
    @interface List {
        GreaterThan [] value();
    }
}

The “field” and “greaterThan” fields contain the names of the bean properties which will be compared. For this comparison we also need a comparator. Finally, we add a “List” field so that we can use more than one instance of the annotation at the class-level.

This annotation definition – already a lot of work for the humble task of comparing two values – pales in comparison to the validator:

/**
* Validates the GreaterThan constraint
*/
public class GreaterThanValidator implements
ConstraintValidator<GreaterThan, Object> {

    /** field > greaterThan ? */
    private String field;
    private String greaterThan;
    private Comparator<Object> comparator;

    /**
    * This will be called before isValid to initialize the validator
    */
    @Override
    public void initialize(GreaterThan ann) {

        field = ann.field();
        greaterThan = ann.greaterThan();
        Class<? extends Comparator<Object>> comparatorClass = ann.comparator();
        try {
            comparator = comparatorClass.newInstance();
        } catch (Exception e) {
            throw new IllegalArgumentException("Can't instantiate comparator",e);
        }
    }

    /**
    * This will be called to validate an object
    */
    @Override
    public boolean isValid(Object validateThis, ConstraintValidatorContext ctx) {
        if (validateThis == null) {
            throw new IllegalArgumentException("validateThis is null");
        }
        Field fieldObj = null;
        Field greaterThanObj = null;
        // Find getters the properties
        try {
            fieldObj=validateThis.getClass().getDeclaredField(field);
        } catch (Exception e) {
            throw new IllegalArgumentException("Invalid field name",e);
        }
        try {
            greaterThanObj = validateThis.getClass().getDeclaredField(greaterThan);
        } catch (Exception e) {
            throw new IllegalArgumentException("Invalid greaterThan name",e);
        }
        if (fieldObj == null || greaterThanObj == null) {
            throw new IllegalArgumentException("Invalid field names");
        }

        try {
            fieldObj.setAccessible(true);
            greaterThanObj.setAccessible(true);
            // get field values
            Object fieldVal = fieldObj.get(validateThis);
            Object largerThanVal = greaterThanObj.get(validateThis);
            // compare
            return fieldVal == null && largerThanVal == null
            || fieldVal != null && largerThanVal != null
            && comparator.compare(fieldVal, largerThanVal) > 0;
        } catch (Exception e) {
            throw new IllegalArgumentException("Can't validate object", e);
        }

    }

}

A lot of work for a simple greater than comparison and it isn’t even production quality code; you would have to add a few more lines for using it in your projects. Of course you could save some work by pushing some of the details into super classes but there are faster ways to achieve the same results. Another problem with this annotation is that it offers no compile-time type safety. Typos in the field names or supplying the wrong comparator class will lead to runtime errors. The following approach solves some of those problems and comes from a forum discussion on the topic (see http://stackoverflow.com/questions/1972933/cross-field-validation-with-hibernate-validator-jsr-303).

The @AssertTrue approach

The @AssertTrue annotation is one of the annotations of the Bean Validation API which are also applicable to methods. It simply asserts that the return value of the annotated method is true when called during validation. So if we just added a public method called isConstraint1Satisfied and annotated it like so:

public class AssertTrueExample {

public int i=0;
public int j=0;

@AssertTrue(message="Constraint 1 is not satisfied")
public boolean isConstraint1Satisfied()
{
return i==j;
}
}

…we could validate our object without breaking into sweat. Let’s see what the authors of the JSR-303 specification have to say about this.

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. […]

Well, the @AssertTrue solution obviously violates this separation of concerns the Bean Validation API is all about and puts the validation logic into the model class. You might disagree, but in my opinion very often, too little is to be gained by separating those concerns when it comes to internal dependencies. Sometimes it is even harmfull. I will give you one example.

In one of our projects we had to encapsulate the valid parameters for a call to a legacy statistics library into an object. The validity of the parameters for this call is highly dependent on the value of other parameters and there was no way those constraints could have been factored into separate reusable constraint annotations. We created one annotation, one validator and used it once and there is no chance this annotation could ever be reused. The authors of the JSR-303 specification are of the opinion that validation logic in the domain model is often or always clutter, but I would hesitate to call this special kind of validation code clutter. Sure, validation code for an email address and the likes can safely be considered clutter because email addresses are really abundant in most information systems and everyone knows what an email address is and what it should look like; hence the validation logic contains little information for someone reading the code. The validation logic for the parameter class is a totally different topic. It really belongs to the model class, is not reusable and contains a lot of important information. It more or less IS the class and therefore extracting it into an annotation makes little sense. I think the @AssertTrue approach (and also the @ScriptAssert approach) fits this use case much better than any other approach I have come across so far.

The @ScriptAssert approach

Hibernate Validator 4.1 brought us the @ScriptAssert class-level annotation. It is an ingenious application of the Java Scripting API. Instead of writing your custom validation code in java it lets you use one of many supported scripting languages. It more or less shares the strengths and weaknesses of the @AssertTrue approach but is even more concise and has the added advantage of not cluttering the interface of the class with validation methods. This is what the last example would look like if we used the @ScriptAssert annotation:

@ScriptAssert(lang="javascript",script="_this.i==_this.j")
public static class ScriptAssertExample
{

int i=0;
int j=0;

}

The big disadvantage compared to the @AssertTrue approach is of course that using a scripting language comes at the cost of losing compile-time safety.

Conclusion

The most important aspect of the three approaches to cross-field validation considered here is where they put the validation logic; into the model class or into an external validator class. In this article I mainly outlined why it is a bad idea to create a custom validator and annotation every time the Bean Validation API supplied annotations don’t fit your needs. But don’t get me wrong, I am not saying that putting the validation logic into the model class is always a good idea. There are at least two things that have to be considered:

  • Is it unlikely that the validation logic can be reused?
  • Does it contain important information that really belongs in the model class?

I think that if you can answer any of those questions with a ‘yes’ you can really put the validation logic into the model class without having to suffer a bad conscience.

In the next article I will present a hybrid approach based on the strategy pattern. This approach makes it possible to easily implement new reusable constraints without the need for a validator per constraint. It will let you choose where to implement validation logic and allow you to target not only types but also fields.