Java Tip: Hibernate validation in a standalone implementation

Implement a consolidated validation strategy with Hibernate Validator

A good data validation strategy is an important part of every application development project. Being able to consolidate and generalize validation using a proven framework can significantly improve the reliability of your software, especially over time.

Whereas unstructured and uncontrolled validation policies will lead to increased support and maintenance costs, a consolidated validation strategy can significantly minimize the cascade effect of changes to your code base. A validation layer can also be a very useful tool for some types of debugging.

I recently needed to implement a lightweight validation framework for a project. I discovered Hibernate Validator, an open source project from JBoss. The framework impressed me with its simplicity and flexibility, so I am introducing it with this Java Tip. I'll share my experience with setting up and using Hibernate Validator, with simple use cases that demonstrate key features such as declarative annotations, composite validation rules, and selective validation.

Getting started with Hibernate Validator

Hibernate Validator provides a solid foundation for building lightweight, flexible validation code for Java SE and Java EE applications. Hibernate Validator is supported by a number of popular frameworks, but its libraries can also be used in a standalone implementation. Standalone Java SE validation components can become an integral part of any complex heterogeneous server-side application. In order to follow this introduction to using Hibernate Validator to build a standalone component, you will need to have JDK 6 or higher installed. All use cases in the article are built using Validator version 5.0.3. You should download the Hibernate Validator 5.0.x binary distribution package, where directory \hibernate-validator-5.0.x.Final\dist contains all the binaries required for standalone implementation.

Download the source code for the Hibernate Validator demos used in this article.
Created by Victoria Farber for JavaWorld.

Listing 1 shows a fragment of the Ant-built script where all binary dependencies, selected for the standalone implementation, are listed under the manifest section. The metainf section is required if externalized metadata validation will be used in the implementation. As a result of the Ant build the final JAR will reference all dependent Validator JARs through the Class-Path header of the manifest file.

Listing 1. Manifest section of the Ant build with dependencies

...
<manifest>
  <attribute name="Built-By" value="${user.name}" />
  <attribute name="Main-Class" value="com.val.utils.ValidationUtility" />
  <attribute name="Class-Path" value="lib/hibernate-validator-cdi-5.0.3.Final.jar lib/hibernate-validator-5.0.3.Final.jar lib/hibernate-validator-annotation-processor-5.0.3.Final.jar lib/validation-api-1.1.0.Final.jar lib/classmate-1.0.0.jar lib/javax.el-2.2.4.jar lib/javax.el-api-2.2.4.jar lib/jboss-logging-3.1.1.GA.jar lib/log4j-1.2.17.jar ." />
  </manifest>
<metainf dir="${workspace_project_path}/Meta-inf" includes="*.xml" />
...

Declarative annotation and constraint definition

Hibernate Validator 5.0.x is the open source reference implementation of JSR 349: Bean Validation 1.1. Declarative annotation and constraint definition are two highlights of the updated Bean Validation 1.1 framework. Expressing validation rules through a declarative syntax greatly improves the readability of the code, as shown in Listing 2.

Listing 2. Declarative annotations in Hibernate Validator

