ONJava.com    
 Published on ONJava.com (http://www.onjava.com/)
 See this if you're having trouble printing code examples


Introducing JavaServer Faces

by Budi Kurniawan
07/30/2003

JavaServer Faces (JSF) has been dubbed the next big thing in Java web programming. With JSF, you use web components on your web pages and capture events caused by user actions. In the near future, Java tools will support this technology. Developing web applications will be similar to the way we write Swing applications today: dragging and dropping controls and writing event listeners. This article is an introduction to JSF. It highlights the most important aspect of JSF: JSF applications are event-driven. Also, it offers a sample JSF application that illustrates the event-driven-ness of JSF. To understand this article, you need to be familiar with servlets, JSP, JavaBeans, and custom tag libraries.

First of all, a JSF application is a servlet/JSP application. It has a deployment descriptor, JSP pages, custom tag libraries, static resources, et cetera. What makes it different is that a JSF application is event-driven. You decide how your application behaves by writing an event listener class. Here are the steps you need to take to build a JSF application:

  1. Author JSP pages, using JSF components that encapsulate HTML elements.
  2. Write a JavaBean as the state holder of user-input and component data.
  3. Write an event listener that determines what should happen when an event occurs, such as when the user clicks a button or submits a form. JSF supports two events: ActionEvent and ValueChangedEvent. ActionEvent is fired when the user submits a form or clicks a button, and ValueChangedEvent is triggered when a value in a JSF component changes.

Related Reading

Mac OS X for Java Geeks
By Will Iverson

Now, let's take a look at how JSF works in detail.

How JSF Works

JSP pages are the user interface of a JSF application. Each page contains JSF components that represent web controls, such as forms, input boxes, and buttons. Components can be nested inside of another component; an input box can reside inside a form. Each JSP page is represented by its component tree. JavaBeans store the data from user requests.

Here is the interesting part: every time the user does something, such as clicking a button or submitting a form, an event occurs. Event notification is then sent via HTTP to the server. On the server is a web container that employs a special servlet called the Faces servlet. The Faces servlet, represented by the javax.faces.webapp.FacesServlet class, is the engine of all JSF applications. Each JSF application in the same web container has its own Faces servlet. Another important object is javax.faces.context.FacesContext, which encapsulates all necessary information related to the current request.

In the background, the processing performed by the Faces servlet is complex. However, you don't need to know all the details. Just bear in mind that the Faces servlet builds a component tree of the JSP page whose control fires an event. The Faces servlet knows how to build the tree because it has access to all JSP pages in the application. The Faces servlet also creates an Event object and passes it to any registered listener. You can obtain the component tree of a JSP page from the FacesContext object associated with the request.

An event fired by a web control on the client browser is encapsulated in an HTTP request, alongside other information such as the browser type, the request URL, etc. Therefore, all requests that need Faces servlet processing must be directed to this servlet. How do you invoke the Faces servlet with every HTTP request? Easy. Just use a servlet-mapping element in your deployment descriptor to map a particular URL pattern with the Faces servlet. By convention, you use the /faces/* pattern, such as the following.

<!-- Faces Servlet -->
<servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

<!-- Faces Servlet Mapping -->
<servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>/faces/*</url-pattern>
</servlet-mapping>

The request URL must contain the pattern in the <url-pattern> element. This is not hard to achieve. Note also that the <servlet> element, which contains the Faces servlet, has a <load-on-startup> element, to make sure the servlet is loaded when the application is first started.

To capture the event fired by a component, you must write an event listener for that component and register it with the component. Do this by enclosing the <action_listener> element in the custom tag representing the component. For example, to allow a listener named jsfApp.MyActionListener to capture the action event fired by a command button called submitButton, write the following code in your JSP page:

<h:command_button id="submitButton" label="Add" commandName="submit" >
  <f:action_listener type="jsfApp.MyActionListener" />
</h:command_button>

An action listener must implement the javax.faces.event.ActionListener interface and a value-changed listener must implement javax.faces.event.ValueChangedListener. Let's build a simple JSF application that illustrates how event-driven JSF is.

A Simple JSF Application

Source Code

Download the source code for the example application.

We'll build a simple JSF application that can add two numbers. To run this application, you need Tomcat 5 and JSF v1.0 EA4 (included in the Java Web Services Developer Pack (JWSDP) 1.2. The application consists of:

For your JSF application to work, it needs a set of .jar files containing the JSF reference implementation and other libraries. Once you install the JWSDP 1.2, you can find these files under its jsf/lib directory. Copy these .jar files to the WEB-INF/lib directory. Here is the list of all .jar and .tld files.

In addition, a JSF application will need the following libraries, which are part of the Apache Jakarta project. These libraries are included in the application accompanying this article.

The following subsections discuss each part of the sample JSF application. The final subsection, "Compiling and Running the Application," explains how the JSF application works.

Creating the Directory Structure

Start by creating a directory structure for your JSF application. In Tomcat, this goes under webapps. Figure 1 depicts the directory structure for an application called myJSFApp.

directory structure of the JSF application
Figure 1. The directory structure of the JSF application

Writing the Deployment Descriptor

Just like any other servlet/JSP application, this application needs a deployment descriptor, as shown in Listing 1.

Listing 1. The deployment descriptor (the web.xml file)

<?xml version="1.0"?>
<!DOCTYPE web-app PUBLIC
    "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
    <!-- Faces Servlet -->
    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup> 1 </load-on-startup>
    </servlet>

    <!-- Faces Servlet Mapping -->
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>/faces/*</url-pattern>
    </servlet-mapping>
</web-app>

There are two sections in the deployment descriptor. The <servlet> element registers the Faces servlet, and the <servlet-mapping> element states that any request containing the pattern /faces/ in the URL must be passed to the Faces servlet.

Authoring the JSP Page

A JSP page called adder.jsp provides the user interface, as seen in Listing 2.

Listing 2. The adder.jsp page

<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<html>
<head>
<title>Add 2 numbers</title> 
</head>
<body>
<jsp:useBean id="NumberBean" class="jsfApp.NumberBean" scope="session" />
<f:use_faces><br />
    <h:form id="addForm" formName="addForm" ><br />
        First Number:<br />
        <h:input_number id="firstNumber" valueRef="NumberBean.firstNumber" /><br />
        Second Number: 
        <h:input_number id="secondNumber" valueRef="NumberBean.secondNumber" /><br />
        Result: 
        <h:output_number id="output" valueRef="NumberBean.result"/><br>
        <h:command_button id="submitButton" label="Add" commandName="submit" >
            <f:action_listener type="jsfApp.MyActionListener" />
        </h:command_button>
    </h:form>
</f:use_faces>
</body>
</html>

We first define two taglib directives to use the two JSF tag libraries, html and core. The tag library descriptors for these two libraries can be found in the jsf-ri.jar file, so you don't have to worry about them. Their prefixes are h and f, respectively.

<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>

The <jsp:useBean> action element defines the NumberBean JavaBean with a session scope.

<jsp:useBean id="NumberBean" class="jsfApp.NumberBean" scope="session" />

Next come the JSF controls. Note that JSF controls must be enclosed in the <f:use_faces> opening and closing elements:

<f:use_faces>
...
</f:use_faces>

Inside them, you have a form.

<h:form id="addForm" formName="addForm">
...
</h:form>

Nested inside of the form are two input_numbers, an output_number, and a command_button.

First Number:
<h:input_number id="firstNumber" valueRef="NumberBean.firstNumber" /><br />
Second Number: 
<h:input_number id="secondNumber" valueRef="NumberBean.secondNumber" /><br />
Result: 
<h:output_number id="output" valueRef="NumberBean.result" /><br />
<h:command_button id="submitButton" label="Add" commandName="submit">
    <f:action_listener type="jsfApp.MyActionListener" />
</h:command_button>

Notice the action listener for the command button? Figure 2 depicts the component tree for this JSP page, with the root omitted.

the component tree of the adder page
Figure 2. The component tree of the adder.jsp page

The main component is the form, which has four child components.

Writing the Object Model

For this application, you need a JavaBean to store the two numbers to add and the result of the addition. Listing 3 presents this JavaBean, NumberBean.

Listing 3. The NumberBean JavaBean

package jsfApp;
public class NumberBean {
    int firstNumber  = 0;
    int secondNumber = 0;

    public NumberBean () {
        System.out.println("Creating model object");
    }

    public void setFirstNumber(int number) {
        firstNumber = number;
        System.out.println("Set firstNumber " + number);
    }

    public int getFirstNumber() {
        System.out.println("get firstNumber " + firstNumber);
        return firstNumber;
    }

    public void setSecondNumber(int number) {
        secondNumber = number;
        System.out.println("Set secondNumber " + number);
    }

    public int getSecondNumber() {
        System.out.println("get secondNumber " + secondNumber);
        return secondNumber;
    }

    public int getResult() {
        System.out.println("get result " + (firstNumber + secondNumber));
        return firstNumber + secondNumber;
    }
}

Writing the Action Listener

The action listener for the command button is the most interesting part of this JSF application. It demonstrates how an event causes a listener to execute. The listener simply prints messages to the console. However, it shows important information, such as the hierarchy of the component tree of the JSP page from which the event was fired and the component that triggered the event. Listing 4 shows the action listener.

Listing 4. The action listener for the command button (MyActionListener.java)

package jsfApp;

import java.util.Iterator;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.event.ActionListener;
import javax.faces.event.PhaseId;
import javax.faces.tree.Tree;

public class MyActionListener implements ActionListener {

    public PhaseId getPhaseId() {
        System.out.println("getPhaseId called");
        return PhaseId.APPLY_REQUEST_VALUES;
    }
  
    public void processAction(ActionEvent event) {
        System.out.println("processAction called");

        // the component that triggered the action event
        UIComponent component = event.getComponent();
        System.out.println("The id of the component that fired the action event: "
            + component.getComponentId());

        // the action command
        String actionCommand = event.getActionCommand();
        System.out.println("Action command: " + actionCommand);
  
        FacesContext facesContext = FacesContext.getCurrentInstance();
        Tree tree                 = facesContext.getTree();
        UIComponent root          = tree.getRoot();

        System.out.println("----------- Component Tree -------------");
        navigateComponentTree(root, 0);
        System.out.println("----------------------------------------");
    }
  
    private void navigateComponentTree(UIComponent component, int level) {
        Iterator children = component.getChildren();

        // indent
        for (int i=0; i<level; i++)
            System.out.print("  ");

        // print component id
        System.out.println(component.getComponentId());

        // navigate children
        while (children.hasNext()) {
            UIComponent child = (UIComponent) children.next();
            navigateComponentTree(child, level + 1);
        }
    }
}

Compiling and Running the Application

To compile the application, change to the myJSFApp/WEB-INF/classes directory. If you are using Windows, type the following command:

$ javac -classpath ../lib/jsf-api.jar;../lib/jsf-ri.jar; \
    ../../../../common/lib/servlet.jar jsfApp/*.java

Note that you need to use library files in the lib directory and the servlet.jar file. In Tomcat, the servlet.jar file can be found in the common/lib subdirectory of Tomcat's home directory.

If you are using Linux or Unix, change the semicolons that separate library files to colons:

$ javac -classpath ../lib/jsf-api.jar:../lib/jsf-ri.jar: \
    ../../../../common/lib/servlet.jar jsfApp/*.java

Then run Tomcat. You can then direct your browser to the following URL:

http://localhost:8080/myJSFApp/faces/adder.jsp

Note that you use the /faces/ pattern before the JSP page name. You will see something similar to Figure 3 in your browser.

running the application
Figure 3. Running the application

In the console, you'll see the following message:

Model Object Created
get firstNumber 0
get secondNumber 0
get result 0
getPhaseId called

Now type in two numbers into the input boxes and click the Add button. The browser now displays the result of the addition, as shown in Figure 4.

the result of addition
Figure 4. The result of addition

More importantly, check the message that gets displayed in the console:

get firstNumber 0
get secondNumber 0
processAction called
The id of the component that fired the action event: submitButton
Action command: submit
----------- Component Tree -------------
null
    addForm
        firstNumber
        secondNumber
        output
        submitButton
----------------------------------------
Set firstNumber 10
Set secondNumber 20
get firstNumber 10
get secondNumber 20
get result 30

Summary

In this article, you have learned the most important characteristic of JSF applications that makes them different from other servlet/JSP applications: event-driven-ness. You have also built a simple JSF application that consists of one JSP page. More importantly, you have written the action listener that responds to an action event.

Real-life JSF applications are more complex, often with multiple JSP pages. In this case, you need to be able to navigate from one JSP page to another. However, this is a topic for another article.

Budi Kurniawan is a senior J2EE architect and author.


Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.