Creating a Custom Validator

If the standard validators don't perform the validation checking you need, you can easily create a custom validator to validate user input. As explained in Validation Model (page 301), there are two ways to implement validation code:

  • Implement a backing bean method that performs the validation.

  • Provide an implementation of the Validator interface to perform the validation.

Writing a Method to Perform Validation (page 421) explains how to implement a backing bean method to perform validation. The rest of this section explains how to implement the Validator interface.

If you choose to implement the Validator interface and you want to allow the page author to configure the validator's attributes from the page, you also must create a custom tag for registering the validator on a component.

If you prefer to configure the attributes in the Validator implementation, you can forgo creating a custom tag and instead let the page author register the validator on a component using the validator tag, as described in Using a Custom Validator (page 385).

You can also create a backing bean property that accepts and returns the Validator implementation you create as described in Writing Properties Bound to Converters, Listeners, or Validators (page 401). The page author can use the validator tag's binding attribute to bind the Validator implementation to the backing bean property.

Usually, you will want to display an error message when data fails validation. You need to store these error messages in resource bundle, as described in Creating a Resource Bundle (page 402).

After creating the resource bundle, you have two ways to make the messages available to the application. You can queue the error messages onto the FacesContext programmatically. Or, you can have the application architect register the error messages using the application configuration resource file, as explained in Registering Custom Error Messages (page 470).

The Duke's Bookstore application uses a general-purpose custom validator (called FormatValidator) that validates input data against a format pattern that is specified in the custom validator tag. This validator is used with the Credit Card Number field on the bookcashier.jsp page. Here is the custom validator tag:

   <bookstore:formatValidator      formatPatterns="9999999999999999|9999 9999 9999 9999|         9999-9999-9999-9999"/>

According to this validator, the data entered in the field must be one of the following:

  • A 16-digit number with no spaces

  • A 16-digit number with a space between every four digits

  • A 16-digit number with hyphens between every four digits

The rest of this section describes how this validator is implemented and how to create a custom tag so that the page author can register the validator on a component.

Implementing the Validator Interface

A Validator implementation must contain a constructor, a set of accessor methods for any attributes on the tag, and a validate method, which overrides the validate method of the Validator interface.

The FormatValidator class also defines accessor methods for setting the formatPatterns attribute, which specifies the acceptable format patterns for input into the fields. In addition, the class overrides the validate method of the Validator interface. This method validates the input and also accesses the custom error messages to be displayed when the String is invalid.

The validate method performs the actual validation of the data. It takes the FacesContext instance, the component whose data needs to be validated, and the value that needs to be validated. A validator can validate only data of a component that implements EditableValueHolder.

Here is the validate method from FormatValidator:

   public void validate(FacesContext context, UIComponent    component, Object toValidate) {      boolean valid = false;      String value = null;      if ((context == null) || (component == null)) {         throw new NullPointerException();      }      if (!(component instanceof UIInput)) {         return;      }      if ( null == formatPatternsList || null == toValidate) {         return;      }      value = toValidate.toString();      //validate the value against the list of valid patterns.      Iterator patternIt = formatPatternsList.iterator();      while (patternIt.hasNext()) {         valid = isFormatValid(            ((String), value);         if (valid) {            break;         }      }      if ( !valid ) {         FacesMessage errMsg =            MessageFactory.getMessage(context,              FORMAT_INVALID_MESSAGE_ID,                 (new Object[] {formatPatterns}));            throw new ValidatorException(errMsg);      }    }

This method gets the local value of the component and converts it to a String. It then iterates over the formatPatternsList list, which is the list of acceptable patterns as specified in the formatPatterns attribute of the custom validator tag.

While iterating over the list, this method checks the pattern of the component's local value against the patterns in the list. If the pattern of the local value does not match any pattern in the list, this method generates an error message. It then passes the message to the constructor of ValidatorException. Eventually the message is queued onto the FacesContext instance so that the message is displayed on the page during the render response phase.

The error messages are retrieved from the Application instance by MessageFactory. An application that creates its own custom messages must provide a class, such as MessageFactory, that retrieves the messages from the Application instance. When creating your own application, you can simply copy the MessageFactory class from the Duke's Bookstore application to your application.

The getMessage(FacesContext, String, Object) method of MessageFactory takes a FacesContext, a static String that represents the key into the Properties file, and the format pattern as an Object. The key corresponds to the static message ID in the FormatValidator class:

   public static final String FORMAT_INVALID_MESSAGE_ID =      "FormatInvalid";    }

When the error message is displayed, the format pattern will be substituted for the {0} in the error message, which, in English, is

   Input must match one of the following patterns {0}

JavaServer Faces applications can save the state of validators and components on either the client or the server. Specifying Where State Is Saved (page 487) explains how to configure your application to save state on either the client or the server.

If your JavaServer Faces application saves state on the client (which is the default), you need to make the Validator implementation implement StateHolder as well as Validator. In addition to implementing StateHolder, the Validator implementation needs to implement the saveState(FacesContext) and restoreState(FacesContext, Object) methods of StateHolder. With these methods, the Validator implementation tells the JavaServer Faces implementation which attributes of the Validator implementation to save and restore across multiple requests.