public class Address {
	@NotBlank
	private String addresseeName;
	...
	@NotBlank
	@Pattern(regexp = "[A-Za-z]\\d[A-Za-z]\\s?\\d[A-Za-z]\\d", message = "Postal code validation failed.")
	private String postalCode;
	@NotBlank
	private String municipality;
	@NotBlank
	@Pattern(regexp = "AB|BC|MB|NB|NL|NT|NS|NU|ON|PE|QC|SK|YT", message = "Province code validation failed.")
	private String province;
	...

Declarative validation on method parameters clearly define preconditions, improving the overall readability of the code. Business rules are much easier to digest because you only need to look at field, method, and class annotations. The declarative style removes the need to build a model of execution, consider state transitions, and analyze post-conditions and preconditions while learning the code.

Constraints can be applied to a composition of objects by using @Valid annotation. In Listing 2, @Valid is used to apply validation to a graph of dependent beans, enforcing simple validation rules and complex dependency constraints.

Listing 3. Validation rules applied to a composition of objects

abstract public class LetterTemplate {
	...
	@NotEmpty( groups = LetterTemplateChecks.class, 
	payload = ValidationSeverity.Error.class )
	@Valid
	private List<Paragraph> content;
	...	

Supported validation targets are not restricted to data only: it is possible to apply constraints to bean properties get methods, method parameters, and constructors.

Composite validation rules

Readability and flexibility don't always work nicely together in source code; in fact they often compete. Hibernate Validator demonstrates a setup where these two characteristics can actually complement each other. For example, Hibernate Validator supports compositions of annotations. The framework supports compositions of constraints and provides syntactical constructs representing OR, AND, and ALL_FALSE validation semantics. The OR composition offers stereotypes to skip validation on empty or null fields and is helpful when there is a need to declare validation for incomplete or optional data.

In Listing 4, Hibernate Validator's @Pattern annotation is converted into an optional validation rule using annotation composition syntax.

Listing 4. Custom annotation declaration for defining OR type constraints (null or numeric field that cannot start with 0)

@ConstraintComposition(CompositionType.OR)
@Null(message = "")
@Pattern(regexp = "[1-9]\\d*")
@Target( { ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {})
@ReportAsSingleViolation
@Documented
public @interface NullOrNumber {
	String message() default "Validation for an optional numeric field failed.";
	Class<?>[] groups() default {};
	Class<? extends Payload>[] payload() default {  };
}

Listing 5 demonstrates the use of the custom composite annotation on the optional postal box property of the Address object.

Listing 5. Declaring a validation rule where the property can be either null or a number

public class Address {
	...	
	// Optional field defining PO box
	@NullOrNumber
	private String poBoxNumber;
	...

Classifying validation rules

Hibernate Validator lets developers classify validation rules. This means, for example, that you could associate a different runtime strategy with varying levels of failure. The framework supports an annotation payload attribute, which can be used in combination with Java marker interfaces to classify validation rules. You can also link the severity of validation failures with different levels of logging severity, where the logging strategy is configurable at runtime. Consider the simplest possible classification, where the validation rules are associated with non-recoverable errors or warnings.

Listing 6. Marker payload interfaces defined for classification

public class ValidationSeverity {
	public interface Error extends Payload {
	}
	public interface Warning extends Payload {
	}
}

In Listing 6, the ValidationSeverity interface class is used as a parameter in a validation-rule declaration. You could use the same technique for custom annotations:

Listing 7. Marking validation constraints as errors or warnings

public class MailLetterTemplate extends LetterTemplate {
	@NotNull ( payload = ValidationSeverity.Warning.class)
	private Address returnAddress;
	@NotNull (payload = ValidationSeverity.Error.class)
	private Address recipientAddress;
	...

In Listing 8, warning-level validation failures are logged at the info level, while errors are logged at the error level.

Listing 8. Associating logging level with validation errors or warnings

for (ConstraintViolation<MailLetterTemplate> error : constraintViolations) {
	if (error.getConstraintDescriptor().getPayload().iterator().next()
	.equals(ValidationSeverity.Warning.class)) {
		StmtValLog.getInstance().info("Property "
			+ error.getPropertyPath().toString()
			+ " runtime value " + error.getInvalidValue()
			+ ". Warning level constraint violation: "
			+ error.getMessage());
	}
	if (error.getConstraintDescriptor().getPayload().iterator().next()
	.equals(ValidationSeverity.Error.class)) {
		StmtValLog.getInstance().error("Property "
			+ error.getPropertyPath().toString()
			+ " runtime value " + error.getInvalidValue()
			+ ". Error level constraint violation: "
			+ error.getMessage());
	}
}

Selective validation and the marker interface technique

Hibernate Validator also lets you apply validation logic selectively at runtime, adding another dimension of flexibility. This technique is helpful if the state of the system and underlying object structures goes through a well-defined set of transitions. For example, in a class hierarchy where derived classes can be in an incomplete state, while base class state has to be complete every class can be associated with a different level of validation.

Use the (groups) marker interface technique to separate validation rules into different categories. In this technique, a marker interface class is passed as a groups parameter to an annotation declaration.

Listing 9. Marker interface declaration and usage in two classes

public interface MailLetterTemplateChecks {}
...
public interface EmailLetterTemplateChecks {}
...
final public class MailLetterTemplate extends LetterTemplate {
@NullOrNotBlank (groups = MailLetterTemplateChecks.class, payload = ValidationSeverity.Warning.class)
private Address returnAddress;
@NotNull (groups = MailLetterTemplateChecks.class, payload = ValidationSeverity.Error.class)
private Address recipientAddress;
final public class EmailLetterTemplate extends LetterTemplate {
private Image logo; 
@NotBlank ( groups = EmailLetterTemplateChecks.class, payload = ValidationSeverity.Error.class )
private String subject; 
@Email ( groups = EmailLetterTemplateChecks.class, payload = ValidationSeverity.Error.class )
private String recipientEmail;
...

If you're doing validation programmatically, the validate method of the Validator class accepts a list of marker interface classes as parameters, defining the scope of a selective validation. By default, all validation rules belong to a default validation group, which is designated by Default.class.

In Listing 10, validation is restricted to rules marked by two classes: LetterTemplateChecks and EmailLetterTemplateChecks.

Listing 10. Selective validation of an email object

EmailLetterTemplate email = new EmailLetterTemplate("slaval@infosystems.com");
email.setSubject("Renewal letter");
email.setContent(new ArrayList<Paragraph>());
email.setSignature("Serge Laval");
Set<ConstraintViolation<EmailLetterTemplate>> constraintViolations =
	validator.validate(email, LetterTemplateChecks.class, 	EmailLetterTemplateChecks.class);
for(ConstraintViolation<EmailLetterTemplate> errors: constraintViolations)
       	System.out.println("Property " + errors.getPropertyPath().toString()  + " runtime 	value " + errors.getInvalidValue() + ". Constraint violation: " + 	errors.getMessage());
}

Selective validation can be used as a powerful debugging technique, where only a subset of the object hierarchy has to be verified at a given time.

Externalized metadata validation

In addition to declarative annotation-based validation, the Bean Validation 1.1 specification supports a validation metadata model and programmatic validation APIs. The validation metadata model can be used as an effective control mechanism at runtime without the need to rebuild code. A variety of control mechanisms can be employed in controlling validation policies at runtime, ranging from disabling programmatically defined validation constraints to complimenting programmatically defined rules. Metadata configuration has several advanced options available. See the Hibernate Validator documentation for the complete set of options.

In order to enable metadata validation, an validation.xml file has to be available on the classpath. Constraint-mapping fields refer to XML descriptors where application-specific constraints are defined.

Listing 11. A Hibernate configuration file, validation.xml

<config:validation-config version="1.1"
	xmlns:config="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 file ...">
	<config:constraint-mapping>stmnts_declarative_validation.xml</config:constraint-mapping>
	<config:property name="hibernate.validator.fail_fast">false</config:property>
</config:validation-config>
1 2 Page
Join the discussion
Be the first to comment on this article. Our Commenting Policies
See more