Java Quiz Player

JSF: Custom Validation Tag Example

Sep 27, 2014

This page has the web application's source code and a screenshot of the user interface.

Source Code

(a) Usage page index.xhtml, (b) the managed bean that specifies the format attributes FormatsBean.java, (c) the custom validator implementation PhoneValidator.java, (d) the tag library descriptor for the custom validator tag app.taglib.xml, (e) the deployment descriptor specifying the location of the tag library web.xml, (f) an empty application configuration file faces-config.xml, and (g) the web application WAR file contents.

(a) Usage page: index.xhtml

<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:app="http://jsfapp.com"/>
  <h:head>
    <title>Custom Validator</title>
  </h:head>
  <h:body>
  <h:form>
    <h3>Custom Validator</h3>
    <h:panelGrid columns="1">
    <h:outputLabel for="phoneNo"
      value="Enter phone number with format as 9999999999 or 999-999-9999:"/>
    <h:inputText id="phoneNo" value="#{requestScope.phone}"
      required="true" label="Phone Number">
      <app:validatePhone formats="#{bean.formats}" />
    </h:inputText>
    <h:commandButton value="Submit" />
    </h:panelGrid>
    <h:outputText value="#{requestScope.phone}" style="color: blue" />
    <h:messages id="msg" for="phoneNo" showDetail="true" showSummary="false"
				style="color:red" />
  </h:form>
  </h:body>
</html>

(b) Formats Bean: FormatsBean.java

package app;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
@RequestScoped
@ManagedBean(name="bean")
public class FormatsBean {
  private String formats= "999-999-9999,999999999";
  public String getFormats() {
    return formats;
  }
}

(c) Custom Validator Implementation: PhoneValidator.java

package app;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.*;
import java.util.*;;
@FacesValidator("app.phoneValidator")
public class PhoneValidator implements Validator {
  // all valid formats
  private static Map<String, String> formatMap = new HashMap<>();
  static {
    formatMap.put("default", "9999999999");
    formatMap.put("hyphen-separated", "999-999-9999");
    formatMap.put("space-separated", "999 999 9999");
  }
  // exception message strings
  private static String summaryMsg = "Invalid phone numer.";
  private static String detailMsg =
  "The phone number is not valid. Please verify the format and enter again.";
  // attribute values specified in the validator tag, and the setter method
  private String formats; 
  public void setFormats(String s) {
    formats = s;
  }
  @Override
  public void validate(FacesContext context, UIComponent component,
      Object inputValue)
        throws ValidatorException {
    // get and validate formats attribute values
    // in case of no valid formats, the default format is used
    List<String> verifiedFormats = verifyAttributeFormats();
    // validate data
    String value = (String) inputValue;
    String stringNo = "";
    Long number = 0L;
    boolean valid = false;
    // validate the input value against each valid format specified in
    // the attribute and when a format match happens exit the loop
    // check for the number for digits, and other criteria
    FormatsLoop:
    for (String format : verifiedFormats) {
      switch (format) {
        case "default":
          stringNo = value;
          if (stringNo.length() == 10) {
            valid = true;
            break FormatsLoop;
          }
          break;
        case "hyphen-separated":
          String [] hyphenTokens = value.split("-");
          if (! formatLengthOkay(hyphenTokens)) {
            valid = false;
            continue FormatsLoop;
          }
          stringNo = value.replace("-", "");
          valid = true;
          break FormatsLoop;
        case "space-separated":
          String [] spaceTokens = value.split(" ");
          if (! formatLengthOkay(spaceTokens)) {
            valid = false;
            continue FormatsLoop;
          }
          stringNo = value.replace(" ", "");
          valid = true;
          break FormatsLoop;
        default:
    } // end FormatsLoop
    if (valid) {	
      try {
        number = new Long(stringNo);
      }
      catch (NumberFormatException nfe) {
        valid = false;
      }
    }
    if (! valid) {
      FacesMessage msg = new FacesMessage(summaryMsg, detailMsg);
      throw new ValidatorException(msg);
    }	
  } // validate()
  private List<String> verifyAttributeFormats() {
    List<String> verified = new ArrayList<>();
    if ( (formats == null) || (formats.isEmpty()) ) {
      // use the default format
      verified.add("default");
      return verified;
    }
    String [] tokens = formats.split(",");
    OuterLoop:
    for (int i = 0; i < tokens.length; i++) {
      InnerLoop: // check if format is valid
      for (Map.Entry<String, String> mapEntry : formatsMap.entrySet()) {
        if (mapEntry.getValue().equals(tokens [i])) {
          verified.add(mapEntry.getKey());
          break InnerLoop;
        }
      } // InnerLoop
    } //OuterLoop
    if (verified.size() == 0) { // no valid formats attribute specified
      // use the default format
      verified.add("default");
    }
    return verified;
  } // verifyAttributeFormats()
  private boolean formatLengthOkay(String [] tokens) {
    if (tokens.length != 3) return false;
    if ((tokens [0]).length() != 3) return false;
    if ((tokens [1]).length() != 3) return false;
    if ((tokens [2]).length() != 4) return false;
    return true;
  }  
}

(d) Tag Library Descriptor: app.taglib.xml

<?xml version="1.0" encoding="UTF-8"?>
<facelet-taglib version="2.0"
  xmlns="http://java.sun.com/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
  http://java.sun.com/xml/ns/javaee/web-facelettaglibary_2_0.xsd">
<namespace>http://jsfapp.com</namespace>
<tag>
  <tag-name>validatePhone</tag-name>
  <validator>
    <validator-id>app.phoneValidator</validator-id>
  </validator>
</tag>
</facelet-taglib>

(e) Deployment Descriptor: web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xmlns="http://java.sun.com/xml/ns/javaee"
  xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
  http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
  version="2.5">
  <servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>/faces/*</url-pattern>
  </servlet-mapping>
  <context-param>
    <param-name>javax.faces.FACELETS_LIBRARIES</param-name>
    <param-value>/WEB-INF/app.taglib.xml</param-value>
  </context-param>
</web-app>

(f) Application Configuration: faces-config.xml

<?xml version="1.0"?>
<faces-config xmlns="http://java.sun.com/xml/ns/javaee" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
  http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd"
  version="2.0">>

<faces-config>

(g) Web Application WAR file contents

phvalidator.war:
index.xhtml
WEB-INF
  |- web.xml, faces-config.xml, app.taglib.xml
  |- classes
        |- FormatsBean.class, PhoneValidator.class
  |- lib
        |- MyFaces library jar files	

Note that no library files are required in case of deplying the application on an application server (like GlassFish 3).

The deployed application may be run using the following URL: http://localhost:8080/validator/faces/index.xhtml

Web Application Screenshot

image

<<Back to the post

Return to top