11.3 Using an ActionForm with the Validator

You can't use the standard Struts ActionForm class with the Validator. Instead, you need to use a subclass of the ActionForm class that is specifically designed to work with the Validator framework. There are two root subclasses to select from, depending on whether you are planning to use dynamic ActionForms. Figure 11-1 shows the ActionForm and its descendants, to help you visualize the hierarchy.

Figure 11-1. The ActionForm class hierarchy

figs/jstr_1101.gif

If you are using dynamic ActionForms, you should use the DynaValidatorForm branch of the hierarchy. If you are using standard ActionForms, you can use the ValidatorForm or one of its descendants instead.

Whether you use dynamic or regular ActionForms, the manner in which you configure the Validator is the same. Just be sure that whichever ActionForm subclass you choose, you configure the form-bean section of the Struts configuration file using the fully qualified class name. See Section 4.7.2.2 for more details.

Dynamic or standard is only the first decision that you have to make when choosing the proper ActionForm subclass. Notice that in both the dynamic and standard branch of the ActionForm hierarchy in Figure 11-1, there are two versions of ValidatorForm. The parent class is called ValidatorForm, or DynaValidatorForm for the dynamic branch.

Each of these has a subclass that contains the name Action in its title. The subclass of the ValidatorForm is called ValidatorActionForm , and the subclass of the DynaValidatorForm is called DynaValidatorActionForm . The purpose of the two different versions is to allow you to associate the validation with the form-bean definition or the action definition. The ValidatorActionForm and DynaValidatorActionForm classes pass the path attribute from the action element into the Validator, and the Validator uses the action's name to look up the validation rules. If you use the ValidatorForm or DynaValidatorForm, the name of the ActionForm is used to look up the set of validation rules to use. The only reason for using one or the other is to have more fine-grained control over which validation rules are executed.

For example, suppose that an ActionForm contains three different validation rules, but only two of them should get executed for a particular action. You could configure the rules to perform only the subset of validation rules when that action gets invoked. Otherwise, all of the rules would be invoked. In general, using ValidatorForm or DynaValidatorForm should be sufficient for your needs.

Let's look at a more complete example of using the Validator framework. As in previous chapters, we'll employ the Storefront application to help us understand the Validator better. In particular, we'll look at the HTML form used to capture the shipping information during checkout of the Storefront application. This is shown in Figure 11-2.

Figure 11-2. Capturing the shipping address information

figs/jstr_1102.gif

For this example, we are going to use a dynamic form. Therefore, we'll use the DynaValidatorForm class to capture the shipping address details. Because the checkout process will span multiple pages and we want to capture this information across pages, we will need to configure the form bean to have session scope. We will also capture all of the checkout properties in a single ActionForm class. Instead of having a ShippingForm and a CreditCardForm, we will have a single form called CheckoutForm that captures all of the information.

In our Struts configuration file, we set up the checkoutForm as shown here:

<form-bean
  name="checkoutForm"
  type="org.apache.struts.validator.DynaValidatorForm">
  <form-property name="firstName" type="java.lang.String"/>
  <form-property name="lastName" type="java.lang.String"/>
  <form-property name="address" type="java.lang.String"/>    
  <form-property name="city" type="java.lang.String"/>
  <form-property name="state" type="java.lang.String"/>
  <form-property name="postalCode" type="java.lang.String"/>
  <form-property name="country" type="java.lang.String"/>
  <form-property name="phone" type="java.lang.String"/>    
</form-bean>

The type attribute specifies the exact ActionForm subclass.

In early beta releases of Struts 1.1, the form-bean section required that you set the dynamic attribute to true when using dynamic ActionForms. This is no longer necessary, as the framework will determine whether the class specified in the type attribute is a descendant of the DynaActionForm class.

The next step is to edit the application-specific validation logic, which is done in the validation.xml file. You must declare a validation rule for each property in the form that you need to validate. In some cases, you might need to specify multiple rules. In Figure 11-2, for example, the phone field is required, and it must fit a specific format. These are two separate rules that both must evaluate to true, or the validation for the form will fail. The entire validation.xml file is not shown because it's too large and most of it is redundant. The section shown in Example 11-2 will help you understand how things are connected.

Example 11-2. A sample validation.xml file for the checkout form
<formset>
  <constant>
    <constant-name>phone</constant-name>
    <constant-value>^\(?(\d{3})\)?[-| ]?(\d{3})[-| ]?(\d{4})$</constant-value>
  </constant>                 
  <constant>
    <constant-name>zip</constant-name>
    <constant-value>^\d{5}(-\d{4})?$</constant-value>
  </constant>                       
  <form name="checkoutForm">
    <field 
      property="firstName"
      depends="required,mask">
      <arg0 key="label.firstName"/>           
      <var>
        <var-name>mask</var-name>
        <var-value>^[a-zA-Z]*$</var-value>
      </var>                  
    </field>
    <field 
      property="postalCode"
      depends="required,mask">
      <arg0 key="registrationForm.zip"/>
      <var>
        <var-name>mask</var-name>
        <var-value>${zip}</var-value>
      </var>
    </field>
    <field 
      property="phone"
      depends="required,mask">
      <arg0 key="registrationForm.phone"/>
      <var>
        <var-name>mask</var-name>
        <var-value>${phone}</var-value>
      </var>
    </field>         
  </form>            
 </formset>   
</form-validation>

Now that we have everything configured for the Storefront, it's time to run the example. The nice thing about using a declarative approach versus a programmatic one is that once you have everything configured, you're ready to go. The absence of programming makes the declarative approach much simpler. This is especially true for the Validator framework. There's nothing to code, as long as the default validation rules satisfy your requirements.

When we submit the shipping address page with no information in the fields, the validation rules kick in. The result is shown in Figure 11-3.

Figure 11-3. The shipping address page using the Validator framework

figs/jstr_1103.gif