About
Side Projects
Blog
2022-10-30

Validation in Java with a Custom Holder Class

Bean Validation

The JSR-303 “Bean Validation” specification (2007) defines a means of validating data within a Java application. Typically one would use a library such as Hibernate Validator to actually implement the specification. An example of usage on a model class within a project might be something like;

public class Book {
    
    @NotNull
    @Size(min = 2, max = 128)
    private String title;
 
    ...
}

The JSR 303 standard defines a number of very commonly used annotations such as @NotNull and others such as @Email are provided by third-party libraries. Common Java infrastructure such as Hibernate (ORM) or SpringMVC (APIs) typically use this mechanism to validate data.

Holder Classes

From time to time, data in a software project has to be wrapped in a structure called a “holder” which adds additional information or context to some other data. A good example is the Optional class from the core Java libraries which denotes if the value wrapped is present or not and provides some functional-style methods to act on the data. Here is an example of the Optional class in action;

Optional.ofNullable(person)
  .map(Person::getAge)
  .filter(age -> age > 10)
  .orElse(0);

Validating the Value in a Holder

Assuming a custom holder class Approved, the question might arise how you can use data wrapped in the custom holder with JSR 303 validations. This, it transpires, is not too tricky as you can write an instance of javax.validation.valueextraction.ValueExtractor that is able to extract the value to be validated from the holder; see the comments on the ValueExtractor interface for further guidance.

A project’s build product is able to incorporate a classpath resource at /META-INF/validation.xml which can contain <value-extractor>...</value-extractor> sections defining any number of ValueExtractor implementations.

The model class can now express the validations on the holder using annotations in the following manner;

public class Book {

    private Approved<@NotNull @Size(min = 2, max = 128) String> approvedTitle;
 
    ...
}

Validating without a ValueExtractor - A Work-Around

When working with some code-generation tools it might be difficult or impossible to manipulate the output code to yield the arrangement with the annotations as shown above. In this situation the best that might be able to be achieved is;

public class Book {

    @NotNull
    @Size(min = 2, max = 128)
    private Approved<String> approvedTitle;
 
    ...
}

There does exist a work-around in this case, but it is more arduous and involves re-implementing the validation logic. Following the example code above, for each validation annotation that is required (eg: @NotNull), it is necessary to implement a concrete instance of ConstraintValidator<NotNull, Approved<?>> such as NotNullApprovedConstraintValidator. The actual validation logic must then be implemented in the NotNullApprovedConstraintValidator class.

To register these custom ConstaintValidator implementations with the validation engine implementation, the project can define a classpath resource constraint-mappings.xml containing items such as;

<constraint-definition annotation="javax.validation.constraints.NotNull">
    <validated-by include-existing-validators="true">
        <value>nz.co.lindesay.NotNullApprovedConstraintValidator</value>
    </validated-by>
</constraint-definition>

This resource, in turn, should be referenced from the /META-INF/validation.xml resource;

<constraint-mapping>nz/co/lindesay/constraint-mappings.xml</constraint-mapping>

This work-around is not ideal, but does provide a path forward for this situation.