Looking at the HiLo Code
Take a look at the code in Listing 1
. My Web deployment descriptor, web.xml, tells my container about the JSF controller Servlet.
The servlet-mapping and servlet blocks specify that any URL that ends in a .jsf extension should be redirected through a servlet called javax.faces.webapp.FacesServlet. This is a controller servlet that manages the request processing lifecycle for the Web application. All JSF requests will route through this controller servlet.
The servlet architecture allows for custom classes to be registered as event listeners. A context listener is one such listener that is triggered when application-context lifecycle events occur. As you can see, there is a context listener that is registered in the deployment descriptor. This listener is required in order to initialize the JSF application framework.
There are two context-param blocks. The javax.faces.CONFIG_FILES parameter defines where the JSF configuration file exists. The faces-config.xml file contains all of the configuration information for a JSF application. This is where you define how one page will navigate to the next as well as where you specify managed beans, which are model objects that JSF uses to get and set data from the UI. The second parameter specifies whether state should be maintained on the client side or on the server side.
The application has four JSPs:
- index.jspThis JSP exists in the root of the Web application and merely routes the user to the greeting page
- greeting.jspThis page greets the user, asks their name, and asks them for the upper limit with which they want to play HiLo
- game.jspThe main game page asks the user for a guess and tells the user if they've guessed high or low
- win.jspThis is the page that the user is directed to if they win the game
The way that these JSPs link together is defined in the faces-config.xml file, which is shown in Listing 2
The root-element is faces-config. There are three navigation-rule blocks and a managed-bean block. The navigation rules specify a from-view-id and one or more navigation-case blocks. The from-view-id is the page that you are currently on. Each navigation-case tells the framework where it should forward the user depending on the from-outcome that results from the action taken. For example, to get from the greeting page to the game page, a play action must occur.
The managed bean (also known as a form-backing bean) is the model of the Model View Controller pattern. The views interact with the managed bean, and the framework maintains all the necessary state for the scope defined in this definition. The managed-bean-name is the name that the views will use to reference the bean. The managed-bean-class is the fully-qualified class name for the bean. Have a look at the managed bean code shown in Listing 3.
The JSF framework will use the managed bean to store state information for the user. The bean has a reference to our actual domain object HiLoGame. This is a good layer separation to try to maintain when developing Web applications, because it helps keep view logic and properties out of your domain classes. This allows your domain classes to remain generic enough to be used by multiple views. It also makes it easier to introduce a service layer (perhaps stateless session EJBs) into your applications, because you don't have to go back and refactor the view code out of your domain.
When the framework sets the maxValue, the game is reset, and when it sets the guess, the HiLoGame domain object is called and the return value is stored in guessOutcome for retrieval by JSP components. The guess outcome will either be high, low, or correct. This outcome will be used both to display to the user, and to determine which navigation path to take based on the outcome of the guess. This will become more clear when I explain the game JSP.
Now let's look at the JSPs. The first JSP I'll look at is index.jsp:
<jsp:forward page="/pages/greeting.jsf" />
This forwarding JSP will direct control to the controller servlet because the URL it is forwarding ends in .jsf. No navigation rules need to be setup for this forward to work. The controller will determine that it needs to forward the user to the greeting page by looking at the URL. The controller will render the greeting.jsp page, using the data from the managed-bean.
Now let's look at the greeting page (see Listing 4 and Figure 1). The first thing you'll notice is that I've included two tag libraries: html and core. These custom tag libraries are a JSP implementation of a JSF rendering kit.
|Figure 1. Greeting.jsp: The greeting page gets you started with the HiLo game.|
The core tags are provided to developer to perform core actions that are independent of a rendering kit. These tags will not display anything to the user. The html tag library is mainly used to render html elements (text, form elements, etc.).
The first tag you see is a core tag called <f:loadBundle>. This tag provides JSF with internationalization support. It loads a properties file and makes its values available in a variable called msg. In a production application, I would have probably made better use of this bundle, but I only reference it once here so that you can see how it works. The bundle is referenced by the first <h:outputText> tag. This HTML tag actually renders text to the screen. The value of the text is a variable that references the bundle that was just created.
JSF uses a templating mechanism to provide variables. Any attribute value that starts with a hash sign and is wrapped in brackets is dynamically substituted in.
The <f:view> core tag tells the JSP that I am going to start using JSF components. All JSF components must be nested inside of an <f:view> tag in a JSP-rendered JSF file.
The <h:form> tag represents a form element. The form action is defined in the <h:commandButton> element. This is the action that is passed to the controller so that it can determine which page it should forward the user to. As you'll see in the game page, the action can also be a variable, thus providing dynamic redirection.
Validation can be achieved in a few ways. The <h:inputText> tag for the name field will render a textbox, and it has a required attribute that tells it that a value must be provided by the user. The maxValue <h:inputText> tag also validates that the field is filled out, but it also has a nested core tag that provides range validation. In this case, the user is required to provide a number between 10 and 1 million. If validation fails, error messages are presented to the user using the <h:message> tag. I have defined two tags, one for maxValue errors and another for name errors. If validation fails for either of these input fields, the user will get an error message.
|Figure 2. Game.jsp: The HiLo game is a good example of how variable results can be handled dynamically.|
Game.jsp (see Listing 5
and Figure 2
) is similar to greeting.jsp except for a few things. The first thing is the <h:panelGroup>
component tag. A panel group is essentially a component container. A panel group is comprised of sub-components and can determine whether or not all of the subcomponents are rendered or not. In this case, an output is rendered that tells the user if their guess is high or low, but only if the user has already made a guess.
|Figure 3. Win.jsp: The final .jsp page in the game is designed to be able to restart the game by sending the user back to index.jsp. |
The other thing to notice about this JSP is that the command button action is not hard coded. The gameBean provides a method called getGuessOutcome
that is called when the action is invoked. Note that an action must reference the actual name of the method, not just using the property name like other HTML components do. This is because an action can call any method on a managed bean, not just JavaBean style methods. The getGuessOutcome
method will return a String that the framework will compare to the navigation-rule in the faces-config.xml
file to determine where to forward the user.
If the user wins, they are forwarded to win.jsp (see Figure 3):
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<h:commandButton action="restart" value="Play Again"></h:commandButton>
This JSP is very simple. It simply has a command button that triggers the restart navigation case, which forwards the user back to the greeting page.