Table of Contents
Bean Validator is a Java 5 metadata annotation-based framework for applying validation constraints to JavaBean-compliant classes. It also provides integration layers for using the core validation framework with Enterprise JavaBeans (EJB) 3.0, JavaServer Faces (JSF) and JBoss Seam.
Let's demonstrate this with an example. The code below is an EJB 3.0 entity bean representing a contact.
@Entity public class Contact { private Integer primaryKey; private String firstName; private String surname; private String phoneCode; private String phoneNo; public Contact() { } @Required @Length(max=15) public String getFirstName() { return firstName; } @Required public String getSurname() { return surname; } public String getPhoneCode() { return phoneCode; } @IfNotNull("phoneCode") @Required public String getPhoneNo() { return phoneNo; } // Primary key... // Setters... }
We have applied the following constraints on all instances of Contact:
firstName is required and can have at most 15 characters.
surname is required.
phoneNo is required, but only if phoneCode is specified.
Now let's say we have a stateless session that persists Contacts (and that we've defined a @Local interface somewhere):
@Stateless @Interceptor(ValidatorInterceptor.class) public class ContactSessionBean { @PersistenceContext private EntityManager em; public void addContact(@ValidateParam Contact contact) throws ValidationException { em.persist(contact); } }
We've specified a Bean alidator interceptor. This interceptor actually performs the validation.
The @ValidateParam annotation marks the contact parameter for validation.
The addContact method throws a Bean Validator exception, ValidationException.
Our Add Contact JSP JSF view looks like this.
<%-- Details omitted... --%> <h:panelGrid columns="3"> <h:label for="firstName" value="First Name"/> <h:inputText value="contactMgr.contact.firstName"/> <h:message for="firstName"/> <h:label for="surname" value="Surname"/> <h:inputText value="contactMgr.contact.surname"/> <h:message for="surname"/> <h:label for="phoneCode" value="Phone Code"/> <h:inputText value="contactMgr.contact.phoneCode"/> <h:message for="phoneCode"/> <h:label for="phoneNo" value="Phone No"/> <h:inputText value="contactMgr.contact.phoneNo"/> <h:message for="phoneNo"/> </h:panelGrid> <h:commandButton action="contactMgr.addContact"/> <%-- Details omitted... --%>
We've defined the following JSF Managed Bean class in session scope with the name contactMgr:
public class ContactManager implements java.io.Serializeable { private Contact contact; public String addContact() throws Exception { ContactSession sess = new InitialContext().lookup("ContactApp/ContactSession/local"); try { sess.addContact(contact); return "success"; } catch (ValidationException ex) { FacesMessageGenerator.getInstance().addErrorsToContext( FacesContext.getCurrentInstance(), ex.getValidationReport()); return "failure"; } } }
Assume that the "failure" outcome returns to the same view.
The FacesMessageGenerator adds validation mesages to the Faces Context using the bean property name as the Faces Client ID.
The result? If, for example, a user typed in a First Name without a surname, then she would see the same form again with an error message next to the Surname field. Typing in a Phone Code without a Phone Number will result in a message next to the Phone Code field.
So what have we gained? We could have used JSF required="true" attributes and <f:validateLength/> to apply the validation right there in the page itself. We could even have jumped through hoops to perform the linked validation between Phone Number and Phone Code in the action listener.
The problem comes when you use a Contact in more than one view. You can simply copy-and-past the validation tags, but that's inelegant and makes maintenance difficult. Putting the data validation in the domain classes, where the definition of the properties lie, makes more sense.
Like we said above - Bean Validator is about putting domain object-level validation where it belongs. You write the validation constraints once only while writing the bean, and can then re-use it in any number of workflows. It's not restricted to EJB 3.0 entities, either - you can use it on any class with JavaBeans-style accessors.
This isn't an original idea - BeanValidator was inspired by the Hibernate Validator (http://www.hibernate.org), but we wrote an alternative that can more readily be user with persistence implementations other than Hibernate, and we also added linked property validators, instance-level validators and validation methods.