http://www.developer.com/

Back to article

The First Time Ever I Saw Your Face: Getting Started with JavaServer Faces


March 31, 2008

Over the years, there have been many frameworks for developing Web-based applications. First came servlets and JSP, but these technologies left a lot of work for developers to build from the ground up each time they built an application.

Apache Struts attempted to address this problem by providing lots of out-of-the-box functionality that could be easily configured, including page navigation, internationalization, and predefined hooks for connecting business logic to the user interface. Although this gave developers a leg up on raw servlets or JSP development, there was still room for improvement.

Now, JavaServer Faces improves on Struts by providing even more pre-built, reusable functionality to developers, including improved navigation functionality, internationalization support, forms input validation, data conversion, and perhaps most powerful of all, a true model-view-controller design for creating web-based applications. Here, I will explore the many features of JSF by developing a small application that takes advantage of what this framework has to offer.

What Is JSF?

JSF is a community-based development effort to bring true model-view-controller design to web-based applications. This new framework provides standard, reusable components for building a web page, built-in support for internationalization, built-in hooks for forms input validation and data conversion, and a standard method for linking client-side events to server-side application processing.

According to the Java Community Process site for JSF, which can be found at http://www.jcp.org/en/jsr/detail?id=127, the goals of JSF are as follows:

  • Create a standard GUI component framework which can be leveraged by development tools to make it easier for tool users to both create high quality GUIs and manage the GUI's connections to application behavior.
  • Define a set of simple lightweight Java base classes for GUI components, component state, and input events. These classes will address GUI lifecycle issues, notably managing a component's persistent state for the lifetime of its page.
  • Provide a set of common GUI components, including the standard HTML form input elements. These components will be derived from the simple set of base classes that can be used to define new components.
  • Provide a JavaBeans model for dispatching events from client-side GUI controls to server-side application behavior.
  • Define APIs for input validation, including support for client-side validation.
  • Specify a model for internationalization and localization of the GUI.
  • Automatic generation of appropriate output for the target client, taking into account all available client configuration data, such as browser version, and so forth.
  • Automatic Generation of output containing required hooks for supporting accessibility, as defined by WAI.

In a nutshell, JSF provides everything you need to render a client-appropriate UI to the user, get and validate the user's input, and connect the UI to the server-side processes that make up the application.

Tools of the Trade

To get started writing a JSF application, you will need to download an implementation of the JSF specification. An easy way to get one is to grab a copy of Sun's J2EE 1.4 application server. This server can be downloaded at http://java.sun.com/j2ee/1.4/download.html. You also can download the Apache MyFaces implementation at http://myfaces.apache.org/ and install it on your own particular application server instance.

The Hangman Application

Now, it's time to get started with JSF!

The application you'll develop is a word game called Hangman. In this game, a player is presented with a word puzzle, shown as blanks to be filled in. The player guesses a letter, and if it is correct, the letter is shown in the puzzle.

Each time the player guesses incorrectly, a message indicates that the letter is not in the puzzle. If the player guesses too many times, the game ends, the answer to the puzzle is revealed, and the player has a chance to play again.

The player can try to solve the puzzle any time during the game. If the player guesses the puzzle correctly, he wins, and has a chance to play a new game. If the guess is incorrect, it does not count against the number of guesses used.

The steps for developing any JSF application are:

  1. Develop the application pages using JSF components.
  2. Define page navigation in the jsf-config.xml file.
  3. Develop the Java beans for the business logic.
  4. Add forms validation to the application.

In the following sections, I'll tell you about various important points about each of these steps. When you're done, you'll have a fully functioning word game.

The faces-config.xml File

Before you jump into developing the application, you must create a faces-config.xml file. Every JSF application must have a faces-config.xml file in which security policies, business logic bean definitions, validation classes, conversion classes, and navigation rules for the application are defined. This file must be present in the WEB-INF directory of the application, along with web.xml, to begin development. I'll show you all of the parts of this file in detail later. A complete listing of the faces-config.xml file is available here.

