Jun 30, 2012

Writing centralized validation using Java Annotation Part 1

The validation component in any application is generally scattered on different layers, though it is not a good practice. On presentation tier we have JS validations and on JSP/ Servlet side we use some POJO stuff. Again if we reach at persistence layer we have hibernate validation or any other db validations. As it is described here and we can realize. Many times validations has been performed at each class level without any code reuse.

The issue is in the architecture. We need to design a clear cut architecture for validation component. We have to identify entities to be validated, kind of business rules to be applied etc. Some entity has to be validated differently for different groups. e.g. User entity - when we receive request we see for values other than id, but while inserting into db we validate id. Similarly multiple flows use the same entity differently. Hence centralizing validation component is not again a simple job.

Here in this series, I will explain you how to achieve the goal at certain extent using Java annotation and hibernate validation. The mechanism may be different for different application.

Let's start with a centralized validator class using annotation

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;

// default validator is required as per JSR303. We have hibernate validator jar for the same.
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();

Set<ConstraintViolation<Object>> constraintViolations = validator.validate(targetObject);

if (constraintViolations.size() > 0) {
for (ConstraintViolation<Object> constraintViolation : constraintViolations) {
// Actions to be taken when validation fails
throw new RuntimeException(constraintViolation.getMessage());
}
}
ValidatorFactory is a factory class found in the implementation jar. We hibernate validation. You can refer Java Validation API. It searches for classes which implements
javax.validation.ConstraintValidator.
Here is an example of such a validator which validates a POJO say it Task

package com.avid.validation.validator;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import com.avid.transfer.Task;
import com.avid.validation.annotation.ValidateTask;

public class TaskValidator implements ConstraintValidator<ValidateTask, Task> {

@Override
public void initialize(ValidateTask validateTask) {}

@Override
public boolean isValid(Task task, ConstraintValidatorContext validationContext) {
if(task == null || task.getId() == -1) {
validationContext.disableDefaultConstraintViolation();
validationContext.buildConstraintViolationWithTemplate("No task selected.").addConstraintViolation();
return false;
}
return true;
}
}
When we call validator.validate(targetObject), javax.validation.Validator first finds suitable validator for object passed in the argument using its class name. It calles initialize method to initialize properties and resources. At last it will call isValid method.
You must be wondering about ValidateTask. It is an annotation used to specify where to perform the validation. Following is the code for ValidateTask

@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy=TaskValidator.class)
public @interface ValidateTask {

// All three properties are required by validation framework
String message() default "{TaskValidator}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
You can use it at class level or instance level. For this example I have used it at class level for simplicity like

@ValidateTask
public class Task extends BaseVO {
private int id;
private String taskName;

...........
}
Now we are done with centralized validator for Task. We can extend it to any number of entities. To use the validator in client, now you need only one line

 DefaultValidator.validate(new Task())
That's it. Now you can modify at one place and can impact entire application without the hassle of finding validation code and fixing it at multiple places.

In next article I will show you how to add Regex validator and db validator in the same component to make it robust.

No comments: