Struts Validator
 Validation Framework for Struts
Home
Installation
Overview
JSP Tags
Javadoc
To Do List
Revision Info
Downloads
Contact Information

Overview
Setup
Internationalization
Constants/Variables
Pluggable Validators
Creating Pluggable Validators
Multi Page Forms
Comparing Two Fields
Validating Outside of Struts

Overview

The Validation Framework was made to work with Struts, but can be used for validation of any java bean. It can perform basic validations to check if a field is required, matches a regular expression, email, credit card, and server side type checking and date validation. Different validation rules can be defined for different locales. The framework has basic support for user defined constants which can be used in some field attributes. The validation routines are modifiable in the validation.xml file so custom validation routines can be created and added to the framework.

Setup

In Struts once you have defined the ValidatorServlet in the web.xml so it can load your ValidatorResources you just have to extend org.apache.struts.validator.action.ValidatorForm instead of org.apache.struts.action.ActionForm. Then when the validate method is called the action's name attribute from the struts-config.xml is used to load the validations for the current form. So the form element's name attribute in the validation.xml should match action element's name attribute.

Another alternative is to use the action mapping you are currently on by extending the ValidatorActionForm instead of the ValidatorForm. The ValidatorActionForm uses the action element's 'path' attribute from the struts-config.xml which should match the form element's name attribute in the validation.xml. Then a separate action can be defined for each page in a multi-page form and the validation rules can be associated with the action and not a page number as in the example of a multi-page form in the validator example.

Internationalization

Validation rules for forms can be grouped under a FormSet in the validation.xml file. The FormSet has language, country, and variant attributes that correspond with the java.util.Locale class. If they are not used, the FormSet will be set to the default locale. A FormSet can also have constants associated with it. On the same level as a FormSet there can be a global element which can also have constants and have validator actions that perform validations.

The default error message for a pluggable validator can be overriden with the msg element. So instead of using the msg attribute for the mask validator to generate the error message the msg attribute from the field will be used if the name of the field's name attribute matches the validator's name attribute.

ex: <field property="lastName"
              depends="required,mask">
        <msg name="mask" key="registrationForm.lastname.maskmsg"/>
        <arg0 key="registrationForm.lastname.displayname"/>
        <var>
           <var-name>mask</var-name>
           <var-value>^[a-zA-Z]*$</var-value>
        </var>
     </field>

The arguments for error messages can be set with the arg0-arg3 elements. If the arg0-arg3 elements' name attribute isn't set, it will become the default arg value for the different error messages constructed. If the name attribute is set, you can specify the argument for a specific pluggable validator and then this will be used for constructing the error message.

ex: <field property="lastName"
              depends="required,mask">
        <msg name="mask" key="registrationForm.lastname.maskmsg"/>
        <arg0 key="registrationForm.lastname.displayname"/>
        <var>
           <var-name>mask</var-name>
           <var-value>^[a-zA-Z]*$</var-value>
        </var>
     </field>

By default the arg0-arg3 elements will try to look up the key attribute in the message resources. If the resource attribute is set to false, it will pass in the value directly without retrieving the value from the message resources.

ex: <field property="integer"
             depends="required,integer,range">
          <arg0 key="typeForm.integer.displayname"/>
          <arg1 name="range" key="${var:min}" resource="false"/>
          <arg2 name="range" key="${var:max}" resource="false"/>
          <var>
             <var-name>min</var-name>
             <var-value>10</var-value>
          </var>
          <var>
             <var-name>max</var-name>
             <var-value>20</var-value>
          </var>
     </field>

Constants/Variables

Global constants can be inside the global tags and FormSet/Locale constants can be created in the formset tags. Constants are currently only replaced in the Field's property attribute, the Field's var element value attribute, the Field's msg element key attribute, and Field's arg0-arg3 element's key attribute. A Field's variables can also be substituted in the arg0-arg3 elements (ex: ${var:min}). The order of replacement is FormSet/Locale constants are replaced first, Global constants second, and for the arg elements variables are replaced last.
ex: <global>
          <constant name="zip" value="^\d{5}(-\d{4})?$" />
     </global>

     <field property="zip"
               depends="required,mask">
          <arg0 key="registrationForm.zippostal.displayname"/>
          <var>
             <var-name>mask</var-name>
             <var-value>${zip}</var-value>
          </var>
     </field>

The var element under a field can be used to store variables for use by a pluggable validator. These variables are available through the Field's getVar(String key) method.
   ex:
     <field property="integer"
              depends="required,integer,range">
          <arg0 key="typeForm.integer.displayname"/>
          <arg1 name="range" key="${var:min}" resource="false"/>
          <arg2 name="range" key="${var:max}" resource="false"/>
          <var>
             <var-name>min</var-name>
             <var-value>10</var-value>
          </var>
          <var>
             <var-name>max</var-name>
             <var-value>20</var-value>
          </var>
     </field>

       See type form's integer field in the example web app for a working example.

Pluggable Validators

Validation actions are read from the validation.xml file. The default actions are setup in the validation.xml file. The ones currently configured are required, mask ,byte, short, int, long, float, double, date (without locale support), and a numeric range. The 'mask' action depends on required in the default setup. That means that 'required' has to successfully completed before 'mask' will run. The 'required' and 'mask' action are partially built into the framework. Any field that isn't 'required' will skip other actions if the field is null or has a length of zero. If the Javascript Validator JSP Tag is used, the client side Javascript generation looks for a value in the validator's javascript attribute and generates an object that the supplied method can use to validate the form. For a more detailed explanation of how the Javascript Validator Tag works, see the JSP Tags section.

