Bean Validation and JSR 303
Join the DZone community and get the full member experience.
Join For FreeIn this article, I will show you how to use the new Bean Validation Framework aka JSR-303.
The legacy
Before getting the result that is JSR 303, aka the Bean Validation framework, there were two interesting attempts at validation framework. Each came from an end of the layers and focused on its scope.
Front-end validation
Struts was the framework to learn and use on the presentation layer in 2001-2002. Struts uses the MVC model and focus on Controllers, which are represented in Struts with Action. Views are plain JSP and Struts uses ActionForm in order to pass data from Controllers to Views and vice-versa. In short, those are POJO that the framework uses to interact with the View.
As a presentation layer framework, Struts concern is validating user input. Action forms have a nifty method called validate(). The signature of this method is the following:
public ActionErrors validate(ActionMapping mapping, HttpServletRequest request)
The developer has to check whether the form is valid then fill the ActionErrors object (basically a List) if it’s not the case. Struts then redirects the flow to an error page (the input) if the ActionErrors object is not empty. Since manual checking is boring and error-prone, it may be a good idea to automate such validation. Even at that time, declarative validation was considered to be the thing.
This is the objective of Apache Commons Validator. Its configuration is made through XML. You specify:
- the validators you have access to. There are some built-in but you can add your own
- the associations between beans and validators: which beans will be validated by which rules
Though Struts tightly integrates Commons Validator, you can use the latter entirely separately. However, the last stable version (1.3.1) was released late 2006. The current developed version is 1.4 but the Maven site hasn’t been updated since early 2008. It is a bit left aside for my own tatse so I rule it out for my validation needs save when I am forced to use Struts.
In this case it is mandatory for me to use it since the Struts plugin knows how to use both XML configuration files to also produce JavaScript client-side validation.
Back-end validation
Previously, we saw that the first validation framework came from user input. At the other end of the specter, inserting/updating data does not require such validation since constraints are enforced in the database. For example, trying to insert a 50 characters length string into a VARCHAR(20) column will fail.
However, letting the database handle validation has two main drawbacks:
- it has a performance cost since you need to connect to the database, send the request and handle the error
- such error cannot be easily mapped to a Java exception and if possible, to a particular attribute in error
In the end, it is better to validate the domain model in the Java world, before sending data to the database. Such was the scope of Hibernate Validator. Whereas Commons Validator configuration is based on XML, Hibernate Validator is based on Java 5 annotations.
Even if Hibernate Validator was designed to validate the domain model, you could use it to validate any bean.
JSR 303 Bean Validation
Finally, JSR 303 came to fruitition. Two important facts: it is end-agnostic, meaning you can use it anywhere you like (front-end, back-end, even DTO if you follow this pattern) and its reference implementation is Hibernate Validator v4.
JSR 303 features include:
- validation on two different levels: attribute or entire bean. That was not possible with Hibernate Validator (since it was database oriented) and only possible with much limitations with Commons Validator
- i18n ready and message are parameterized
- extensible with your own validators
- configurable with annotations or XML. In the following, only the annotation configuration will be shown
In JSR 303, validation is the result of the interaction between:
- the annotation itself. Some come with JSR 303, but you can build your own
- the class that will validate the annotated bean
Simplest example
The simplest example possible consist of setting a not-null constraint on an attribute of a class. This is done simply so:
public class Person {
private String firstName;
@NotNull
public String getFirstName() {
return firstName;
}
// setter
}
Note that the @NotNull annotation can be placed on the attribute or on the getter (just like in JPA). If you use Hibernate, it can also use your JSR 303 annotations in order to create/update the database schema.
Now, in order to validate an instance of this bean, all you have to do is:
Set<ConstraintViolation<Person>> violations = validator.validate(person);
If the set is empty, the validation succeeded, it not, it failed: the principle is very similar to both previous frameworks.
Interestingly enough, the specs enforce that constraints be inherited. So, if a User class inherits from Person, its firstName attribute will have a not-null constraint too.
Constraints groups
On the presentation tier, it may happen that you have to use the same form bean in two different contexts, such as create and update. In both contexts you have different constraints. For example, when creating your profile, the username is mandatory. When updating, it cannot be changed so there’s no need to validate it.
Struts (and its faithful ally Commons Validator) solve this problem by associating the validation rules not with the Java class but with the mapping since its scope is the front-end. This is not possible when using annotations. In order to ease bean reuse, JSR 303 introduce constraint grouping. If you do not specify anything, like previously, your constraint is assigned to the default group, and, when validating, you do so in the default group.
You can also specify groups on a constraint like so:
public class Person {
private String firstName;
@NotNull(groups = DummyGroup)
public String getFirstName() {
return firstName;
}
// setter
}
So, this will validate:
Person person = new Person(); // Empty set Set<Constraintviolation<Person>> violations = validator.validate(person);
This will also:
Person person = new Person(); // Empty set Set<Constraintviolation<Person>> violations = validator.validate(person, Default.class);
And this won’t:
Person person = new Person(); // Size 1 set Set<Constraintviolation<Person>> violations = validator.validate(person, DummyGroup.class);
Custom constraint
When done playing with the built-in constraints (and the Hibernate extensions), you will probably need to develop your own. It is very easy: constraints are annotations that are themselves annotated with @Constraint. Let’s create a constraint that check for uncapitalized strings:
@Target( { METHOD, FIELD, ANNOTATION_TYPE }) @Retention(RUNTIME) @Constraint(validatedBy = CapitalizedValidator.class) public @interface Capitalized { String message() default "{ch.frankel.blog.validation.constraints.capitalized}"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
The 3 elements are respectively for internationalization, grouping (see above) and passing meta-data. These are all mandatory: if not defined, the framework will not work! It is also possible to add more elements, for example to parameterize the validation: the @Min and @Max constraints use this.
Notice there’s nothing that prevents constraints from being applied to instances rather than attributes, this is defined by the @Target and is a design choice.
Next comes the validation class. It must implement ConstraintValidator<?,?>:
public class CapitalizedValidator implements ConstraintValidator<Capitalized, String> { public void initialize(Capitalized capitalized) {} public boolean isValid(String value, ConstraintValidatorContext context) { return value == null || value.equals(WordUtils.capitalizeFully(value)); } }
That’s all! All you have to do now is annotate attributes with @Capitalized and validate instances with the framework. There’s no need to register the freshly created validator.
Constraints composition
It is encouraged to create simple constraints then compose them to create more complex validation rules. In order to do that, create a new constraint and annotate it with the constraints you want to compose. Let’s create a constraint that will validate that a String is neither null nor uncapitalized:
@NotNull @Capitalized @Target( { METHOD, FIELD, ANNOTATION_TYPE }) @Retention(RUNTIME) @Constraint(validatedBy = {}) public @interface CapitalizedNotNull { String message() default "{ch.frankel.blog.validation.constraints.capitalized}"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
Now, annotate your attributes with it and watch the magic happen!
Of course, if you want to prevent constraint composition, you’ll have to restrain the @Target values to exclude ANNOTATION_TYPE.
Conclusion
This article only brushed the surface of JSR 303. Nevertheless, I hoped it was a nice introduction to its features and gave you the desire to look into it further.
You can find here the sources (and more) for this article in Eclipse/Maven format.
Opinions expressed by DZone contributors are their own.
Comments