Creating a simple validator is a fairly straightforward matter. Each validator consists of two parts- an Annotation that defines the validator, and is applied to the field being validated, and a Class that performs the validation itself. The Annotation is annotated with a ValidatorDescriptor that links it to the validation class. Validation of an object is then performed using the static validate(Object, ValidationResult) method of the AnnotatedObjectValidator class.
The ValidatorDescriptor provides a lot of flexibility with regards to specifying the method that performs the validation, but for now we'll focus on a simple validation that uses the default validation method.
For creating an Annotation , a basic knowledge of how they work is useful. I'd recommend taking a look at Sun's annotation tutorial at http://java.sun.com/docs/books/tutorial/java/javaOO/annotations.html .
As mentioned in the above section, a validator consists of two parts: An Annotation , and a Class . The Annotation is applied to a field in the object being validated, which, at runtime, is examined by the AnnotatedObjectValidator . The Class contains the actual validation code.
The first step that needs to be performed is the creation of the Annotation . For our example, I'll use one of the simplest and most common validation cases: Validating a required field.
Our initial Annotation will look like this:
public @interface ValidateRequired { }
If you've worked with Annotation s before, or have read the aforementioned tutorial, then you'll know that this alone does not produce a usable Annotation . We need to specify two important things first: The Target and the Retention . Target tells the Annotation what it can be applied to- Class , Field , Method , etc. Retention tells it when the Annotation should be available for reading.
The Annotation s we create for the validator should have these values set to ElementType.FIELD and RetentionPolicy.RUNTIME , respectively.
These are specified using Annotation s on the Annotation itself:
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface ValidateRequired { }
Now we have an Annotation that can be applied to a Field , and be read by Java at runtime. The Annotation doesn't do anything by itself though- to actually validate the Field we need to write a validator object.
The validator object, and the validation method it contains, are the actual nuts and bolts of the validator. The validator object can be any Class anywhere on the classpath, but for the sake of keeping everything in one place I recommend creating an inner Class in the Annotation itself, with the public and static modifiers:
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface ValidateRequired { public static RequiredFieldValidator { } }
Next, we need to add the method that performs the validation. This method must return a boolean - true if validation was successful, otherwise false . The name and arguments of this method can be defined however we like using the advanced features of the validator, but by default a method called validate with a single argument of type Object is expected:
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface ValidateRequired { public static RequiredFieldValidator { public boolean validate(Object object) { if (object instanceof String) { return StringUtils.isNotBlank((String) object); } else { return object != null; } } } }
The final step in creating a validator is linking the Validator Class to the Annotation . This is done using a ValidatorDescriptor , which tells the AnnotatedObjectValidator what to do when a specific validator is encountered. The ValidatorDescriptor allows for a lot of flexibility when fully implemented, but by default only requires two pieces of information:
Optionally, an errorCode parameter may be specified in place of errorCodeSuffix . This overrides the generation of an error code, and replaces it with whatever you specify. Error codes will be covered more in the "Error Codes" section of this page.
Applying the ValidatorDescriptor is extremely straightforward when we use the defaults:
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @ValidatorDescriptor( validatorClass=ValidateRequired.RequiredFieldValidator.class, errorCodeSuffix="required" } public @interface ValidateRequired { public static RequiredFieldValidator { public boolean validate(Object object) { if (object instanceof String) { return StringUtils.isNotBlank((String) object); } else { return object != null; } } } }
And now we're done! Our validator is ready for use.
In order to validate a particular field, we need to do two things: Apply the Annotation we created to the field, and then invoke the static validate method of AnnotatedObjectValidator with the object containing the field and a ValidationResult implementation (more on this below) to store the results.
Applying a validator to a field is extremely simple: all you need to do is annotate the field you want to validate with the Annotation that describes your validator:
public class FormObject { @ValidateRequired private String name; public void setName(String name) { this.name = name; } public String getName() { return this.name; } }
Note: In the case of many frameworks, it is important to provide getter and setter methods for your field!
It really doesn't get much simpler than that!
Validating an object is performed by invoking the static validate(Object, ValidationResult) method of the AnnotatedObjectValidator class. This requires two arguments: the first, and most obvious, is the object being validated. The second is an object that implements the ValidationResult interface. This provides the means for your application to respond to validation violations- that is, fields that fail validation. You'll need to write one of these for the framework you're using (support for the Spring framework is currently provided in a seperate spring-support package). For the purpose of this exercise, we'll use a simple stub implementation:
public class StubValidationResult implements ValidationResult { @Override public void recordValidationViolation(String field, String errorCode, Object value) { System.out.println("Validation of the field \" + field + "\" failed" + " for the value \"" + value + "\" and generated the error" + " code \"" + errorCode + "\"."); } }
This stub implementation will simply print any validation failures that occur to the console.
Each ValidationResult implementation must implement the method recordValidationViolation(String, String, Object) . The arguments, as demonstrated above, are as follows:
With a working ValidationResult , and an object with appropriately annotated fields, we are now able to invoke the validator:
FormObject formObject = new FormObject(); formObject.setName(null); AnnotatedFormValidator.validate(formObject, new StubValidationResult());
With our StubValidationResult , this will cause the following message to be printed to the console:
Validation of the field "name" failed for the value "null" and generated the error code "FormObject.name.required".
Assuming there was an error validating the FormObject , the ValidationResult implementation for your project will have stored an entry for each validation rule that was violated. These should each contain an error code that can be used to resolve a message in an external message source (generally a .properties file), and display it in your UI. In order for the error message to be displayed, you'll need to add it to the appropriate file, with the right key.
The key is generated automatically by the validator, using the class name of the object, the name of the field, and the value of the errorCodeSuffix that you specified when you made your Annotation , and follows the pattern ObjectClassName .fieldName .errorCodeSuffix .
So, for the examples above, our error code will be FormObject.name.required . Enter this in your .properties file as follows:
FormObject.name.required=Please enter a name