The 'mask' action let's you validate a regular expression mask to the field. It uses the Regular Expression Package from the jakarta site. All validation rules are stored in the validation.xml file. The main class used is org.apache.regexp.RE.

Example Validator Configuration from validation.xml.
   <validator name="required"
         classname="org.apache.struts.validator.util.StrutsValidatorUtil"
         method="validateRequired"
         msg="errors.required"/>

   <validator name="mask"
         classname="org.apache.struts.validator.util.StrutsValidatorUtil"
         method="validateMask"
         depends="required"
         msg="errors.invalid"/>

Creating Pluggable Validators

The ValidatorAction method needs to have the following signature. See the org.apache.struts.validator.util.StrutsValidator class for examples.

   (java.lang.Object,
    org.apache.commons.validator.ValidatorAction, org.apache.commons.validator.Field,
    org.apache.struts.action.ActionErrors, ,
    javax.servlet.http.HttpServletRequest, javax.servlet.ServletContext)
java.lang.ObjectBean validation is being performed on.
org.apache.commons.validator.ValidatorActionThe current ValidatorAction being performed.
org.apache.commons.validator.FieldField object being validated.
org.apache.struts.action.ActionErrorsThe errors objects to add an ActionError to if the validation fails.
javax.servlet.http.HttpServletRequestCurrent request object.
javax.servlet.ServletContextThe application's ServletContext.

Multi Page Forms

The field element has an optional page attribute. It can be set to an integer. All validation for the any field page value less than or equal to the current page is performed server side. All validation for the any field page equal to the current page is generated for the client side Javascript. A mutli-part form expects the page attribute to be set.
   ex: <html:hidden property="page" value="1"/>

Comparing Two Fields

This is an example of how you could compare two fields to see if they have the same value. A good example of this is when you are validating a user changing their password and there is the main password field and a confirmation field.

<validator name="twofields"
       classname="com.mysite.StrutsValidator"
       method="validateTwoFields"
       msg="errors.twofields"/>

<field property="password"
       depends="required,twofields">
          <arg0 key="typeForm.password.displayname"/>
          <var>
             <var-name>secondProperty</var-name>
             <var-value>password2</var-value>
          </var>
</field>

public static boolean validateTwoFields(Object bean,
    ValidatorAction va, Field field,
    ActionErrors errors,
    HttpServletRequest request, ServletContext application) {
   
    String value = ValidatorUtil.getValueAsString(bean, field.getProperty());
    String sProperty2 = field.getVarValue("secondProperty");
    String value2 = ValidatorUtil.getValueAsString(bean, sProperty2);
   
    if (!GenericValidator.isBlankOrNull(value)) {
       try {
          if (!value.equals(value2)) {
             errors.add(field.getKey(), ValidatorUtil.getActionError(application, request, va, field));

             return false;
          }
       } catch (Exception e) {
             errors.add(field.getKey(), ValidatorUtil.getActionError(application, request, va, field));
             return false;
       }
    }
   
    return true;
}

Validating Outside of Struts

Here is a short example of validating something outside of the Struts Framework. The validator element's methodParams attribute have a default method signature that all the StrutsValidator validation methods use.

<form-validation>
    <global>
       <validator name="capLetter"
             classname="com.mysite.Validator"
             methodParams="java.lang.Object,org.apache.commons.validator.Field,java.util.List"
             method="isCapLetter"
             msg="Letter is not in upper case."/>
    </global>
    <formset>
        <form name="testForm">
            <field property="letter"
                     depends="capLetter">
            </field>
        </form>
    </formset>
</form-validation>

The method signature and parameters are dynamically created based on the methodParams and the resources added to the Validator using the class name as a key. The class "java.lang.Object" is reserved for the bean that is being validated and there can't be any duplicate class names because they are used as the key when associating the actual instance of the class when setting up the Validator.

The ValidatorAction and the Field are automatically passed in if specified in the methodParams attribute. The other instances of classes need to be added to the Validator using the addResource method along with the class they represent from the validator element's methodParams attribute. Bean is the object being validated. The ValidatorResourcesInitializer can be used to load the validation.xml file and return an instance of ValidatorResources based on the xml file. So based on the validation.xml file defined above the getLetter method will be called on the bean variable.

    ValidatorResources resources = ValidatorResourcesInitializer.initialize("validation.xml", debug);
    Validator validator = new Validator(resources, "testForm");
    validator.addResource(Validator.BEAN_KEY, bean);
    validator.addResource("java.util.List", lErrors);

    try {
       validator.validate();
    } catch (ValidatorException e) {
       // Log Exception
    }

This is the validation method being used by the capLetter validator. The validation fails if the value retrieved is null, has a length other than one, and the character doesn't fall in the range A-Z. Error messages are added to the java.util.List that is passed into the method.

    public static boolean isCapLetter(Object bean, Field field, List l) {
       String value = ValidatorUtil.getValueAsString(bean, field.getProperty());
   
       if (value != null && value.length() == 1) {
          if (value.charAt(0) >= 'A' && value.charAt(0) <= 'Z') {
             return true;
          } else {
             l.add(field.getMsg);
             return false;
          }
       } else {
          l.add(field.getMsg);
          return false;
       }
    }