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:
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 |
Now, let's take a look at how JSF works in detail.
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.
|
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:
NumberBean, a JavaBean for storing user dataMyActionListener, an action listenerFor 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.
javax.faces package.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.
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.
Figure 1. The directory structure of the JSF
application
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.
|
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.

Figure 2. The component tree of the adder.jsp page
The main component is the form, which has four child components.
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;
}
}
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);
}
}
}
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.

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.

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