Skip to main content

WebWork Validation

January 19, 2006

{cs.r.title}









Contents
Setting Up the Sample Application
Manually Validating Forms
   Validating Inside the execute() Method
   Implementing the Validatable Interface
Using Built-In Validators
   Configuring Interceptors
   Specifying Validation Rules
Visitor Field Validation
Displaying Error Messages
Custom Validators
Client-Side Validation
Conclusion
Resources

OpenSymphony's
WebWork is a web
application framework designed to keep productivity high and the
code simple. It has gained popularity for several reasons, including
its integration with "http://www.springframework.org/">Spring, a powerful tag
library, and OGNL
support. Its powerful validation framework is borrowed from another
OpenSymphony project, "https://xwork.dev.java.net/">XWork.

In this article, we will explore the various validation features
of WebWork/XWork, including custom and conditional validation. A
sample application that contains working examples of the different
validation techniques is also included. I will also show how to
perform "re-usable validation" using WebWork's visitor validation.
This article assumes that you are somewhat familiar with WebWork
and will focus on the validation aspects of the framework rather
than the basics.

Setting Up the Sample Application

You can download the sample application from the "?page=3#resources">Resources section. The sample application uses
"http://www.opensymphony.com/webwork/download.action">WebWork 2.2
Beta 3
, which comes packaged with XWork 1.0.5. The
build.xml file has instructions on how to make the .war
file using Apache Ant. The .war
file can be deployed in any supporting servlet container. I'm using
Apache Tomcat 5.5.12.
Copying the .war file into Tomcat's webapps directory should
deploy the application, which can then be accessed at the following
URL:

[prettify]
http://localhost:8080/webworkapp
[/prettify]

If you see a listing of examples by navigating to this page, all
is well.

Manually Validating Forms

The straightforward way to validate user input is to write Java
code to check the request parameters. WebWork allows you to do this
using two different methods.

Validating Inside the execute() Method

The simplest way to validate user input is to verify the request
parameters inside of the execute() method of your WebWork
actions, as shown in the following example
(com...ex1.actions.UserSignupAction).

[prettify]
public String execute() {
  boolean validationPassed = true;
  if (StringUtils.isBlank(user.getName())) {
    addActionError(getText("user.name.empty"));
    validationPassed = false;
  }
  if (StringUtils.isBlank(user.getAddress())) {
    addActionError(getText("user.address.empty"));
    validationPassed = false;
  }
  return (validationPassed ? SUCCESS : INPUT);
}
[/prettify]

However, this technique involves putting validation code in the
execute() method, which is undesirable as it mixes
business logic with validation. A good alternate is to have your
action class implement the Validatable interface.

Implementing the Validatable Interface

Instead of validating inside the execute() method,
you can have the action class implement the
Validatable interface, which requires the implementing
class to define a method with the following signature:

[prettify]
public void validate();
[/prettify]

As shown below, the validation code for the action resides in
validate() instead of execute(), thus
separating validation from business logic.

[prettify]

public String execute() {
  return SUCCESS;
}
public void validate() {
  User user = getUser();
  if (StringUtils.isBlank(user.getName())) {
    addActionError(getText("user.name.empty"));
  }
  if (StringUtils.isBlank(user.getAddress())) {
    addActionError(getText("user.address.empty"));
  }
}
[/prettify]

Even though the validation code has moved to a much more
suitable place, we still find ourselves doing too much work by
manually comparing request parameters to determine the validity of
our form input. Fortunately, WebWork provides a finely tuned
validation framework, which helps developers avoid the chore of
writing code for each validation scenario.

Using Built-in Validators

Using built-in validation is a two-step process:

  1. You must tell WebWork the action that you want to validate.
    • Accomplished via interceptors.
  2. You must tell WebWork what the validation rules are.
    • Accomplished by specifying rules in an XML file.
Configuring Interceptors

One of the concepts at the foundation of WebWork is that of
interceptors. Interceptors allow various tasks to be
performed when an action is invoked. Once such task is validation.
When we want validation to occur for our actions, we must ensure
that WebWork's validator interceptor is in the interceptor stack
specified in xwork.xml.