Creating the User Interface

The application needs pages to perform the following functions:

  1. newGame.jsp: The first page of the application. This page will allow the player to enter his name, choose a difficulty level, and start the game.
  2. showPuzzle.jsp: This page is where the puzzle is shown and the player has a chance to guess a letter or solve the puzzle. If the player correctly guesses a letter, it is shown in the puzzle. If not, a message will tell the user that the letter was not in the puzzle, or that the letter was already guessed.
  3. youWin.jsp: On this page, the player receives a congratulatory message for solving the puzzle, and a link is shown to get back to the new game page.
  4. youLose.jsp: On this page, the answer to the puzzle is shown, and a link is shown to get back to the new game page.
  5. index.jsp: This page is the default page that comes up whenever a player visits the default application URL, http://localhost:8080/hangman/. It will automatically forward the player to the newGame.jsp page.

To develop the JSP pages that make up the application, you'll use the standard components that are available in JavaServer Faces. These are analogous to the normal HTML tags for form rendering and user input.

First, look at the newGame.jsp page, shown in Listing 1 below.

Listing 1

<HTML>
<HEAD> <title>Hangman!</title> </HEAD>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<f:loadBundle basename="com.dlt.developer.hangman.Messages"
              var="bundle"/>

<body bgcolor="white">

<f:view>
<h:form id="startGameForm" >

<h:graphicImage id="hangmanThumbnail"
                url="/images/hangman-thumbnail.gif"
                alt="Small image of hangman"/>

<h2>

<h:outputText id="welcome" value="#{bundle.welcomeMessage}"/>

</h2>

<br/>
<h:message style="color: red;
           font-family: 'New Century Schoolbook', serif;
           font-style: oblique;
           text-decoration: overline"
              id="errors1" for="playerName"/>
<br/>

   <h:panelGrid columns="1">
   <h:panelGrid columns="2">

<h:outputText id="namePrompt" value="#{bundle.nameLabel}"/>

<h:inputText id="playerName"
             value="#{GameBean.playerName}"
             size="25" required="true"/>

   </h:panelGrid>

   <h:panelGrid columns="2">

<h:outputText id="difficultyPrompt"
              value="#{bundle.difficultyLevelLabel}"/>

<h:selectOneMenu value="#{GameBean.difficultyLevel}" >
<f:selectItem itemValue="E"
              itemLabel="#{bundle.difficultyLevelEasy}" />
<f:selectItem itemValue="I"
              itemLabel="#{bundle.difficultyLevelIntermediate}"/>
<f:selectItem itemValue="D"
              itemLabel="#{bundle.difficultyLevelDifficult}"/>
</h:selectOneMenu>

</h:panelGrid>
</h:panelGrid>

<br/>

<h:commandButton id="submit"
                 action="#{GameBean.startGame}"
                 value="#{bundle.startGameLabel}" />

</h:form>
</f:view>

</body>
</HTML>

Most of the JSF components you see in this listing give you a pretty good idea of what they do by their names, but you should look at each in detail.

The first thing you will notice is the tag library declarations for the JSF components and core library at the top. The JSF UI components all have a prefix of h:, whereas the core library tags have a prefix of f:.

Next, you have the declaration for loading the application's resource bundle. This is where all internationalized strings for the application reside. This declaration loads the resource bundle, and makes it available to the rest of the page in the variable "bundle." Next comes the <f:view> tag. The view tag is extremely important. This is what tells the server that what is contained inside should be accessible by the FacesContext object. This object is the main conduit through which the various pieces of JSF work together. Without the view tag, it would be impossible to create the hooks between the JSP front-end and the business logic on the back-end.

Next comes the form tag. This tag is similar to the normal HTML form tag, but with some important differences. First, there's no method for determining get or post operations. Second, there's no action attribute that tells the form what to do when it is submitted. This is because both of these details are handled by the JSF framework. The get/post operations are abstracted away by this tag, so that the proper operations can be determined at the time the server serves up the request; if the request is an HTTP request, the operation will be a get or post, but a WAP request, for example, would require different operations. The action attribute is missing because the action will be determined at run-time by the JSF configuration that you set up for page navigation later on, as you'll see in the next section.

