Getting Started With Javaserver Faces 1.2, Part 1:: Building Basic Applications
Getting Started With Javaserver Faces 1.2, Part 1:: Building Basic Applications
2, Part 1:
Building basic applications
Skill Level: Introductory
18 Dec 2007
JSF is a more-traditional GUI development environment like AWT, SWT, and Swing.
One of its major benefits is that it makes Web development easier by putting the
hard work on the framework developers, not the application developers. Granted,
JSF itself is more complex than many other Web frameworks, but the complexity is
hidden from the application developer. It is much easier to develop Web applications
in JSF than in most other frameworks: it requires less code, less complexity, and
less configuration.
If you are doing Java server-side Web development, JSF is the easiest framework to
learn. It is geared for creating Web applications (not Web sites per se). It allows you
to focus on your Java code without handling request objects, session objects,
request parameters, or dealing with complicated XML files. With JSF, you can get
more things done more quickly than with other Java Web frameworks.
Objectives
In this tutorial, you get an overview JSF's features and learn how to write a basic
JSF application. You build a simple calculator application and, in subsequent
iterations, improve its look and feel, modify its structure to add dependency injection,
and implement JSF's navigation mechanism. In Part 2, you'll build custom
converters, validators, and phase-listeners.
Prerequisites
This tutorial is written for Java developers whose experience is at a beginning to
intermediate level. You should have a general familiarity with using the Java
language, with some GUI development experience.
System requirements
To run the examples in this tutorial, you need a Java development environment
(JDK) and Apache Maven. It helps to have a Java IDE. Maven project files and
Eclipse Java EE and Web Tools Project (WTP) project files are provided. See
Download to obtain the example code.
A component-based architecture
Think stateful components
The biggest hurdle you might have is forgetting that JSF is a stateful
component model. If you used Struts before, repeat after me: "JSF
is not Struts. JSF is not Struts." I find people who have a Swing,
AWT, Visual Basic, or Delphi GUI background learn JSF more
quickly than people who have been using Struts for years and have
never done GUI component development before. This tutorial is
designed to help you think in terms of stateful components.
JSF provides component tags for every input field available in standard HTML. You
can also write your own custom components for application-specific purposes or for
combining multiple HTML components to form a composite — for example, a Data
Picker component that consists of three drop-down menus. JSF components are
stateful. Their statefulness is provided through the JSF framework. JSF uses
components to produce HTML responses. Many third-party JSF GUI components
are also available.
JSF includes:
• An event-publishing model
• A lightweight inversion-of-control (IoC) container
• Components for just about every other common GUI feature, including
(but not limited to):
• Pluggable rendering
• Server-side validation
• Data conversion
• Page-navigation management
Being a component-based architecture, JSF is extremely configurable and
extensible. Most JSF functions — such as navigation and managed-bean lookup —
can be replaced with pluggable components. This degree of pluggability gives you
considerable flexibility in building your Web application GUIs and allows you to
incorporate other component-based technologies easily into your JSF development
efforts. For example, you could replace JSF's built-in IoC framework with the more
full-featured IoC/aspect-oriented programming (AOP) Spring framework for
managed-bean lookups. I'll cover a lot of the advanced features in Part 2.
That said, the truth is that JSF is not bound to JSP technology inherently. In fact, the
JSF tags used by JSP pages merely reference the components so they can be
displayed. The components have a different life cycle from the JSP page.
You'll realize this the first time you modify a JSP page to change the attributes of a
JSF component and reload the page ... and nothing happens. This is because the
tag looks up the component in its current state. If the component already exists, the
custom tag doesn't modify its state. The component model allows your controller
code to change a component's state (for example, disable a text field), and when
that view is displayed, the current state of your component tree is displayed.
A typical JSF application needs no Java code and very little Universal Expression
Language (JSTL EL) code in the UI. As I noted previously, there are lots of IDE tools
for building and assembling applications in JSF, and there is a third-party market for
JSF GUI components. It is also possible to code JSF without the use of WYSIWYG
tools (as you'll do in this tutorial), although JSF was designed with WYSIWYG IDE
tools in mind.
JSP 2.1 added many new features to support JSF, including the universal
Expression Language (EL) API that JSF 1.2 also adds. With standard JSTL tags,
you can now iterate through a list and render JSF components, something
impossible with JSF 1.1 and JSP 2.0. See Resources for more details on changes
that were made in JSP 2.1. (Even with the improvements, Facelets is a much better
fit for JSF, and many ideas from Facelets are going into JSF 2.0.)
The real issue with most Model 2 frameworks is that the event model is too simplistic
(essentially a highly scaled-down MVC), and it has no stateful GUI components,
which leaves too much of the work to the developer. A richer component and event
model makes it easier to create the kinds of interactions most users expect. Like
JSP technology, most Model 2 frameworks also make it too easy to mix HTML
layout and formatting with GUI custom tags, which act loosely like components
except they are not stateful. And some Model 2 architectures (like classic Struts)
make the mistake of separating behavior and state, which leaves many Java
developers feeling like they're programming COBOL.
JSF provides a component model and a richer MVC environment — much richer
than Model 2 frameworks. JSF is much closer to a true MVC programming
environment than the Model 2 architectures, although it still builds on top of a
stateless protocol. JSF also facilitates building more fine-grained, event-driven GUIs
than the Model 2 frameworks. Whereas JSF gives you a host of event options —
menu item selected, button clicked, tree node expanded, and so on — most Model
2s rely on the more simple "request received."
JSF's fine-tuned event model allows your applications to be less tied to HTTP details
and simplifies your development effort. JSF also improves somewhat on the
traditional Model 2 architecture by making it easier to move presentation and
business logic out of your controller and move business logic out of your JSP pages.
In fact, simple controller classes aren't tied to JSF at all, making them easier to test.
Unlike a true MVC architecture, the JSF model tier is unlikely to issue many events
that must be resolved in more than one viewport (although Crank tries to do this,
with support from JBoss ajax4JSF; see Resources). Again, this would be
unnecessary because you're dealing with a stateless protocol. The system event for
changing or updating a view is almost always (dare I say always?) a request from
the user.
Figure 1 illustrates the structure of an example application (the one you're about to
get to know in detail!) from a MVC point of view:
If this first section on JSF has left you shaking your head a little, don't worry: you're
over the worst hump. Getting into the conceptual framework of JSF is more than half
the battle with implementing this technology — and you'll soon see that it's well
worth the trouble. But that's enough theory: let's get into some bare-knuckled coding!
The goal of the initial Calculator application is to present a page that allows the user
to enter two numbers and then add or multiply them.
• A form
• Two text fields
• Two labels
• Two error-message locations
• Two Submit buttons
• A results panel
The text fields are for entering the numbers. The labels are for labeling the text
fields. The error-message locations are to display validation or data-conversion error
messages for the text fields. There are two JSP pages: calculator.jsp and index.jsp,
which just redirects to calculator.jsp. A managed bean called Calculator serves
as the model for calculator.jsp. This simple example does not have a controller layer
other than what JSF provides.
• Declare the Faces Servlet and add Faces Servlet mapping in the Web
application deployment descriptor (web.xml) file
• Specify the faces-config.xml file in the web.xml file
• Create the Calculator class
• Declare the Calculator bean in the faces-config.xml file
• Create the index.jsp page
• Create the calculator.jsp page
The application uses the following directory layout:
+---src
+---main
+---java
+---webapp
+---pages
+---WEB-INF
+---lib
The Java code is under src/main/java/. The web.xml file is under the
src/main/webapp/WEB-INF directory. The JSF configuration file is also under
src/main/webapp/WEB-INF. The example application was created with the Eclipse
IDE for Java EE Developers (Eclipse JEE), which includes a JSF project-creation
wizard that creates the web.xml file and faces-config.xml file with the right entries. I
assume you are going to use an application server that supports Java EE 5, that is,
that has the JSF and JSTL JAR files. See Resources for instructions on setting up
Tomcat to run JSF, setting up Eclipse JEE to run Tomcat 6, and running the
examples with Maven 2.
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
This is similar to most web.xml descriptors, except that you're giving control to the
JSF servlet to handle requests instead of specifying your own servlet. All requests to
JSP files that use an <f:view> tag (which the sample application does) must go
through this servlet. Therefore, you need to add a mapping and load only the
JSF-enabled JSP technology through that mapping, as shown in Listing 2:
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.jsf</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
Listing 2 tells the Faces Servlet container to send all requests that start with /faces/
or end with *.jsf to the Faces Servlet for processing. This allows JSF to initialize the
Faces context and the view root before displaying the JSF page. The view root
contains the JSF component tree. The Faces context is the way to interact with JSF.
My goal is to demonstrate how to get started with JSF, so I've kept the model object
very simple. The model of this application is contained within one model object,
shown in Listing 3. Later you'll split it into two classes: controller and model.
package com.arcmind.jsfquickstart.model;
/**
* Calculator. Simple POJO.
*
* @author Rick Hightower
*/
public class Calculator {
/** First number used in operation. */
private int firstNumber = 0;
/** Result of operation on first number and second number. */
private int result = 0;
/** Second number used in operation. */
private int secondNumber = 0;
/** Add the two numbers. */
public void add() {
result = firstNumber + secondNumber;
}
/** Multiply the two numbers. */
public void multiply() {
result = firstNumber * secondNumber;
}
/** Clear the results. */
public void clear() {
result = 0;
}
/* ---------- properties ------------- */
public int getFirstNumber() {
return firstNumber;
}
public void setFirstNumber(int firstNumber) {
this.firstNumber = firstNumber;
}
public int getResult() {
return result;
}
public void setResult(int result) {
this.result = result;
}
public int getSecondNumber() {
return secondNumber;
}
public void setSecondNumber(int secondNumber) {
this.secondNumber = secondNumber;
}
}
Listing 3 is straightforward and does not require explanation; just read the code.
Remember, though, that the Calculator POJO has nothing to do with JSF.
The bean declaration Listing 4 specifies is the name of the bean — calculator —
All this page does is redirect the user to calculator.jsp under the faces Web
context. This puts the calculator.jsp page under the JSF context path, where it can
find its view root.
Listing 6. /src/main/webapp/calculator.jsp
Note that most of this file is plain old HTML (XHTML to be exact). You can use
HTML inside of <f:view>, <h:form>, and <h:panelGroup> tags. It is a common
myth that you can't mix HTML with JSF tags. You can in many cases. You cannot
use HTML inside of the <h:commandButton>, which takes only other components
as children in its body.
Because this page is complex, I'll show you how to build it step by step.
Declare taglibs
You start by declaring the taglibs for JSF, as shown in Listing 7:
Listing 7 tells the JSP engine that you want to use the two JSF taglibs html and
core. The html taglib contains all the tags for dealing with forms and other
HTML-specific items. The core taglib contains all the logic, validation, controller,
and other tags specific to JSF.
Without <f:view>, JSF cannot build the component tree and later cannot look up
the component tree that was already created. Use the <f:view> tag as shown in
Listing 8:
<f:view>
<h:form id="calcForm">
...
</h:form>
</f:view>
The first line in Listing 8 is the declaration of <f:view>, telling the container that it is
managed by JSF.
<table>
<tr>
<td><h:outputLabel value="First Number" for="firstNumber" /></td>
<td><h:inputText id="firstNumber"
value="#{calculator.firstNumber}" required="true" /></td>
<td><h:message for="firstNumber" /></td>
</tr>
<tr>
<td><h:outputLabel value="Second Number" for="secondNumber" />
</td>
<td><h:inputText id="secondNumber"
value="#{calculator.secondNumber}" required="true" /></td>
<td><h:message for="secondNumber" /></td>
</tr>
</table>
Again note the heavy use of HTML. You could lay out your application with spans,
divs, tables, or whatever else you want. JSF is designer friendly. There are even
tools that allow you to use JSF in Dreamweaver (see Resources). JSF is even more
designer friendly when you use it with Facelets (which you can use with JSF 1.1 and
higher and will be part of JSF 2.0).
EL. However, the universal EL code actually associates the fields with the
corresponding values of the backing bean's properties. This association is reflexive;
that is, if firstNumber were 100, then 100 would show up when the form was
displayed. Likewise, if the user submitted a valid value such as 200, then 200 would
be the new value of the firstNumber property (assuming a conversion and
validation pass, which you'll learn more about later).
In addition to the fields, the calcForm is associated with two actions using three
commandButtons, as shown in Listing 10:
<div>
<h:commandButton action="#{calculator.add}" value="Add" />
<h:commandButton action="#{calculator.multiply}" value="Multiply" />
<h:commandButton action="#{calculator.clear}" value="Clear" immediate="true"/>
</div>
About stylesheets
The look and feel of all JSF components is declared through
stylesheet classes. Each component has a style to associate inline
styles, and a styleClass to associate the component with a
styleSheet class. The <panelGrid>, which you'll use in the
tutorial's next section, also has style attributes to associate styles
with rows and columns. In that section, you'll use Cascading Style
Sheets (CSS) with JSF.
The code in Listing 10 binds the three buttons to the calculator class's add(),
multiply(), and clear() methods, so that when a button is clicked, its
respective method is called (assuming that validation and conversion are performed
successfully).
By default, JSF validates the form before executing any of the action methods (such
as add() or multiply()). However, if you use immediate="true", as Listing 10
does for the Clear button, then JSF skips the validation phase and executes the
method directly (more or less — more details to follow) without validating the form.
<table>
<tr><td>
First Number ${calculator.firstNumber}
</td></tr>
<tr><td>
Second Number ${calculator.secondNumber}
</td></tr>
<tr><td>
Result ${calculator.result}
</td></tr>
</table>
</h:panelGroup>
Again you mostly use HTML. Notice that you can mix JSP style expressions in the
body of your <h:panelGroup>. The <h:panelGroup> has a rendered
expression, as do all JSF components. Thus the results section appears only if the
expression calculator.result != 0 is true. For this reason, when users first
load the calculator page, this section does not show. When they enter values, the
results section shows (as long as the results are not zero). (The problem with this
expression is that it puts logic in the view. And, what if you want the user to be able
to enter 0 + 0 (or 7 * 0) and show the result? You'll fix this later in the tutorial.)
To run this application, go to the page where the WAR file is mapped (on my box
this http://localhost:8080/calculator0/). This causes the index.jsp file to load the
calculator.jsp page under the JSF context. If you are using Eclipse WTP and you
have the server set up, you right-click the calculator.jsp page in the Navigator and
choose the Run As > Run On Server option.
If you enter some invalid text (for example, abc) in either the First Number field or
the Second Number field and submit, you're taken back to the /calculator.jsp view,
and an error message is displayed next to the corresponding field. If you leave either
field blank and submit, you're taken back to the /calculator.jsp view, and an error
message is displayed next to the corresponding field. Thus, you can see that some
validation is nearly automatic in JSF merely by specifying that the fields are required
and binding the fields to int properties. Figure 4 shows how the application deals
with validation and data-conversion errors:
Notice the weird error messages. The default error messages for conversion and
required values are not as user friendly:
Once you enter and submit two values whose sum or product isn't zero, the results
section appears, as shown in Figure 5:
Listing 12 shows how the input-form code changes to use the <h:panelGrid>:
<h:form id="calcForm">
<h4>Calculator</h4>
<h:panelGrid columns="3">
<%-- First Number--%>
<h:outputLabel value="First Number" for="firstNumber" />
<h:inputText id="firstNumber"
value="#{calculator.firstNumber}" required="true" />
<h:message for="firstNumber" />
<%-- Second Number--%>
<h:outputLabel value="Second Number" for="secondNumber" />
<h:inputText id="secondNumber"
value="#{calculator.secondNumber}" required="true" />
<h:message for="secondNumber" />
</h:panelGrid>
Listing 13 shows how the results section changes to use the <h:panelGrid>:
You've eliminated almost 20 lines of code (about a third), making it a little easier to
read. And now this application has a higher level of "JSFness," which some people
(for example, most developers) like, and others (Web designers) hate. You need to
balance what you do according to your project's needs. Projects vary, and so do the
need for absolute control (pure HTML layout) and easy-to-maintain applications
(more JSFness).
Here is where things get a little tricky. An <h:panelGrid> can contain only
components, whereas <h:form>, <f:view>, and <h:panelGroup> can contain
both HTML and components. The first <h:panelGrid> in the form has three
columns, so as soon as you add an extra component, it goes to the next row. The
second <h:panelGrid> contains only one column, so each component is added to
the next row. The <h:panelGrid> renders a table, so the output is nearly identical
to what you had before. (To the user it is identical.) To reiterate: you can't add HTML
to a <h:panelGrid>. It won't render it where you expect. It takes only components.
you to do this as well. You'll import a CSS style sheet and then use it with the
<h:panelGrid>. You'll make the <h:panelGrid> show up with a border and
alternating silver and white rows.
<head>
<title>Calculator Application</title>
<link rel="stylesheet" type="text/css"
href="<%=request.getContextPath()%>/css/main.css" />
</head>
oddRow {
background-color: white;
}
evenRow {
background-color: silver;
}
formGrid {
border: solid #000 3px;
width: 400px;
}
resultGrid {
border: solid #000 1px;
width: 200px;
}
Listing 15 defines the oddRow and evenRow styles, making odd rows white and
even rows silver.
Now apply these styles to the panelGrids. Listing 16 adds them to the form
panelGrid:
I've only scratched the surface of the styles that the <panelGrid> supports. Refer
to the tag lib API link in Resources for more details.
Notice that you use the label="First Number" attribute in the h:inputText
field. Now the message text is what you see in Figure 7:
The label names are no longer property names, so they are more user friendly. But
why you would want labels at all, given that error messages are laid out right next to
the fields anyway? Also, the label messages are very long. You can shorten them by
using the code in Listing 19:
Notice that Listing 19 sets the showSummary and showDetail of the h:message
component as showSummary="true" showDetail="false". This yields "First
Number: 'aaa' must be a number consisting of one or more digits." and "Second
Number: Validation Error: Value is required." for conversion and requirement of the
firstNumber and secondNumber input, respectively. This still doesn't work well,
though. Read on for a better alternative.
A problem with this approach is that you need to add requiredMessage and
converterMessage to every inputText field. For this simple example, that is not
a problem at all. For a real application, though, it could be a large maintenance
problem and surely breaks the DRY (don't repeat yourself) principle.
javax.faces.component.UIInput.REQUIRED_detail=required
javax.faces.converter.IntegerConverter.INTEGER_detail=not a valid number
Now the messages are changed globally whenever a field is required or an integer
conversion fails.
The improvements you've made in this section have increased the application's GUI
logic, so in the next section, you'll add a CalculatorController class that injects
the Calculator class.
• Adding FacesMessages
• Using the h:messages
• Binding components to the controller
You'll work through each of these steps. Then I'll backtrack and explain each step in
more detail.
package com.arcmind.jsfquickstart.model;
/**
* Calculator. Simple POJO.
*
* @author Rick Hightower
*/
public class Calculator {
/** First number used in operation. */
private int firstNumber = 0;
/** Result of operation on first number and second number. */
private int result = 0;
...
/** Divide the two numbers. */
public void divide() {
this.result = this.firstNumber / this.secondNumber;
}
/** Clear the results. */
public void clear () {
result = 0;
}
...
}
CalculatorController, shown in Listing 24, keeps the JSF peanut butter out of
your POJO chocolate. It is JSF aware, has components bound to it, and puts error
and status messages into the facesContext.
package com.arcmind.jsfquickstart.controller;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIInput;
import javax.faces.component.UIPanel;
import javax.faces.context.FacesContext;
import com.arcmind.jsfquickstart.model.Calculator;
public class CalculatorController {
private Calculator calculator;
private UIPanel resultsPanel;
private UIInput firstNumberInput;
private UIInput secondNumberInput;
public String add() {
FacesContext facesContext = FacesContext.getCurrentInstance();
try {
calculator.add();
resultsPanel.setRendered(true);
facesContext.addMessage(null, new FacesMessage(
FacesMessage.SEVERITY_INFO, "Added successfully", null));
} catch (Exception ex) {
resultsPanel.setRendered(false);
facesContext.addMessage(null,
new FacesMessage(FacesMessage.SEVERITY_ERROR, ex.getMessage(), null));
}
return null;
}
public String multiply() {
FacesContext facesContext = FacesContext.getCurrentInstance();
try {
calculator.multiply();
resultsPanel.setRendered(true);
facesContext.addMessage(null, new FacesMessage(
FacesMessage.SEVERITY_INFO, "Multiplied successfully", null));
} catch (Exception ex) {
resultsPanel.setRendered(false);
facesContext.addMessage(null,
new FacesMessage(FacesMessage.SEVERITY_ERROR, ex.getMessage(), null));
}
return null;
}
public String divide() {
FacesContext facesContext = FacesContext.getCurrentInstance();
try {
calculator.divide();
resultsPanel.setRendered(true);
facesContext.addMessage(null, new FacesMessage(
FacesMessage.SEVERITY_INFO, "Divided successfully", null));
} catch (Exception ex) {
resultsPanel.setRendered(false);
if (ex instanceof ArithmeticException) {
secondNumberInput.setValue(Integer.valueOf(1));
}
facesContext.addMessage(null,
new FacesMessage(FacesMessage.SEVERITY_ERROR, ex.getMessage(), null));
}
return null;
}
public String clear() {
FacesContext facesContext = FacesContext.getCurrentInstance();
try {
calculator.clear();
resultsPanel.setRendered(false);
facesContext.addMessage(null, new FacesMessage(
FacesMessage.SEVERITY_INFO, "Results cleared", null));
} catch (Exception ex) {
resultsPanel.setRendered(false);
facesContext.addMessage(null,
new FacesMessage(FacesMessage.SEVERITY_ERROR, ex.getMessage(), null));
}
return null;
}
public String getFirstNumberStyleClass() {
if (firstNumberInput.isValid()) {
return "labelClass";
} else {
return "errorClass";
}
}
//remove simple props
Update calculator.jsp
Next, update calculator.jsp as shown in Listing 25 to display error messages and
bind to calculatorController instead of the Calculator POJO directly:
<h4>Results</h4>
<h:panelGrid columns="1" rowClasses="oddRow, evenRow"
styleClass="resultGrid">
<h:outputText value="First Number #{calculatorController.calculator.firstNumber}"/>
<h:outputText value="Second Number #{calculatorController.calculator.secondNumber}"/>
<h:outputText value="Result #{calculatorController.calculator.result}"/>
</h:panelGrid>
</h:panelGroup>
</f:view>
</body>
</html>
Now that you've modified the entire application, I'll break it down and cover the
details.
JSF supports dependency injection. You can inject beans into the properties of other
beans. Because you're injecting the calculator bean into
calculatorController, you can put it in scope none. Scope none means it will
just be created and not put into a scope when it gets created. Listing 27 shows the
portion of faces-config.xml that injects the managed calculator bean with a scope
of none:
<managed-bean>
<managed-bean-name>calculator</managed-bean-name>
<managed-bean-class>
com.arcmind.jsfquickstart.model.Calculator
</managed-bean-class>
<managed-bean-scope>none</managed-bean-scope>
</managed-bean>
<managed-bean>
<managed-bean-name>calculatorController</managed-bean-name>
<managed-bean-class>
com.arcmind.jsfquickstart.controller.CalculatorController
</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>calculator</property-name>
<value>#{calculator}</value>
</managed-property>
</managed-bean>
try {
calculator.add();
resultsPanel.setRendered(true);
...
} catch (Exception ex) {
...
resultsPanel.setRendered(false);
}
return null;
}
Let's pause here. This is important: Because JSF is a component model, and
components are stateful, the components remember their state. You don't need to
include logic as you did in the earlier Calculator example. Tell the component not to
render itself, and it does not render itself anymore. Tell a component to disable itself,
and it's disabled every time you load the view as long as the view is active. JSF is
much closer to a traditional GUI application than Model 2 style frameworks. The less
code you need to write, the more quickly you can develop a Web application. JSF
puts the application in Web application. Think about it.
The messages are displayed to the user with the <h:messages> tag, as shown in
Listing 33:
Listing 33. Showing the end user error and status messages
Setting the globalOnly attribute to true shows only messages that are not tied to
specific components, like the ones you added in Listing 32. Notice that you use
different styles for status messages and error messages.
It's important to think of JSF as more of a traditional GUI component model and not
a fancy version of Model 2. Lots of possibilities emerge if you remember JSF is a
component model. You can set the value of the secondNumberInput in Listing 35
because it is an object, not a piece of HTML in a JSP. You can manipulate it, and it
will remember its value. It is stateful.
Listing 37 asks the firstNumbedInput component if its input is valid and then
changes the styleClass that's returned based on whether it is valid.
Navigation rules
Figure 11 shows the navigation rules that you'll add to the Calculator application:
It's nice to have tools that help you lay out your Web application's flow. Many IDEs
provide tools for drawing the navigation rules of an JSF application. Figure 12 shows
the Navigation Rule Layout Tool from Eclipse JEE:
After you work through this section, Figures 11 and 12 will make a lot more sense.
next. But many JSF component frameworks have tree views and
tab views. One view might have 10 tabs, and clicking on a tab loads
a different part of the view. Using this design, you could probably
easily create a large application without ever writing navigation rules
because the whole application is in one view. The ramifications of
this approach, including workarounds and best practices, are out of
this tutorial's scope.
You'll first add a home page that links to the calculator page. Then you'll split the
calculator page into two pages: one to show the calculator view, and one page to
show the results view. You'll also need navigation rules back and forth among the
calculator page, results page, and the home page.
<navigation-rule>
<navigation-case>
<from-outcome>CALCULATOR</from-outcome>
<to-view-id>/pages/calculator.jsp</to-view-id>
</navigation-case>
</navigation-rule>
Listing 38 states that any action that returns CALCULATOR will cause JSF to load
/pages/calculator.jsp as the next view. This rule is dynamic, in that any action from
anywhere in the application that returns CALCULATOR will go to /pages/calculator.jsp
(unless a more-specific rule applies — in JSF you can add a <from-view-id> that
then makes the rule apply only to that view; I'll cover the more-specific case later in
this section.) Listing 38 is similar to a global forward in Struts-speak.
In the form, add the commandLink shown in Listing 39, which must be inside of a
<h:form>:
<h:form>
<h:panelGrid columns="1">
<h:commandLink action="CALCULATOR" value="Calculator Application"/>
...
This works as you would expect: the Calculator application loads in the browser. But
the user might find it confusing that the URL in the browser still says
http://localhost:8080/calculator3/home.jsf even though the calculator view is
showing. This can be disconcerting, especially to Web-savvy users. And any user
who wants to bookmark the calculator part of the application won't know the true link.
One way to fix this is by using a redirect in the faces-config.xml navigation rule,
as shown in Listing 40:
<navigation-rule>
<navigation-case>
<from-outcome>CALCULATOR_REDIRECT</from-outcome>
<to-view-id>/pages/calculator.jsp</to-view-id>
<redirect/> <!-- LOOK HERE -->
</navigation-case>
</navigation-rule>
A commandLink can use the navigation rule by specifying the outcome string as the
action attribute. When clicked, the link in Listing 41 takes the user to the calculator
page:
This solves the problem, but at the cost of a small extra hit to the server, which on a
slow connection might be pretty long. But if you're building an internal application,
then this hit won't amount to much.
You don't have to hit the server twice if you don't mind directly linking to the page
you want to load, as shown in Listing 42:
<h:outputLink value="pages/calculator.jsf">
<h:outputText value="Calculator Application (outputlink)"/>
</h:outputLink>
Listing 42 links directly to the next view. In a Model 2 architecture and in JSF, it is
considered bad form to link directly from one view to the next view. Typically, the
controller should have a chance to initialize the model for the next view, so it is
better to go through an action method. But Listing 42 does create a link, and the
right URL will show up in the browser.
The ability to bookmark JSF applications has been a problem for a while. Several
frameworks have addressed this, including JBoss Seam. This is a small nit that will
also be addressed in JSF 2.
Listing 43. Navigation rule from all actions in calculator view to results page
<navigation-rule>
<display-name>Calculator View</display-name>
<from-view-id>/pages/calculator.jsp</from-view-id>
<navigation-case>
<from-outcome>results</from-outcome>
<to-view-id>/pages/results.jsp</to-view-id>
</navigation-case>
</navigation-rule>
Listing 43 says if the current view is the calculator view (calculator.jsp), and any
action returns results, then the next JSF view will be the results page (results.jsp).
JSF looks at the return value from the action methods and converts them to a string
(if they are not null) and uses that string to pick the next view. The return type can be
any object because its toString method will get called. (A lot of folks use Enums.)
Change all of your operations to return results, as in the add() method shown in
Listing 44:
Notice that the add() method returns results, and that results is mapped to
results.jsp. Note that the Cancel button does not return results. If an action
returns a value that is not mapped to a navigation rule, JSF stays on the current
view.
<navigation-rule>
<display-name>Calculator View</display-name>
<from-view-id>/pages/calculator.jsp</from-view-id>
<navigation-case>
<from-action>#{calculatorController.add}</from-action>
<from-outcome>results</from-outcome>
<to-view-id>/pages/results.jsp</to-view-id>
</navigation-case>
</navigation-rule>
But now you'd need to have a mapping per method. Yawn! That is not very DRY.
<div>
<h:commandButton action="#{calculatorController.add}" value="Add" />
<h:commandButton action="#{calculatorController.multiply}" value="Multiply" />
<h:commandButton action="#{calculatorController.divide}" value="Divide" />
<h:commandButton action="#{calculatorController.clear}" value="Clear" immediate="true"/>
<h:commandButton action="HOME" value="Home" immediate="true"/>
</div>
Note that the home link specifies the action value of HOME. Listing 47 shows the
navigation rule that maps the HOME outcome that the home link uses to the home
page:
<navigation-rule>
<navigation-case>
<from-outcome>HOME</from-outcome>
<to-view-id>/home.jsp</to-view-id>
<redirect/>
</navigation-case>
</navigation-rule>
Note that the commandButtons and commandLinks work the same with respect to
action handling and navigation rules.
The results page has a link back to the calculator page and a link back to the home
page, as shown in Listing 48:
You can go back to the calculator two ways. One is similar to the way you've already
seen, shown in Listing 49:
<navigation-rule>
<display-name>Results Page</display-name>
<from-view-id>/pages/results.jsp</from-view-id>
<navigation-case>
<from-outcome>calculator</from-outcome>
<to-view-id>/pages/calculator.jsp</to-view-id>
</navigation-case>
</navigation-rule>
Listing 50. Another mapping back to the calculator from anywhere under
pages/*
<navigation-rule>
<from-view-id>/pages/*</from-view-id>
<navigation-case>
<from-outcome>calculatorMain</from-outcome>
<to-view-id>/pages/calculator.jsp</to-view-id>
<redirect/>
</navigation-case>
</navigation-rule>
Listing 50 allows you to define logical areas of your application and have outcomes
that apply to just those places.
Section 7. Conclusion
This tutorial covered JSF and showed that it is a component model. Because JSF is
a component framework like Swing, SWT, or AWT, it makes Web application
development more like traditional GUI development and less like traditional Web
development. Applications written with JSF are shorter and easier to understand and
maintain, versus applications written in a typical Model 2 style. There seems to be a
lot of interest in JSF in the Java community and growing job demand to match.
JSF 2, which is forming, incorporates Facelets concepts, adds native Ajax support,
and makes JSF component development easier. JSF 2 should further enhance your
investment in JSF. The new model has partial page rendering via Ajax, which is
available today from tools like Ajax4JSF.
This is not to say that JSF does not have competitors. Among the server-side
component models, Tapestry 5 looks solid yet is not compatible with Tapestry 4.
Wicket is also very interesting but has not gained enough momentum for many to
consider it.
Then there are the noncomponent server-side Java frameworks. It has been said
that Struts 2.x has done a great job in improving WebWork, and some expect Struts
2.1.x to open the door to bigger and better things, yet Struts 2 is really based on
WebWork and not Struts 1.x. Spring MVC continues to grow by leaps and bounds
and is a good choice if you pick a non-GUI component server-side Web framework.
Lastly, there are pure client-side plays that delegate to services on the server, such
as the Google Web Toolkit (GWT) and Adobe Flex. (The architecture is different
from JSF but the target applications are the same.) Each has its advantages and
drawbacks but could impact JSF adoption.
Yet JSF is likely to continue to do well because it is the standard for Java EE and
has quite an active community behind it. Demand for JSF exceeds demand for
Tapestry, Spring MVC, Java Flex, and GWT. JSF 2.0 could propel JSF even further.
Part 2 in this tutorial series will cover the JSF life cycle and working with validators,
converters, phase listeners, and other advanced JSF features.
Downloads
Description Name Size Download
method
Sample code for this article1 j-jsf1.zip 112KB HTTP
Note
1. For instructions and JAR files to run this tutorial's source code with Eclipse JEE, Tomcat, and
Maven 2, see Resources.
Resources
Learn
• Instructions and JARs for running this tutorial's source code with Eclipse JEE,
Tomcat, and Maven 2.
• "Web Tier to Go With Java EE 5: Summary of New Features in JavaServer
Faces 1.2 Technology" (Jennifer Ball and Ed Burns, java.sun.com, February
2006): Read about the new features in JSF 1.2.
• "Web Tier to Go With Java EE 5: Summary of New Features in JSP 2.1
Technology" (Pierre Delisle and Jennifer Ball, java.sun.com, February 2006):
Get an overview of the new features introduced in version 2.1 of JSP
technology.
• Facelets: Facelets is a view technology that focuses on building JSF component
trees.
• "Facelets fits JSF like a glove" (Richard Hightower, developerWorks, February
2006): In this article, the author introduces Facelets' easy HTML-style
templating and reusable composition components.
• "Advanced Facelets programming" (Richard Hightower, developerWorks, May
2006): Read about advanced ways to bridge the gap between JSF and EL.
• JSFToolbox for Dreamweaver: A suite of design and coding extensions for
Dreamweaver that lets Java developers design and build JSF pages with
Adobe's Web authoring tool.
• JSF Tag Library Documentation: Official documentation for the html and core
tag libraries.
• JSF API: API Javadocs for the JSF specification.
• JBoss Ajax4jsf: Partial page rendering with JSF and Ajax.
• Crank: Crank is a master/detail, CRUD, and annotation-driven validation
framework built with JPA, JSF, Facelets, and Ajax. Crank is a use-case analysis
of what is possible with the new JSF 2.0 stack.
• Browse the technology bookstore for books on these and other technical topics.
• developerWorks Java technology zone: Hundreds of articles about every aspect
of Java programming.
Discuss
• Check out developerWorks blogs and get involved in the developerWorks
community.
Trademarks
Java and all Java-based trademarks are trademarks of Sun Microsystems, Inc. in the
United States, other countries, or both.