Javier has posted 48 posts at DZone. View Full User Profile

Be Standard, be Free: Use JSR-303 for Validation

03.03.2011
| 10633 views |
  • submit to reddit
No matter what type of application we develop, coding validations is our everyday task. For years we have used a big variety of techniques and frameworks for validation with success. However, flor some time we have a standard in Java for validation, the Bean Validation specification (JSR-303). The question is: is it worth writing (or rewriting) our validations using the Bean Validation standard? Is there some practical advantage in use JSR-303?
First, let's write a validator (constraint in Bean Validation nomenclature) and then let's use it in our code, to see what we are talking about.

Creating a JSR-303 constraint

A JSR-303 constraint is just a plain Java annotation. For example, if we want to validate ISBNs, we have to create an @ISBN annotation like the next one:

package org.openxava.books.constraints;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
import java.lang.annotation.*;
import javax.validation.*;
import org.openxava.books.constraints.impl.*;

@Constraint(validatedBy = ISBNValidator.class) // ISBNValidator contains the validation logic
@Target({ METHOD, FIELD })
@Retention(RUNTIME)
public @interface ISBN {

String message() default "{org.openxava.books.constraints.ISBN.message}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };

}

The key part here is the @Constraint annotation. It marks this annotation as a constraint and indicates the validator class, ISBNValidator, that is the class with the validation logic:

package org.openxava.books.constraints.impl;

import javax.validation.*;
import org.openxava.books.constraints.*;

public class ISBNValidator implements ConstraintValidator<ISBN, String> {

private static org.apache.commons.validator.ISBNValidator validator =
new org.apache.commons.validator.ISBNValidator(); // Validator from commons validator

public void initialize(ISBN isbn) {
}

public boolean isValid(String value, ConstraintValidatorContext ctx) { // The validation logic
if (value == null || value.trim().equals("")) return true;
return validator.isValid(value); // We simply delegate in commons validator
}

}

The validator must implement ConstraintValidator, so it needs to have the initialize() and isValid() methods, the latter contains the validation logic. In this case we simply delegate in a validator from Apache Commons Validator project.
As we can see create a validator is pretty simple. Now, how can we use this validation in our application?

Using the validator

The JSR-303 specification talks about how to define validators, but not about how they are used and behave in our application, this depends on the framework we are using. JPA2 and JSF2 have Bean Validation integration, moreover many modern frameworks have support for JSR-303 as well. In this case we are going to develop a mini-application using the OpenXava framework that use the above @ISBN application. Don't panic, developing an OpenXava application is short (and sweet), given we only need to write the domain classes.
After creating a new OpenXava project (executing an ant target) we add the Author and Book classes to it.

Author.groovy:

package org.openxava.books.model

import javax.persistence.*
import org.openxava.model.*
import org.openxava.annotations.*

@Entity
class Author extends Identifiable {

@Required
String name

}

Book.groovy:

package org.openxava.books.model

import org.openxava.model.*;
import org.openxava.annotations.*;
import org.openxava.books.constraints.*;
import javax.persistence.*;

@Entity
class Book extends Identifiable {

@Required @Column(length=60)
String title

@ManyToOne(fetch=FetchType.LAZY)
@DescriptionsList
Author author

@Column(length=10)
@ISBN
String isbn

}

Although we could write the classes with Java, we have chosen Groovy. Yes, Groovy Web Development without Grails is possible. However, the important point here is the @ISBN annotation in isbn property. We do not need more work to see the validation working. If we go to http://localhost:8080/Books/modules/Book, and we try to add a Book with an incorrect ISBN, we'll get something like this:

As we can see using the @ISBN validation in our application is a completely declarative task, so dead easy.

Why should I use Bean Validation?

Though JSR-303 is easy to use and versatile enough to meet our validation needs, really is not something spectacular, and possibly not much better than our current validation framework, so, why should we use it? Because it is supported by JPA2, JSF2, Spring Roo, Wicket, Tapestry, etc. in addition to be included as part of Java EE 6. Therefore, it gives us more freedom, because our code is less dependent from the application framework we are using, thus it's easier to migrate our code (at least the model part) from Wicket to Tapestry, or from Spring Roo to OpenXava.

 

Use the Bean Validation standard. Be free!

Resources

Published at DZone with permission of its author, Javier Paniza.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Zqudlyba Navis replied on Thu, 2011/03/03 - 3:14am

Spring framework is non-standard. Should I stop using it too ?

Javier Paniza replied on Thu, 2011/03/03 - 4:50am in response to: Zqudlyba Navis

Hi Zqudlyba,

Spring framework is non-standard. Should I stop using it too 

No, the point of the article is "use the standard for validation", not "use standards for everthing". You're right, there are a lot of non-standard tools like Eclipse, JUnit, Maven, Ant, etc. that are essencial for Java developers. Moreover, the standards have a hard time when they try to produce innovation, look at MDA, CORBA or EJB for example. However, when a standard is already consolidated (like JPA) using it allows you to not depend on a specific vendor, and it is a very important advantage. For example, if you use JPA (the standard) instead of Hibernate, you can move from Hibernate to other persistence provider to improve performance, to access to non-relational database, etc.

Nicolas Frankel replied on Thu, 2011/03/03 - 7:03am in response to: Javier Paniza

Hi Javier,

Although I agree to the general content of your article, I must tell you the JPA/Hibernate example couldn't be worse. I have the freedom to tell you so because I made the mistake of bringing JPA 1 as the standard to use in my enterprise.

Big mistake indeed: JPA 1 features are less than 10% that of Hibernate (50% with JPA v2?) and you always have to dig the Hibernate Session delegate from the EntityManager or use Hibernates annotations because there's no JPA equivalent.

Beyond "Hello world!", JPA should be a big no no, although using JPA annotations with Hibernate is a good idea.

My own 2 cents...

Andrew Spencer replied on Fri, 2011/03/04 - 6:35am

Thanks for this article. (I have a dream that one day I'll work for a client who uses something more recent than Struts 1.x, so that I can put it into action.)

Norris Shelton replied on Fri, 2011/03/04 - 7:56am in response to: Zqudlyba Navis

Spring 3 does support JSR-303 annotations.

Liam Knox replied on Fri, 2011/03/04 - 5:56pm in response to: Zqudlyba Navis

J2EE is a 'standard' is this a good reason to use it ? What ever the word standard means these day ...

Cloves Almeida replied on Sun, 2011/03/06 - 1:10pm

Standards are not a panacea - where they exists, they should be preffered over non-standard solutions. The reason is obvious, just think what the world would be if each city could choose it's electric voltage (50v, 300v, 180v, etc.) instead of using a standard like 220v or 110v.

As for JPA, I might implement only 20% of what Hibernate provides. However, those 20% features solves 90% of the problems. You might need to access org.hibernate.Session every now and then, but that's no excuse to use it when javax.persistence.EntityManager would suffice.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.