Now, we get into the nitty-gritty of rendering the UI. First, you'll see a graphic tag. This is similar in function to the HTML <img> tag, and renders a small .gif image with alternate text for non-visual browsers. Next, you have the <h:outputText> tag. This tag renders an internationalized piece of text, the welcome message, to the screen using the bundle you saw above. It also can be used to render non-static text from the application, as you'll see later. This same tag also is used for the "Name" and "Difficulty Level" prompts.

The next interesting bit of code is the first user input component for entering in a player's name, the <h:inputText> tag. This is similar in function to the <input type="text"> tag in HTML. The id attribute specifies a name by which this component is known within the application. The value attribute specifies the text that will be displayed as the predetermined value of the field. You'll notice a reference that looks suspiciously like a Java bean property, and it is. This expression links the value of this field to the player name property on the GameBean object. I'll explain this further later on, because you haven't yet created your business logic, but the expression shown links this field value to your backing Java bean, both at the time the page is rendered and after the form is submitted. The size attribute specifies how large the text field should be. Finally, you come to the required attribute. There is no analog to this attribute in HTML because it specifies a JSF-specific bit of functionality. It instructs the JSF implementation that the user must fill in a value for this field before allowing the user to leave the page. I'll demonstrate this further in the section on forms validation.

Next, you see the UI component for letting the player select a difficulty level. This is similar to the HTML <select> tag. As before, you can see that the value attribute for this tag is linked to a Java bean property on the GameBean object. Also, note the various items specified in the <f:selectItem itemValue="" itemLabel="" /> tags. Each one enumerates a menu selection for the selector. JSF provides several different options for rendering a number of choices, including menus, radio buttons, and checkbox groups.

You may have also noticed several <h:panelGrid columns="2"> tags in the code listing. This tag is used to format the output on the page in the same manner that a <table> does in HTML, or a LayoutManager does for Swing applications. However, the various table attributes are abstracted within this component. There's no need to specify individual table rows or cells; JSF takes care of the rendering for you.

Finally, you see the most important component on the page, a command button. Without this component, our word game could never get underway! The <h:commandButton> tag renders an HTML submit button on the form. When the form button is clicked, all form validation will be performed, and, if no errors are detected, the startGame() method on your GameBean object will be invoked. You'll see what happens after that in a bit in the section on page navigation.

Now, look at the showPuzzle.jsp page. It is shown in Listing 2.

Listing 2

<HTML>
<HEAD> <title>Hangman</title> </HEAD>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="/WEB-INF/tlds/hangman.tld" prefix="hm" %>
<f:loadBundle basename="com.dlt.developer.hangman.Messages"
              var="bundle"/>

<body bgcolor="white">

<f:view>
<h:form id="guessForm" >

<h:graphicImage id="hangmanThumbnail"
                url="/images/hangman-thumbnail.gif"
                alt="Small image of hangman"/>

<h2>

<h:outputFormat id="guessMessage"
                value="#{bundle.guessMessage}">
<f:param value="#{GameBean.playerName}"/>
<f:param value="#{GameBean.numberOfGuesses}"/>
</h:outputFormat>

</h2>
<br/>

<h:message for="guessForm" />
<h:message for="guessLetter" />
<br/>

<h:panelGrid columns="1">

<h1>
<h:outputText id="puzzle" value="#{GameBean.puzzle}"/>
</h1>

</h:panelGrid>

<h:panelGrid columns="3">

<h:outputText id="guess1" value="#{bundle.guessLabel}"/>

<h:inputText id="guessLetter"
             value="#{GameBean.guessedLetter}"
             validator="#{GameBean.validate}"/>

<h:commandButton id="guess"
                 value="#{bundle.guessLabel}"
                 action="#{GameBean.guessLetter}"/>

</h:panelGrid>