To save a set of values, you must implement the saveState(FacesContext) method. This method is called during the render response phase, during which the state of the response is saved for processing on subsequent requests. When implementing the saveState(FacesContext) method, you need to create an array of objects and add the values of the attributes you want to save to the array. Here is the saveState(FacesContext) method from FormatValidator:

   public Object saveState(FacesContext context) {      Object values[] = new Object[2];      values[0] = formatPatterns;      values[1] = formatPatternsList;      return (values);    }

To restore the state saved with the saveState(FacesContext) method in preparation for the next postback, the Validator implementation implements restoreState(FacesContext, Object). The restoreState(FacesContext, Object) method takes the FacesContext instance and an Object instance, which represents the array that is holding the state for the Validator implementation. This method sets the Validator implementation's properties to the values saved in the Object array. Here is the restoreState(FacesContext, Object) method from FormatValidator:

   public void restoreState(FacesContext context, Object state){      Object values[] = (Object[]) state;      formatPatterns = (String) values[0];      formatPatternsList = (ArrayList) values[1];    }

As part of implementing StateHolder, the custom Validator implementation must also override the isTransient and setTransient(boolean) methods of StateHolder. By default, transientValue is false, which means that the Validator implementation will have its state information saved and restored. Here are the isTransient and setTransient(boolean) methods of FormatValidator:

   private boolean transientValue = false;    public boolean isTransient() {      return (this.transientValue);    }    public void setTransient(boolean transientValue) {      this.transientValue = transientValue;    }

Saving and Restoring State (page 445) describes how a custom component must implement the saveState(FacesContext) and restoreState(FacesContext, Object) methods.

Creating a Custom Tag

If you implemented a Validator interface rather than implementing a backing bean method that performs the validation, you need to do one of the following:

  • Allow the page author to specify the Validator implementation to use with the validator tag. In this case, the Validator implementation must define its own properties. Using a Custom Validator (page 385) explains how to use the validator tag.

  • Create a custom tag that provides attributes for configuring the properties of the validator from the page. Because the Validator implementation from the preceding section does not define its attributes, the application developer must create a custom tag so that the page author can define the format patterns in the tag.

To create a custom tag, you need to do two things:

  • Write a tag handler to create and register the Validator implementation on the component.

  • Write a TLD to define the tag and its attributes.

Using a Custom Validator (page 385) explains how to use the custom validator tag on the page.

Writing the Tag Handler

The tag handler associated with a custom validator tag must extend the ValidatorELTag class. This class is the base class for all custom tag handlers that create Validator instances and register them on UI components. The FormatValidatorTag class registers the FormatValidator instance onto the component.

The FormatValidatorTag tag handler class does the following:

  • Sets the ID of the validator.

  • Provides a set of accessor methods for each attribute defined on the tag.

  • Implements the createValidator method of the ValidatorELTag class. This method creates an instance of the validator and sets the range of values accepted by the validator.

The formatPatterns attribute of the fomatValidator tag supports literals and value expressions. Therefore, the accessor method for this attribute in the FormatValidatorTag class must accept and return an instance of ValueExpression:

   protected ValueExpression formatPatterns = null;    public void setFormatPatterns(ValueExpression fmtPatterns){      formatPatterns = fmtPatterns;    }

Finally, the createValidator method creates an instance of FormatValidator, exTRacts the value from the formatPatterns attribute's value expression and sets the formatPatterns property of FormatValidator to this value:

the formatPatterns property of FormatValidator to this value:

   protected Validator createValidator() throws JspException {      FacesContext facesContext =        FacesContext.getCurrentInstance();      FormatValidator result = null;      if(validatorID != null){        result = (FormatValidator) facesContext.getApplication()           .createValidator(validatorID);     }     String patterns = null;     if (formatPatterns != null) {        if (!formatPatterns.isLiteralText()) {           patterns = (String)           formatPatterns.getValue(facesContext.getELContext());        } else {          patterns = formatPatterns.getExpressionString();        }

Writing the Tag Library Descriptor

To define a tag, you declare it in a tag library descriptor (TLD), which is an XML document that describes a tag library. A TLD contains information about a library and each tag contained in it. See Tag Library Descriptors (page 229) for more information about TLDs.

The custom validator tag is defined in bookstore.tld, located in <INSTALL>/javaeetutorial5/examples/web/bookstore6/web/ directory. It contains a tag definition for formatValidator:

   <tag>      <name>formatValidator</name>      ...      <tag-class>         com.sun.bookstore6.taglib.FormatValidatorTag</tag-class>      <attribute>         <name>formatPatterns</name>         <required>true</required>         <deferred-value>            <type>String</type>         </deferred-value>      </attribute>    </tag>

The name element defines the name of the tag as it must be used in the page. The tag-class element defines the tag handler class. The attribute elements define each of the tag's attributes. The formatPatterns attribute is the only attribute that the tag supports. The deferred-value element indicates that the formatPatterns attribute accepts deferred value expressions. The type element says that the expression resolves to a property of type String.

The JavaT EE 5 Tutorial
The JavaT EE 5 Tutorial
Year: 2004
Pages: 309 © 2008-2017.
If you may any questions please contact us: