Recently I was faced with the challenge of implementing form-level (or page-level) validation in a JSF-based application. What I mean by form-level validation is the need to evaluate a subset of a form's fields as a unit, rather than simply validating each field in isolation. An example of this type of validation can be found on a user registration form where one has to select a password in one text field, and then retype the same password in another text field for confirmation. Validating that these two text fields contain the same password is an example of form level validation.

In my case, I had two date selector components on my form, one for a start date/time and one for an end date/time for an event that was being scheduled. The rule I needed to validate was that the end date/time was later than the start date/time.

There are a few ways to implement validation like this, including but not limited to:

Build a custom component that renders selectors for both the start and end date/time, then validate as a unit. This actually is field-level validation and doesn't truly address the form-level problem.

Implement a validator method on a managed bean that will evaluate the data submitted for multiple components.

I'll address the second method in this HOWTO.

First, you'll need to bind at least n-1 of the components that you want to validate to properties on your managed bean. The simplest way is to declare properties of type UIInput:

To understand why I say n-1 components, think of the way the validation phase occurs in JSF. Data is bound to the components in the order that they occur in the JSF component tree, which just so happens to be the order in which they appear in the JSP source. Looking at the validateEndDate method, you'll see that I only reference the startDateComponent from the binding, but I reference the endDate as the Object value reference that was passed into the method. This is why you only need to bind n-1 components, because you get the nth component from the method signature.

If you want to be more uniform and bind all of the components, you could create an extra dummy hidden value component and bind the validator method to it. You could then bind all of the components to your managed bean and access them all from the bindings rather than accessing one from the method signature.

The validateEndDate method itself is rather simple. First you access the data by getting the local value of each component (again, the endDate value is not accessed in this way - in fact, it hasn't been bound yet because it must be validated first, and that's what's happening in this method!). You then apply the business rule. You'll see that first I look to see if the startDate is null. I'm not sure why this is possible, but if the startDate was not submitting a good value on the FIRST submit, the local value was null. So, I catch that here. I add an error message to the startDateComponent and throw a ValidatorException. If the business rule is violated, throw a ValidatorException. (I'm also using the addError method provided by AppFuse to work w/ its message framework as well, but that is not necessary w/ all JSF apps).

Now, for the final problem I encountered. In Weblogic server, which we're still using for the time being, if your session cannot be serialized then it deletes your entire session. Obviously this can cause major problems in any web app. To deal with this, ANY SESSION SCOPED MANAGED BEAN must be fully serializable, meaning it and any objects referenced in its state. Herein lies the problem for JSF. Instances of UIComponent (an ancestor of UIInput) are not serializable, so if we bind our components to UIInput fields on a session-scoped managed bean (the bean backing this form is an Order Form/Shopping Cart style bean), it will not be serializable and Weblogic will kick out your session.

To deal with this problem, realize that there is no reason that you can only have one managed bean backing a form. In fact, you can reference as many managed beans as you need. Since validation is done for each request, there is no need to manage any state there across multiple requests like we need to do with a shopping cart. So, why not declare an additional managed bean that is REQUEST SCOPED, and then put the bindings and validation method there. That is exactly what I did. Here is the entire bean:

The added bonus is that you can reuse this bean across all forms where you need this behavior. My application happens to have 2 additional forms where I would have repeated this logic, so I just reference this bean there.

FWIW, I tried using a hidden input as you hypothesized to do the binding outside the date inputs, but it doesn't fire because the input value won't change (would have to do something funky with javascript -- not worth it).

Also, regarding your serialization issue, would it work to just declare the UIInput fields as transient? I guess it depends on when the setter methods of the binding are called (once or per request) and whether you'd need them to work after deserialization.

Search

About Me

Matt Stine

I am the Group Leader for Research Application Development in the Research Informatics division of Information Sciences at St. Jude Children's Research Hospital in Memphis,TN. I primarily spend my days developing and supporting Java EE-based applications in support of basic life sciences research. I have been developing enterprise-scale as well as medium-scale web and desktop-based Java applications for St. Jude since 2000. I have experience developing applications using numerous open source technologies including Spring, Hibernate, and iBATIS.
When I'm not coding, I primarily spend my time trying to keep four very important girls happy: my sweet wife Wendy and my three daughters Abby, Isabella, and Ali Kate. I am also blessed to be the father of the next generation of Stines, Grant Dawson.