</h:form>
</f:view>
</body>
</HTML>

This page has many of the same components and declarations as the newGame.jsp page you learned about earlier. You have a JSF view, a form, <h:outputText> tags for rendering text, a <h:inputText> tag for getting user input, and a <h:commandButton> tag for submitting the form, and panel grid tags for controlling the layout of the screen.

One new tag on this page is the <h:outputFormat> tag. It is similar in function to the <h:outputText> tag, but allows internationalization of messages that require parameters. Here, you want to display a message like: "David, please guess a letter or solve the puzzle. You have guessed 1 times so far." However, to do this, you need to be able to render the player's name and the number of guesses thus far as part of the internationalized string. To specify these values, you use the <f:param value=""/> tags for each of these parameters. The <h:outputFormat> tag must be used here, because the <h:outputText> tag cannot accept any parameters for internationalization.

Finally, wrap up this step of the JSF development process by looking at the youWin.jsp and youLose.jsp pages shown in Listing 3 and listing 4.

Listing 3

<HTML>
<HEAD> <title>Hangman</title> </HEAD>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<f:loadBundle basename="com.dlt.developer.hangman.Messages"
              var="bundle"/>
<body bgcolor="white">

<f:view>
<h:form id="youWinForm" >

<h:graphicImage id="hangmanThumbnail"
                url="/images/hangman-thumbnail.gif"
                alt="Small image of hangman"/>

<h2>

<h:outputFormat id="youWinMessage"
                value="#{bundle.youWinMessage}">
<f:param value="#{GameBean.playerName}"/>
</h:outputFormat>

</h2>
<br/>

<h:commandLink id="newGame"
               value="#{bundle.playAgainMessage}"
               action="success"/>

</h:form>
</f:view>

</body>
</HTML>

Listing 4

<HTML>
<HEAD> <title>Hangman</title> </HEAD>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<f:loadBundle basename="com.dlt.developer.hangman.Messages"
              var="bundle"/>
<body bgcolor="white">

<f:view>
<h:form id="youLoseForm">

<h:graphicImage id="hangmanThumbnail"
                url="/images/hangman-thumbnail.gif"
                alt="Small image of hangman"/>

<h2>

<h:outputFormat id="youLoseMessage"
                value="#{bundle.youLoseMessage}">
<f:param value="#{GameBean.playerName}"/>
<f:param value="#{GameBean.answer}"/>
</h:outputFormat>

</h2>
<br/>

<h:commandLink id="newGame"
               value="#{bundle.playAgainMessage}"
               action="success"/>

</h:form>
</f:view>

</body>
</HTML>

In Listings 3 and 4, you see another new tag, the <h:commandLink> tag. This tag is identical in function to the <h:commandButton> tag, but renders a link rather than a submit button on the form. This link is a bit different than the command buttons you saw before because the action attribute has a hard-coded value, "success", rather than linking directly to a business logic function. This is because the navigation from this page is handled automatically by the JSF framework. You'll see how this is done in the next section.

Determining Page Navigation

So, now that you have a user interface built, you can move on to the next development step: defining page navigation.

You should revisit the flow of your word game application. First, you begin by starting a new game on the newGame.jsp page. When the user clicks the Start Game button, you move on to the showPuzzle.jsp page, and remain there until you have either solved the puzzle or guessed too many times. From the showPuzzle.jsp page, you can 1) go back to the showPuzzle.jsp page for another guess, 2) solve the puzzle correctly and go to the youWin.jsp page, or 3) guess too many times and go to the youLose.jsp page. From both youWin.jsp and youLose.jsp, you can start a new game by going back to newGame.jsp, at which point the cycle begins again.

To define how navigation is performed in the application, you need to define some navigation rules in the faces-config.xml file. The rule for the newGame.jsp page is shown below:

<navigation-rule>
   <description>
      Send the user to the puzzle page after the new game
      is set up
   </description>
   <from-view-id>/newGame.jsp</from-view-id>
   <navigation-case>
      <description>
         Go to the puzzle page if everything on the new game
         page validated OK
      </description>
      <from-outcome>success</from-outcome>
      <to-view-id>/showPuzzle.jsp</to-view-id>
   </navigation-case>
