ONJava.com    
 Published on ONJava.com (http://www.onjava.com/)
 See this if you're having trouble printing code examples


Programming Jakarta Struts

Using the Validator Framework with Struts

by Chuck Cavaness, author of Programming Jakarta Struts
12/11/2002

Every application has a responsibility to ensure that only valid data is inserted into its repository. After all, what value would an application offer if the data that it relied upon were corrupted? For applications that use a formal database, like a RDBMS, for example, there are rules or constraints that can be placed upon the fields, which help to guarantee that the data stored within it meets a certain level of quality. Any and all applications that utilize the data within the repository have a responsibility to protect the integrity of the data that they submit.

Attempts to insert or update data that do not meet the criteria should be detected as soon as possible and rejected. This detection usually occurs in several places throughout an application; the presentation tier (if one is present) might perform some level of validation, the business objects typically have business-level validation rules, and as mentioned, the data repository usually does, as well.

Unfortunately, because data validation can occur in several areas within an application, there's usually a certain amount of redundancy that exists in applications for validating application data. This redundancy is generally an unwanted characteristic of an application, since it typically means longer development and maintenance times because the work is being repeated in multiple places. A more resilient application will attempt to reuse the validation rules throughout the application. This most likely translates into quicker development, easier customization cycles, and a more flexible application.

Related Article:

Jakarta Struts: Seven Lessons from the Trenches -- Chuck Cavaness, author of Programming Jakarta Struts, describes for Java programmers some of lessons he learned the hard way when he used the Struts framework to build a company application.

Introducing the Jakarta Commons Validator

The Validator framework is an open source project that was created by David Winterfeldt and is part of the Jakarta Commons subproject. The Commons project was created for the purpose of providing reusable components like the Validator. Other well-known Commons components include BeanUtils, Digester, and the Logging framework. Version 1.0 of the Validator was released at the beginning of November 2002.

The Benefits of Using the Validator

The Validator framework offers several benefits over the more conventional method of defining validation rules within the source code of an application. A few of the benefits include:

Related Reading

Programming Jakarta Struts
By Chuck Cavaness

These benefits aside, the most important characteristic of the Validator is its inherent support for pluggability. As you'll see later in this article, the Validator comes with several built-in validation rules that you can leverage right "out of the box." More importantly, however, the Validator allows you to define your own validation routines and easily plug them into the existing framework.

The Relationship Between Struts and the Validator

It should be pointed out that the Validator was originally created for use with the Jakarta Struts framework. The creator of the Validator, David Winterfeldt, was using Struts and realized that there was a great deal of redundancy in programming the same validation rules over and over again inside of the Struts ActionForm classes. He decided to create the Valdiator framework to eliminate that redundancy and subsequently, the Validator was born.

Although the Validator was originally created for use with Struts, it is designed and built in such a way that makes it possible to use on its own, without Struts. This characteristic allows you to use the framework in your applications, Struts-based or not. Just because you are not using Struts doesn't mean that you can't leverage the fine work that's been done. In fact, this is why the Validator is part of the Jakarta Commons project rather then being tied directly to the Struts project.

For now, we'll stick to the use of the framework with Web applications like those built on top of Struts. Later in the article, we'll discuss the steps required to use it with other types of applications, like those built with EJBs.

Overview of the Validator Components

There are several components that make up the Validator framework.

What are Validators?

A Validator is a Java class that, when called by the Validator framework, executes a validation rule. The framework knows how to invoke a Validator class based on its method signature, as defined in a configuration file. Typically, each Validator provides a single validation rule, and these rules can be chained together to form a more complex set of rules.

Note: It's possible to define multiple rules within a single Java class. You might do this for convenience. Each validation rule is a static method and contains no client- specific state.

The framework provides 14 default validation rules, which are sometimes referred to as "basic validators." The basic Validators are listed in Table 1.

Table 1. Basic Validators provided by the framework.

NameDescription
byte,short,integer,
long,float,double
Checks if the value can safely be converted to the corresponding primitive.
creditCardChecks if the field is a valid credit card number.
dateChecks if the field is a valid date.
emailChecks if the field is a valid email address.
maskSucceeds if the field matches the corresponding regular expression mask.
maxLengthChecks if the value's length is less than or equal to the given maximum length.
minLengthChecks if the value's length is greater than or equal to the given minimum length.
rangeChecks if the value is within a minimum and maximum range.
requiredChecks if the field isn't null and length of the field is greater than zero, not including whitespace.

As you can see from Table 1, the Validator framework provides many of the validation rules that most Web applications require. You can leverage these existing rules just by using the framework and setting up the proper configuration files. However, as was mentioned earlier and as we'll talk about later, you are free to add more Validators based on the needs of your application.

For now, let's stick with the basic Validators and discuss how you configure them for a Struts-based application.

Configuring the Validator for a Struts Application

One of the things that gives the Validator framework its flexibility is that all of the rules and details are declaratively configured in external files. Your application doesn't have to know anything about the specific validation rules. This characteristic allows the set of rules to be extended or modified without having to touch the code. This is very important when you have to customize each install of your application or when business requirements change, which they inescapably do.

When using the Validator framework with Struts 1.1, there are two configuration files used. One if called validator- rules.xml and the other is validation.xml. You can name these files anything that you want, or even combine the contents into a single XML file. However, it's better to leave them separated, because each one fulfills a slightly different purpose.

Note: If you just downloaded the Validator package from the Jakarta Commons site, these two files are not included. They are only present with a download of Struts, which includes the Validator.

The validator-rules.xml File

The validator-rules.xml file defines the Validator definitions available for a given application. The validator-rules.xml file acts as a template, defining all of the possible Validators that are available to an application.

Note: this XML file and the one we'll discuss next must be placed in a location where they can be found by the classloader. When using the Validator with a Web application, the correct location is under the WEB-INF directory.

The validator-rules.xml file is governed by validator- rules_1_1.dtd, which can be found and downloaded at jakarta.apache.org/struts/dtds/validator- rules_1_1.dtd. It would be too time-consuming to go into every detail about the XML definition file, so we'll just cover the essentials here.

The most important element within the validator-rules.xml file is contained with the <validator> element, as shown in Example 1.

Example 1. A simple validator-rules.xml File

<form-validation>
 <global>
  <validator 
     name="required"
     classname="org.apache.struts.util.StrutsValidator"
     method="validateRequired"
     methodparams="java.lang.Object,
                   org.apache.commons.validator.ValidatorAction,
                   org.apache.commons.validator.Field,
                   org.apache.struts.action.ActionErrors,
                   javax.servlet.http.HttpServletRequest" 
     msg="errors.required"/>
     
  <validator name="minlength"
     classname="org.apache.struts.util.StrutsValidator"
     method="validateMinLength"
     methodparams="java.lang.Object,
                   org.apache.commons.validator.ValidatorAction,
                   org.apache.commons.validator.Field,
                   org.apache.struts.action.ActionErrors,
                   javax.servlet.http.HttpServletRequest"
     depends="required"
     msg="errors.minlength"/>
 </global>
</form-validation>

There is one <validator> element for each validator that an application uses. In Example 1, there are two validators shown; one is the required validator and the other is the minlength validator. There are many attributes supported by the <validator> element. These attributes are necessary so that the framework knows the correct class and method to invoke on the Validator. For example, in the required validator element in Example 1, the method validateRequired() will be called in the org.apache.struts.util.StrutsValidator class. Validators can also depend on one another. This is shown in Example 1 with the minlength Validator. It includes a depends attribute, which indicates that it depends on the required validator. The msg attribute allows you to specify a key from a resource bundle that the framework will use to generate the correct error message. A resource bundle is used to help localize the error messages.

The <validator> element also supports a <javascript> child element that allows you to specify a JavaScript function that can be executed on the client side. In this way, both server-side and client-side validation can be specified in a single location, making maintenance easier.

The validation.xml File

The second configuration file for the Validator is normally named validation.xml, although again you are free to name it whatever you like or just put the contents into the validator-rules.xml file.

The validation.xml file is where you couple the individual Validators defined in the validator-rules.xml to components within your application. Since we are talking about using the Validator with Struts, the coupling occurs between the Validators and Struts ActionForm classes. ActionForm classes are simple JavaBean-like components within the Struts framework that capture user input and help to transfer the input to components deeper within the application. ActionForms also provide a convenient spot to validate the user input before passing it to the business layer. Example 2 shows a simple validation.xml file.

Example 2. A simple validation.xml File

<form-validation>
 <formset>
  <form name="checkoutForm">
    <field 
      property="firstName"
      depends="required">
      <arg0 key="label.firstName"/>
    </field>
         
    <field    
      property="lastName"
      depends="required">
      <arg0 key="label.lastName"/>
    </field>
  </form>
 </formset>
</form-validation>

Example 2 shows a <form> element with the attribute name equal to checkoutForm. The checkoutForm is an ActionForm that has been defined in the Struts configuration file. Therefore, the XML in Example 2 is coupling that ActionForm and its firstName and lastName properties (remember that ActionForms are simple JavaBeans) to the required Validator. This can be seen in the <field> elements, respectively.

There are actually many other features that allow you to define constants and global values that can be used throughout the validation.xml file. This comes in handy when you need to reuse certain constants or values over and over. For a more detailed discussion of the elements and attributes of the validation.xml file, you can download the DTD: jakarta.apache.org/struts/dtds/validation_1_1.dtd.

The Resource Bundle

The resource bundle is used to help localize messages and other textual information when dealing with users from various locales. It's also beneficial because it reduces the amount of redundant text hard-coded within an application. So instead of using the text label "Name:" directly within one or more JSPs, for example, you can put this text string within a resource bundle and pull the value from the bundle using a logical key. This way, if you needed to change the text label to "First Name:" you would only need to change it in one place.

For the Validator, the error messages that are created when validation rules fail come from the resource bundle. The Validator provides several default messages that can be placed in the Struts application resource bundle along with the normal application message resources. They look like:

#Normal resource bundle messages
label.firstName=First Name
label.lastName=Last Name

#Error messages used by the Validator
errors.required={0} is required.
errors.minlength={0} can not be less than {1} characters.
errors.maxlength={0} can not be greater than {1} characters.
errors.invalid={0} is invalid.
...

When a validation rule fails, an error message is created for that specific validation rule. The framework will also automatically insert parameters for the message. For example, if we were using the validation rules from Example 1 and 2 on the checkoutForm and the firstName property was null, we would see an error message like:

First Name is required.

You can also modify the messages within the bundle and/or configuration files to display any message you want.

Hooking Up the Validator to Struts

Now that we've talked about the Validator, you should have a pretty good feel for it. Let's quickly talk about how easy it is to use the Validator framework with Struts.

The first thing you need to do is to make your Struts application aware of the Validator. You do this by using a new feature of Struts 1.1 called "Plug-in." Just add the following to the Struts configuration file:

<plug-in classname="org.apache.struts.validator.ValidatorPlugIn">
  <set-property 
    property="pathnames" 
    value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"/>
</plug-in>

and Struts automatically becomes aware of the Validator.

The other necessary steps are to create the ActionForms (standard ones or Dynamic) and ensure the Validator configuration files are available, and away you go. There's no need to call the validation rules or do anything special. Struts will automatically do this, based on the declarative configurations. You will, however, need to have the error messages tag in the JSPs in order to see the validation failures.

Creating Your Own Validators

Even though the Validators provided by the framework cover many of the validation rules that Web applications require, there are special needs that will require you to create your own rules. Fortunately, the Validator framework is easily extensible and the effort to do so is minimal.

To create your own validator, just create a Java class that implements your special validation rule. For example, suppose you needed a rule to validate that a user was the legal drinking age; this might be necessary to purchase alcoholic beverages online. You could possibly use one of the existing validation rules, but it would be more obvious to create a rule to validate that the user meets the required drinking age. The Java class might look something like the one in Example 3.

Example 3. A custom validation rule

import java.io.Serializable;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.validator.Field;
import org.apache.commons.validator.GenericValidator;
import org.apache.commons.validator.ValidatorAction;
import org.apache.commons.validator.ValidatorUtil;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.util.StrutsValidatorUtil;

public class NewValidator implements Serializable{

  public static boolean validateDrinkingAge( Object bean,
    ValidatorAction va, 
    Field field, 
    ActionErrors errors, HttpServletRequest request) {

    String value = null;
    if (isString(bean)) {
      value = (String) bean;
    } else {
      value = 
        ValidatorUtil.getValueAsString(bean, field.getProperty());
    }
    String sMin = field.getVarValue("drinkingAge");

    if (!GenericValidator.isBlankOrNull(value)) {
      try {
        int iValue = Integer.parseInt(value);
        int drinkingAge = Integer.parseInt(sMin);

        if ( iValue < drinkingAge ){
          errors.add(field.getKey(),
          StrutsValidatorUtil.getActionError(request, va, field));
          return false;
        }
      } catch (Exception e) {
        errors.add(field.getKey(),
        StrutsValidatorUtil.getActionError(request, va, field));
        return false;
      }
    }
  return true;
}

private static boolean isString(Object o) {
  if (o == null) {
    return (true);
  }
  return (String.class.isInstance(o));
 }
}

After you create the new Validator, you simply add it to the list of existing Validators in the validation-rules.xml file. Once that's done, you can use the new Validator just like it was one of the "basic validators."

Using the Validator with a Non-Struts Application

As mentioned earlier, the Validator was originally designed for use with the Struts framework. However, because of its flexible design, there's no direct coupling to any Struts components, and you're free to use it in ordinary Java applications as well. There are, however, some steps that you'll have to take in order to make the Validator easier to digest.

You can utilize the same configuration files as in Web- based applications. This is another advantage of using the Validator framework. The Struts framework finds and loads these files based on the addition of the plug-in mentioned earlier. However, in non-Struts applications, the manner in which these configuration files are discovered and loaded must be done manually. This is typically done at application startup using the following method calls:

...
ValidatorResources resources = new ValidatorResources();

InputStream rules = 
   ValidateExample.class.getResourceAsStream("validator-rules.xml");

ValidatorResourcesInitializer.initialize(resources, in);

InputStream forms = 
   ValidateExample.class.getResourceAsStream("validation.xml");

ValidatorResourcesInitializer.initialize(resources, forms);

...

This snippet of code creates a new instance of a ValidatorResources class and initializes it based on the two Validator configuration files. The ValidatorResources object can then be used throughout your application to validate the configured JavaBeans.

Example 4 shows a small snippet of how you go about validating a Person bean using the initialized ValidatorResources object.

Example 4. A snippet of how to validate your beans using the Validator

// Suppose we already had a CheckoutForm object created and populated
CheckoutForm form = new CheckoutForm();

// Create a validator with the checkoutForm
Validator validator = new Validator(resources, "checkoutForm");

// Tell the validator which bean to validate against.  
validator.addResource(Validator.BEAN_KEY, form);

// Validate the checkoutForm object and store the validation results
ValidatorResults results = validator.validate();

In Example 4, you can see that the name of the JavaBean checkoutForm is being passed to the constructor of the Validator class. This is necessary so that the Validator instance knows which set of validation rules to use against the bean.

As you can see, using the Validator with non-Struts applications is a little less automatic, but still provides a more flexible solution. The other benefit is that it keeps the validation rules out of the source code and in external configuration files. This can save time when customization of your application is a vital aspect of your business.

Client-Side Versus Server-Side Validation

Finally, we should briefly mention the JavaScript support provided by the Validator. Because some applications would rather validate on the client side, it's necessary to sometimes write JavaScript to perform simple checks on the client. By client, we're referring to a Web browser.

The Validator provides the support for dynamically and automatically creating JavaScript validation rules based on rules within the configuration file. For each <validator> element, you can also include a <javascript> child element and include the JavaScript code right in the file. When a JSP is rendered that contains the appropriate custom tags, JavaScript will be rendered, as well, and perform validation on submittal of the form. The JSP custom tag is included in the set of Struts tags and is called JavaScriptValdiatorTag. The inclusion of the tag would look like:

<html:javascript formName="checkoutForm"/>

I think it's generally accepted that the amount of JavaScript should be limited when possible. However, when you need to perform some client-side validation, the Validator tags work very well and also support localization based on the user's locale.

Conclusion

By now, I hope I've enticed you to go and give the Validator framework a whirl. I've really only scratched the surface and there's much more available within the walls of the framework. The regular expression support alone could fill a small book.

Like any good framework, the Validator provides a solid foundation from which you can extend or customize the behavior to fit your specific needs. The overriding benefit of using a framework like the Validator is that it has already been built, has been tested by many, and is being actively and successfully used. Don't reinvent the wheel when you don't have to; save your energy for solving your core business problem, not one that is peripheral to your core business.


O'Reilly & Associates recently released (November 2002) Programming Jakarta Struts.

Chuck Cavaness is a graduate from Georgia Tech with degrees in computer engineering and computer science. He has built Java-based enterprise systems in the healthcare, banking, and B2B sectors. He is also the author of two O'Reilly books, Programming Jakarta Struts and Jakarta Struts Pocket Reference.


Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.