Download PDF Home History |  | API Search Feedback FAQ

> ## 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, 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 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 implementation, you can forgo creating a custom tag and instead let the page author register the validator on a component using a `validator` tag. This tag simply refers to the `Validator` implementation, which handles the configuration of the validator's attributes. See Using a Custom Validator for information on how the page author uses a custom validator in the page.

> Usually, you will want to display an error message when data fails validation. You need to store these error messages in a `ResourceBundle`. For more information on creating a `ResourceBundle`, see Creating a Resource Bundle.

> When validation fails, you can queue the error messages onto the `FacesContext` programmatically. Alternatively, you can have the application architect register the error messages using the application configuration resource file. Registering Messages explains how to register error messages with the application.

> 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 either:

> - 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 attribute `formatPatterns`, 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 `UIInput` component or a component that extends `UIInput`.

> 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)patternIt.next()), 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 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 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 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 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 `ValidatorTag` class. This class is the base class for all custom tag handlers that create `Validator` instances and register them on UI components. The `FormatValidatorTag` is the class that registers the `FormatValidator` instance onto the component.

> The `FormatValidatorTag` tag handler class does the following:

> - Sets the ID of the `Validator` by calling `super.setValidatorId("FormatValidator")`.
> - Provides a set of accessor methods for each attribute defined on the tag.
> - Implements the `createValidator` method of the `ValidatorTag` class. This method creates an instance of the validator and sets the range of values accepted by the validator.

> Here is the `createValidator` method from `FormatValidatorTag`:

>     protected Validator createValidator() throws JspException {
>       FormatValidator result = null;
>       result = (FormatValidator) super.createValidator();
>       result.setFormatPatterns(formatPatterns);
>       return result;
>     }

> This method first calls `super.createValidator` to get a new `Validator` instance and casts it to `FormatValidator`.

> Next, the tag handler sets the `Validator` instance's attribute values to those supplied as the values of the `formatPatterns` tag attribute. The handler gets the attribute values from the page via the accessor methods that correspond to the attributes.

> #### 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 for more information about TLDs.

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

>     <tag>
>       <name>formatValidator</name>
>       ...
>       <tag-class>taglib.FormatValidatorTag</tag-class>
>       <attribute>
>         <name>formatPatterns</name>
>         <required>true</required>
>         <rtexprvalue>false</rtexprvalue>
>         <type>String</type>
>       </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.

Download PDF Home History |  | API Search Feedback FAQ

All of the material in The J2EE(TM) 1.4 Tutorial is copyright-protected and may not be published in other works without express written permission from Sun Microsystems.