The built-in validationWorkflowStack includes the
validator stack and will suffice for our validation needs. Instead
of specifying the validationWorkflowStack for each
action, I've made it the default for all actions by using the
element.

However, we don't want to perform validation for all actions
(e.g., when simply viewing the form), and for these cases, I've
explicitly overridden the validationWorkflowStack with
basicStack as shown below:

[prettify]
<action name="viewEx3"
  class="ex3UserSignupAction"
  method="viewInputForm">
  <result name="success">
    /WEB-INF/pages/ex3/input.jsp</result>
  <interceptor-ref name="basicStack"/>
</action>
[/prettify]

This might not be ideal in terms of dealing with the workflow of
your application, but it serves its purpose in the sample
application. You should have a good grip on "http://www.opensymphony.com/webwork/wikidocs/Interceptors.html">interceptors
before you start working with WebWork.

Tip: In many cases, it is desirable to write a custom
interceptor that ignores validation on all GET requests, thus
saving the chore of having to specify a basicStack
interceptor for each GET action.







Specifying Validation Rules

WebWork provides built-in validators that cover most cases of
form validation. They are defined in the
web/WEB-INF/classes/validators.xml file and are on Open
Symphony's "http://www.opensymphony.com/webwork/wikidocs/Simple%20validators.html">
Simple Validators wiki page
. You may also specify your own
custom validators by adding to this file (more on this later).

The following example validates a user signup
(com...ex3.vo.User) using the
com...ex3.action.UserSignupAction action. The rules to
validate a particular action class are stored in an XML file named
after the action, in one of three possible formats:

  1. Based on the Action class:
    UserSignupAction-validation.xml
  2. Based on an Action alias:
    UserSignupAction-createUser-validation.xml
  3. Considering the inheritance hierarchy and the interfaces
    implemented by the Action class.

This example uses the first option. WebWork searches for the
rules file in the action's classpath, in this case at
com\esolaria\webworkapp\ex3\action\UserSignupAction-validation.xml.
The rules file below utilizes some of the built-in validators to
validate a User's signup process.

[prettify]
<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN"
"http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
<validators>
  <field name="user.name">
    <field-validator type="requiredstring">
      <message key="user.name.empty">
      resource not found</message>
    </field-validator>
  </field>
  <field name="user.email">
    <field-validator type="requiredstring"
         short-circuit="true">
      <message key="user.email.empty"/>
    </field-validator>
    <field-validator type="email">
      <message key="user.email.invalid"/>
    </field-validator>
  </field>
  <field name="user.phone">
    <field-validator type="stringlength">
      <param name="minLength">10</param>
      <message key="user.phone.length"/>
    </field-validator>
  </field>
  <field name="user.city">
    <field-validator type="requiredstring">
      <message key="user.city.empty"/>
    </field-validator>
  </field>
  <field name="user.favoritePositiveNumber">
    <field-validator type="int">
      <param name="min">1</param>
      <message key="user.favoritePositiveNumber.invalid"/>
    </field-validator>
  </field>
  <field name="user.password">
    <field-validator type="requiredstring">
      <message key="user.password.empty"/>
    </field-validator>
  </field>
  <field name="user.confirmPassword">
    <field-validator type="requiredstring">
      <message key="user.confirmPassword.empty"/>
    </field-validator>
  </field>
    <validator type="expression">
    <param name="expression">
      user.password.equals(user.confirmPassword)</param>
    <message key="user.confirmPassword.nomatch"/>
  </validator>
</validators>
[/prettify]

Let's analyze some of the salient aspects of the validation
file:

Presence validation: The requiredstring validator
forces user input to be a non-empty string. On the other hand, the
required validator only forces the user input to be
non-null. In most cases, requiredstring is the one to
use.

Integer validation: The int validator can be used
in two different ways: with min and max
ranges and without them. If min and max
are specified, the validator forces the input to be an integer
between the two specified values. If only one of them is specified, the validator only considers one
of the bounds. Validation for the
favoritePositiveNumber field is an example of this.
The stringlength validator works in a similar way.