</navigation-rule>

The <from-view-id> tag tells JSF which page you are defining possible page navigations for. Then, you define the possible outcomes for that page by using the <navigation-case> tags. For newGame.jsp, the only outcome is "success" from the user clicking the "Start Game" button on the form. When this happens, you want to go to showPuzzle.jsp, so you indicate this by setting the <from-outcome> tag to "success" and the <to-page-view> tag to showPuzzle.jsp. This tells JSF that, after page validation is performed on newGame.jsp, you want to go to showPuzzle.jsp.

Now, look at the more complicated case of showPuzzle.jsp. Remember that here you have three possible outcomes, not just one, as shown below:

<navigation-rule>
   <description>
      Defines behavior once the user is on the puzzle page
   </description>
   <from-view-id>/showPuzzle.jsp</from-view-id>
   <navigation-case>
      <description>
         If the user is on the puzzle page and hasn't solved
         the puzzle, have them guess again
      </description>
      <from-outcome>guess_again</from-outcome>
      <to-view-id>/showPuzzle.jsp</to-view-id>
   </navigation-case>
   <navigation-case>
      <description>
         If the user is on the puzzle page and solves
         the puzzle, go to the win page
      </description>
      <from-outcome>success</from-outcome>
      <to-view-id>/youWin.jsp</to-view-id>
   </navigation-case>
   <navigation-case>
      <description>
         If the user is on the puzzle page and guessed too
         many times, go to the you lose page
      </description>
      <from-outcome>game_over</from-outcome>
      <to-view-id>/youLose.jsp</to-view-id>
   </navigation-case>
</navigation-rule>

So, how does defining the navigation rules help you? Remember your <h:commandButton> tag from showPuzzle.jsp:

<h:commandButton id="guess"
   value="#{bundle.guessLabel}"
   action="#{GameBean.guessLetter}"/>

The key here is the action attribute. It specifies that a business logic function on the GameBean object called guessLetter() is to be invoked whenever the user clicks the Guess button. It is up to guessLetter() to return one of the outcomes listed in the navigation rules above. In this case, guessLetter() must return "guess_again" if the user still hasn't solved the puzzle and has guesses left. If the user solves the puzzle, "success" should be returned. If the user has guessed too many times, "game_over" should be returned.

This concept should be familiar to Struts developers, because the navigation framework for JSF is very similar. From this example, you can see how the flow of the application is controlled by the invokation of business logic methods, without those methods needing to be involved in the mechanics of directing the user to a JSP page.

You also define rules for youWin.jsp and youLose.jsp, so that the command links on those pages can navigate back to newGame.jsp. A complete listing of faces-config.xml is available here.

Writing the Business Logic

Now, examine the business logic behind the application. As you've seen from earlier code samples, the application will consist mainly of a GameBean object that will encapsulate the various functions and properties that the UI will need to invoke or access. To prevent a long discussion of the details of the GameBean's inner workings, the relevant methods and a description of their functions are shown in the following table. A complete listing of GameBean.java is available here for those inclined to look through it, but for the purposes of understanding how the GameBean interacts with JSF, the 10,000-foot view below should do.

Table 1: GameBean.java

Method Name Description
startGame() This method randomly selects a puzzle for the user to solve based on the difficulty level selected.
guessLetter() This method takes the user's input and either guesses a letter in the puzzle or guesses the puzzle. If the puzzle is solved, success is returned; if the letter is not in the puzzle, "guess_again" is returned, and if the user has guessed too many times, "game_over" is returned.
getPlayerName()/setPlayerName() Getter/setter methods for player name property
getDifficultyLevel()/setDifficultyLevel() Getter/setter methods for the puzzle difficulty level
getEasyDifficultyLevel(), getIntermediateDifficultyLevel(), getDifficultDifficultyLevel() Return the constants for the various possible difficulty levels.
getNumberOfGuesses() Returns the number of guesses so far.
getGuessedLetter()/setGuessedLetter() Getter/setter methods for the letter or puzzle answer theuser guesses.
getPuzzle() Returns the current text of the puzzle, including _ characters for unguessed letters.
getAnswer() Returns the answer to the puzzle.
getResourceBundle() Returns the resource bundle for all internationalized strings.

