The Java EE 5 Tutorial

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)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.