Expression validation: One of the most common scenarios in
web applications is making the user enter a password twice. In
WebWork, comparison between fields when validating is accommodated
using the expression validator. In the above example, the
values of password and confirmPassword
are being compared for equality. Other operators you can use in the
expression field include < (<),
> (&rt;), startsWith(), and
endsWith(), amongst others.

"Short circuiting:" Two validation rules are applied when
validating an email: one for existence of data, and
one to make sure that the data supplied is a valid email. If the
user fails to provide any data for the email, it is pointless to
check the input to be of the correct format. The
short-circuit attribute of the
element prevents any further
validation rules being applied if the current one fails. In a
scenario where the user leaves the email field blank, the user will
receive only one error message.

Validator types: Notice the two different types of
validators: and
. Error messages associated with a
are stored relative to the
field being validated. Errors messages associated with
are stored in an action-level
Collection. This is further discussed in the
"Displaying Error Messages" section later on in this
article.

Internationalization:You can specify a resource key to
display the error message for a field. If the resource key does not
exist, the contents of the tag will be
used. By default, WebWork looks in a file called
.properties in the same package for the
resource bundle (in this case, UserSignupAction.properties).
Parameters defined in the validator can also be accessed in the
resource bundle as shown when defining the error message for the
favoritePositiveNumber field.

Visitor Field Validation

It is often the case that the form being validated maps to a
business object in our model layer. For example, in the above case,
UserSignupAction action is in actuality validating the
com...ex3.vo.User object. Suppose now that we want to
implement a UserSaveAction where a user can save their
already-existing information. The validation rule file,
UserSaveAction-validation.xml, would be very similar (if not
identical) to UserSignupAction-validation.xml, since the same
fields are being validated. To avoid this duplication of code,
WebWork has a concept called "visitor field validation," which
allows re-use of validation based on business objects.

The following example shows a user signup and a user save
scenario using visitor validation. We supply a validation file that
has rules to validate properties of a business object. In the
example, the business object com...ex4.vo.User is a
member variable of both the
com...ex4.action.UserSignupAction and
com...ex4.action.UserSaveAction. Both will validate
against the rules specified in the User's validation
file.

The earlier naming convention still applies and the validation
file User-validation.xml resides in the same package as
User. The User-validation.xml file has the following
contents:

[prettify]
<validators>
  <field name="user.firstName">
    <field-validator type="requiredstring">
      <message key="user.firstName.empty"/>
    </field-validator>
  </field>
  <field name="user.lastName">
    <field-validator type="requiredstring">
      <message key="user.lastName.empty"/>
    </field-validator>
  </field>
  <field name="user.email">
    <field-validator type="requiredstring"
       short-circuit="true">
      <message key="user.email.empty"/>
    </field-validator>
    <field-validator type="email"
       short-circuit="true">
      <message key="user.email.invalid"/>
    </field-validator>
  </field>
</validators>
[/prettify]

Now that the way to validate a user is defined, all that is left
is telling the action classes to invoke the validation on their
user member variables. This is done by specifying the
following in the actions' validation files,
UserSignupAction-validation.xml and
UserSaveAction-validation.xml.

[prettify]
<validators>
  <field name="user">
    <field-validator type="visitor">
      <message/>
    </field-validator>
  </field>
</validators>
[/prettify]

Input is validated using the User-validation.xml file for
both actions.







Displaying Error Messages

WebWork stores validation error messages in two different places,
depending on the error. Errors that were added using
addActionError() can be retrieved with
getActionErrors(), while errors that were added using
addFieldError() can be retrieved with
getFieldErrors(). When validating fields using XML
files, WebWork uses addFieldError() to populate the
errors.

When displaying messages, we must check both cases. The method
getActionErrors() returns a List of
Strings for us to easily loop over. However,
getFieldErrors() returns a Map of
Lists where the keys of the map correspond to the
fields and the values correspond to the error messages associated
with the field. In order to display all field-level messages, a
nested loop is required.