As you have already seen, startGame() and guessLetter() are invoked to control game play and manage navigation in the application. The various properties are displayed in the UI as either static text or part of internationalized strings. Now, all that's left is to tell JSF that the GameBean is available, what its scope is, and what its class name is. To do this, you must add an entry in faces-config, as shown below:

<managed-bean>
   <description>
      The backing bean for the Hangman game application
   </description>
   <managed-bean-name>GameBean</managed-bean-name>
   <managed-bean-class>
      com.dlt.developer.hangman.GameBean
   </managed-bean-class>
   <managed-bean-scope>session</managed-bean-scope>
</managed-bean>

Now, the application knows how to instantiate the GameBean and how to manage the bean. It is possible to define page-scope and application-scope beans as well. However, in this case, the GameBean is only needed as long as the player's session lasts, but must last longer than one page request to keep track of the state of the game, so session scope is used.

Performing Forms Validation

The next step in the development process is to create the validation logic for the forms input the user will submit. There are four ways to perform validation in JSF:

  1. Validation using built-in JSF validators
  2. In-line validation
  3. Using a Validator class
  4. Creating a custom validator tag

Each approach has advantages for various situations. I'll explain each in detail in the following sections.

Built-In Validation

JSF provides built-in validator classes to check some simple validation criteria, such as whether required fields are present, or if a numeric value falls into a desired range of values. An example of this is the <h:inputText> tag for the player name on the newGame.jsp page:

<h:inputText id="playerName"
             value="#{GameBean.playerName}"
             size="25"
             required="true"/>

The required attribute tells JSF to use the predefined JSF validator to determine whether a field value for player name was entered. Validation occurs when the form containing this control is submitted. If the validation check fails, a message is displayed by using the <h:message> tag:

<h:message style="color: red;
           font-family: 'New Century Schoolbook', serif;
           font-style: oblique;
           text-decoration: overline" id="errors1"
                            for="playerName"/>

Note the "for" attribute on this tag. This attribute specifies that any validation messages associated with the UI component whose ID is "playerName" should be displayed where this tag appears, using the specified style for the text. The error message that is displayed is the standard JSF validation message for required fields, but this can easily be overridden in the application's resource bundle to present a more user-friendly message.

If the validation check passes, JSF then determines which page should be displayed by using the navigation rules defined earlier, which, in this case, would send the user to the showPuzzle.jsp page.

Even though the predefined validators do provide some useful functionality, they are limited in what they can do. Next, you'll see how to add some additional complexity to application validation.

In-Line Validation

To provide more complicated validation logic for the application, you can implement an in-line validation method to check various conditions and give appropriate error messages. An example of this is shown on showPuzzle.jsp for the guessed letter input field:

<h:inputText id="guessLetter"
             value="#{GameBean.guessedLetter}"
             validator="#{GameBean.validate}"/>

The "validator" attribute on the tag indicates to JSF that the GameBean's validate() method should be invoked when the form's input needs to be checked.This method is shown below:

public void validate(FacesContext facesContext,
   UIComponent uIComponent, Object value)
   throws ValidatorException {
   String msg1 = getBundle().getString(GUESS_A_LETTER_MSG);
   String msg2 = getBundle().getString(LETTER_ALREADY_GUESSED_MSG);

   // Get the component's contents and cast it to a String
   String theLetter = value.toString().trim().toUpperCase();

   if (theLetter.length() == 0) {
      FacesMessage message = new FacesMessage();
      message.setDetail(msg1);
      message.setSummary(msg1);
      message.setSeverity(FacesMessage.SEVERITY_ERROR);
      throw new ValidatorException(message);
   } // if

   String alreadyGuessedLetters = thePuzzle.getGuessedLetters();
   if (alreadyGuessedLetters.contains(theLetter)) {
      FacesMessage message = new FacesMessage();
      message.setDetail(msg2);
      message.setSummary(msg2);
      message.setSeverity(FacesMessage.SEVERITY_ERROR);
      throw new ValidatorException(message);
   } // if

} // validate()

The two checks that are performed in this message are to verify that a value was actually entered and to check whether the user already guessed that letter. If either of these conditions turns out to be true, a new FacesMessage with the appropriate internationalized error message is created, and a ValidationException is thrown. JSF then will recognize the fact that validation did not pass, and will display the generated message on the UI wherever the <h:message> tag is displayed:

<h:message for="guessLetter" />

Using a Validator Class

Although the in-line validation approach is more flexible than the built-in validators, it lacks reusability. If this same series of validation checks might be needed throughout the application, or across several applications, it would be best to create a validator class that could be reused independently of the application itself. An example of this might be a credit card number validator that checks the format of the credit card digits entered, and gives a validation exception if alphabetic characters appear in the number, or if the checksum for the number does not compute properly.

In this case, you'll implement the same logic that you did in the GameBean's validate() method, but the validator will grab all of the necessary data from the JSF framework, rather than from its own internal instance variables. The code for this validator is shown below:

package com.dlt.developer.hangman;
import javax.faces.context.FacesContext;
import javax.faces.application.FacesMessage;
import javax.faces.validator.ValidatorException;
import javax.faces.validator.Validator;
import javax.faces.component.UIComponent;
public class GuessValidator implements Validator {

   public void validate(FacesContext facesContext,
      UIComponent uIComponent, Object value)
      throws ValidatorException {

      GameBean theGameBean = (GameBean)
         facesContext.getApplication().getVariableResolver().
         resolveVariable(facesContext, "GameBean");

      String msg1 = theGameBean.getBundle().
         getString(GameBean.GUESS_A_LETTER_MSG);
      String msg2 = theGameBean.getBundle().
         getString(GameBean.LETTER_ALREADY_GUESSED_MSG);

      // Get the component's contents and cast it to a String
      String theLetter = value.toString().trim().toUpperCase();

      if (theLetter.length() == 0) {
         FacesMessage message = new FacesMessage();
         message.setDetail(msg1);
         message.setSummary(msg1);
         message.setSeverity(FacesMessage.SEVERITY_ERROR);
         throw new ValidatorException(message);
      ) // if

      String alreadyGuessedLetters =
         theGameBean.getPuzzleInstance().getGuessedLetters();
      if (alreadyGuessedLetters.contains(theLetter)) {
         FacesMessage message = new FacesMessage();
         message.setDetail(msg2);
         message.setSummary(msg2);
         message.setSeverity(FacesMessage.SEVERITY_ERROR);
         throw new ValidatorException(message);
      } // if

   } // validate()

// GuessValidator

The first thing to note about the GuessValidator class is that it implements the Validator interface. This interface must be implemented for any class that performs validation in JSF. The only method that this interface defines is the validate() method, as shown.

The only other bit of razzle-dazzle in this class is the fact that you are grabbing an instance of the GameBean class to check whether the letter the user entered was already in the puzzle:

GameBean theGameBean =
   (GameBean) facesContext.getApplication().getVariableResolver().
   resolveVariable(facesContext, "GameBean");

Here, you grab an instance of the JSF application object, get a variable resolver, and ask it to get the current instance of the GameBean, so that you can have access to the current puzzle value and already guessed letters.

As before, if any of the validation checks fails, you throw a ValidationException, and the message is displayed wherever the <h:message> tag for the UI component appears.

Now, you must tell JSF that you want to use this validator class for the guessed letter input field:

<h:inputText id="guessLetter"
             value="#{GameBean.guessedLetter}" >
<f:validator validatorId="guessValidator"/>
</h:inputText>

There is one last detail, however, that you must take care of to get this validator working. A validator entry needs to be placed in the faces-config.xml file so that the validatorId attribute on your <f:validator> tag can be resolved. The validator tag is shown below:

<validator>
   <description>
      Registers the guess validator class
   </description>
   <validator-id>guessValidator</validator-id>
   <validator-class>
      com.dlt.developer.hangman.GuessValidator
   </validator-class>
</validator>

Note that the validatorId attribute on the <f:validator> tag matches the <validator-id> tag's value above. Now, you can reuse this validator anywhere in the application by simply specifying a <f:validator> tag with the validatorId set as you have done here.

Creating a Validator Tag

Although creating a validator class does add a great deal of reusability to the validation logic that is written, there may be cases where even more complexity is desired for validation. Even though this is not the case with your guess validation scenario, consider the case where the JSP page author needs to specify some values that must be used at validation time to perform the check, such as a list of acceptable phone number formats. Under these circumstances, it is best to wrap the validator class in a special validator tag for maximum flexibility and reusability.

The first step is to create the validator tag and link it to the validator class that is responsible for performing the validation logic. The tag for your guess validator is shown below:

package com.dlt.developer.hangman;


import javax.faces.validator.Validator;
import javax.faces.webapp.ValidatorTag;
import javax.servlet.jsp.JspException;

/**
  * GuessValidatorTag is the tag handler class for
  * GuessValidator tag,
  * <code>guess_validator</code>.
  */

public class GuessValidatorTag extends ValidatorTag {
   public GuessValidatorTag() {
   super();
   super.setValidatorId("guessValidator");
   }
   protected Validator createValidator() throws JspException {
      GuessValidator result = null;
      result = (GuessValidator) super.createValidator();


      return result;
   }

} // GuessValidatorTag

The validator tag is very similar to any other custom JSP tag. However, there are two things that must occur for this tag to be used as a validator. The first is the linkage between the validator tag and the validator class that will actually do the work of validation. This is done in the constructor of the GuessValidatorTag:

super.setValidatorId("guessValidator");

The string that is passed to this method is the validator ID specified in the faces-config.xml file for the GuessValidator class.

The next thing that is special about this class is that it implements the protected method createValidator(). It grabs the instance of GuessValidator that was created when the tag class was instantiated, and returns it for use by the JSF framework.

Now, you must create a JSP tag definition in a taglib configuration file to let the JSP page know about the new validator tag:

<tag>
   <name>guess_validator</name>
   <tag-class>
      com.dlt.developer.hangman.GuessValidatorTag
   </tag-class>
   <description>
      Defines the guess-validator tag, with the tag-handler
      class, com.dlt.developer.hangman.GuessValidatorTag.
      This tag must be nested inside a UI component tag.
      The value of the UI component whose tag encloses
      the guess_validator tag is validated against the
      current puzzle.
   </description>
</tag>

Next, you implement the new tag on the showPage.jsp page by adding the new taglib definition to the top of the page, and changing the way you specify the validator on the guessed letter input field yet again:

<%@ taglib uri="/WEB-INF/tlds/hangman.tld" prefix="hm" %>
...
<h:inputText id="guessLetter" value="#{GameBean.guessedLetter}" >
<hm:guess_validator/>
</h:inputText>

Now, the GuessValidator tag can be reused anywhere that the taglib is specified, without the page author having intimate knowledge of the inner workings of the validation logic. All that is necessary is to include the taglib definition and the custom tag.

Conclusion

You have now built a small JSF application from the ground up; it utilizes many of the features of JSF. You have learned how to create a JSF user interface, define page navigation rules, define validation, and connect the UI to the back-end business logic of the application. Although this is a very simple example, it should get you well on your way to using this flexible, powerful framework to develop real-world JSF applications.

Download the Code

You can download the code for this article here.

References

About the Author

David Thurmond is a Sun Certified Developer with over fifteen years of software development experience. He has worked in the agriculture, construction equipment, financial, home improvement and logistics industries.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date