[prettify]
<ww:if test="hasErrors()">
  <p style="color: red;">
  <b>Errors:</b>
  <ul>
  <ww:if test="hasActionErrors()">
  <ww:iterator value="actionErrors">
    <li style="color: red;"><ww:property/></li>
  </ww:iterator>
  </ww:if>
  <ww:if test="hasFieldErrors()">
  <ww:iterator value="fieldErrors">
    <ww:iterator value="value">
    <li style="color: red;"><ww:property/></li>
    </ww:iterator>
  </ww:iterator>
  </ww:if>
  </ul>
  </p>
</ww:if>
[/prettify]

The benefit of adding field-level errors is that it associates
errors with an input field, allowing errors to be displayed next to
the input field. Using the default theme attribute in
the input type displays error messages next to the field. However,
in the earlier example, I've set the theme attribute
to simple, which does not do this. The disadvantage of
using field-specific errors is that if you want to display the
messages in a nested for loop (like above), they will not come in
order, since a Map's keys are being iterated over.

Action-level errors are stored in a List and will
print in the order in which they were added.

Custom Validators

When none of WebWork's built-in validators fit your business
needs, you can quite easily declare a custom validator and have it
behave the way you want. It can be invoked and used just like the
built-in validators.

To create a custom validator, you must define a class that
implements the Validator interface, which contains
several methods. Fortunately for us, in most cases we don't need to
implement all of the methods and instead can extend the
ValidatorSupport class, which is an abstract class
implementing the Validator interface. All we are left
to do is implement the validate(Object) method.
Example 5 uses a custom validator to force the user to enter a
Canadian postal code if Canada is selected as the country, and an
American one if America is the chosen country.

Let's have a look at the validate(Object) method to
see how this can be done.

[prettify]
public void validate(Object object)
          throws ValidationException {
  String country = (String)
  getFieldValue("country", object);
  String zipCode = (String)
  getFieldValue("zipCode", object);
  ValidatorContext ctxt = getValidatorContext();
  if ("CAN".equals(country)) {
    if (!Pattern.matches(
      "[a-zA-Z]\\d[a-zA-Z]\\d[a-zA-Z]\\d",
      zipCode)) {
      ctxt.addActionError(
          "Invalid Canadian Zip code (Eg: A1B2C3)");
      return;
    }
  } else if ("US".equals(country)) {
    if (!Pattern.matches("\\d{5}", zipCode)) {
      ctxt.addActionError(
      "Invalid US Zip Code  (Eg: 64145)");
      return;
    }
  }
}
[/prettify]

To use your custom validator, you must first register it by
adding an entry for it in the validators.xml file.

[prettify]
<validator name="zipcode"
   class="com.esolaria.webworkapp.ex5.ZipCodeValidator"/>
[/prettify]

The alias given to the validator is zipcode, which is what
we will refer to it by in UserSignupAction-validation.xml
file, as shown below:

[prettify]
<field name="country">
  <field-validator type="requiredstring">
    <message>Please select a country</message>
  </field-validator>
</field>
<validator type="zipcode">
  <message/>
</validator>
[/prettify]

Client-Side Validation

WebWork provides client-side validation via the
DWRValidator class. It uses "https://dwr.dev.java.net/">Direct Web Remoting to validate
fields on onblur events. To use client-side
validation, the should read:

[prettify]
<ww:form action="submitForm" name="submitForm"
   validate="true">
  <ww:text name="name" required="true"/>
  <ww:submit name="btn" value="Submit" />
</ww:form>
[/prettify]

Lack of proper client-side validation is one of the shortcomings of
WebWork and will undoubtedly be addressed when it is "http://blogs.opensymphony.com/webwork/2005/11/webwork_joining_struts.html">
merged with Struts
. Using Direct Web Remoting in this manner
has performance and load issues that can be avoided by using simple
JavaScript validation. If you want client-side validation, the
Jakarta
Commons Validator
is a good option.

Conclusion

You should now have a good understanding of how to validate web
forms using WebWork's validation framework. The authoritative text
on WebWork is "http://www.manning.com/books/lightbody">WebWork in
Action
, written by its creators Patrick Lightbody and Jason
Carreira. It is a valuable resource for anyone working with WebWork
and also covers validation in greater detail.

Resources

width="1" height="1" border="0" alt=" " />
Zarar Siddiqi is an analyst for the University of Toronto.
Related Topics >> Programming   |   Web Development Tools   |