JavaServer Faces (JSF) 1.0 Framework makes it easy to build powerful and dynamic web applications. There are many web user interface frameworks available, but JSF stands out for several reasons: it is a Java Community Process standard, it brings JavaBeans paradigms to Web UI programming, and it was built by taking the best ideas from many existing frameworks. As a standard, many tool venders can rely on well defined and consistent behavior. JSF was designed for use inside of tools from the very beginning. Users can also rely on consistent and specific behavior from one JSF implementation to the next.
There are many features available in JSF. Some of the main feature highlights include:
This article will give an overview and examples of some of these JSF features.
A JSF application is essentially a Servlet or JSP application running on a J2EE 1.3 or 1.4 compliant container. This means Servlet version 2.3 and JSP version 1.2 or later. On a side note, JSF does not require that JSP pages be used in the application, you are free to use straight up Servlets or another templating technology. If you do use JSF with a JSP container, you benefit from the built-in JSF Core and HTML component libraries via the JSF custom component tags. The JSF components represent web controls like text fields, forms, buttons, tables, checkboxes, and so forth.
When you create a JSP page with JSF components, a component tree is built in memory on the server, with each component tag corresponding to a UIComponent instance in the tree. This component tree is used by the framework to handle your application request and create a rendered response. When the user generates an event, for example, by clicking on a button, the JSF lifecycle handles that event and generates an appropriate response. This is a familiar paradigm common to most forms of graphical user interface programming.
The FacesServlet is the entry point into the JSF framework. It handles the request processing lifecycle and acts as a front controller. JSF also has the notion of a context where important information request information is stored. That object is called the FacesContext. It is modified at each stage of the JSF Lifecycle and is valid per-request.
The JSF framework also has the notion of Value Binding and Method Binding Expressions. If you are familiar with technologies like the JSP Standard Tag Library (JSTL) or JSP 2.0 then you are already familiar with the notion of an Expression Language. The JSF Binding Expressions allow you to easily interact with your underlying Data Model. The Character Combat demo illustrates how you can retrieve values from your Data Model using Value Bindings.
This article includes a simple JSF application that illustrates some of the important JSF concepts. In order to understand the application, you should already be familiar with basic J2EE web technologies including JSPs, Servlets, and Tag Libraries.
The basic high-level idea behind the sample application is to engage the user in a quick fantasy game. Have you ever wondered what would happen if you took two characters from The Lord of the Rings and pitted them against another? The sample application answers that question in a simple and fun way. The application is called Character Combat.
Character Combat consists of the following:
The above diagram shows the page flow through the Character Combat Demo Application:
You will note that this workflow lends itself to the "wizard" UI design pattern. We have extracted the wizard functionality of this example into a simple reusable bean for your own applications. For more on the wizard bean, see the section: Example Wizard Component.
The latest JSF 1.0 Framework and all the runtime dependencies are already integrated in the Sun Java Systems Application Server 8.0 PE. In SJSAS 8.0 PE you do not need to take any extra configuration steps to setup a JSF web application. SJSAS 8.0 PE is free and contains the latest implementation of J2EE technologies.
If you write your own JSF web application, the only thing you would need to do in SJSAS 8.0 PE in order to use the JSF 1.0 Framework is to simply specify the Faces Servlet and Mapping in your web application's deployment descriptor. All the dependencies are already part of the container and you don't need to bundle any extra jar files.
In order to run the sample application discussed in this article, simply deploy the supplied war file in the application server. The application will be available by the context name "jsf-characterCombat". The demo source is available along with a pre-built war file. You will find instructions on how to deploy and built in the README provided with the source distribution.
The demo source is available in both zip and tar form:
To unpack the zip version, you can use your favorite utility or unpack it from the command line:
% unzip article.zip |
To unpack the tar version, you can invoke the following command:
% tar zxvf article.tar.gz |
In order to use the JavaServer Faces framework in a web application you need to define your Faces Servlet and a Faces Servlet Mapping in your Deployment Descriptor. Here is an example:
<!-- 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 Faces Servlet acts as a front controller into the JSF framework. It handles all of the JSF related requests.
In the above example extension mapping is used. With extension mapping, the web container will pass all requests to the Faces Servlet for pages that have extensions ending in "*.faces".
You can also map to the Faces Servlet using prefix mapping. For instance, you can have all web pages prefixed by "/faces/*" go through the Faces Servlet.
All of your JSF specific configuration information will go in an application configuration file like faces-config.xml
. In the configuration file you can define your Managed Beans, Navigation Rules, Converters, Validators, and so forth.
Here is an example of an entry in the configuration file:
|
This entry creates a mapping between a bean name and class. This mapping is used by the Managed Bean creation facility. The first time the "modelBean" is referenced, the model object is created and stored in the appropriate scope. Model Beans are discussed later on in the article.
There are many more configuration options. You can find the complete set by looking at the JSF 1.0 Specification.
A pre-built war file is included in the demo source distribution. You can simply deploy that war file in your web container.
Note that the war file contains a JSTL 1.1 implementation. If your web container does not support JSTL 1.1 then you will need to rebuild your war with a JSTL 1.0 implementation.
If you wish to build the demo on your own you will need to take the following steps:
<SJSAS_HOME>/bin/asant.
asant
" in your unpacked Character Combat demo directory where your "build.xml" file and customized "build.properties" files exist.
A managed bean is simply a Java class with a public, no-args constructor, that conforms to the JavaBeans 1.0 naming conventions for method names, and is identified to the Faces application using the managed bean facility. The managed bean facility is configured in the WEB-INF/faces-config.xml
file. You can place any number of <managed-bean>
declarations in this file, giving each one a name, class, and scope. In your web application itself you simply reference the bean using your defined bean name in a JSF Expression Language expression. The bean will be created and placed in the appropriate scope the first time you reference it. Managed Beans are very flexible and allow you to customize your bean by specifying properties, including Java Arrays, Maps, Lists, and other Managed Beans.
The Character Combat example has a backing bean called ModelBean. Here is how the ModelBean is defined in the WEB-INF/faces-config.xml
file using the managed bean facility:
|
That bean is then referenced in the web application's JSP pages. Here is an example of how the bean is used:
<h:inputText value="#{modelBean.customName}" /> |
Where <h:inputText/>
is a text field component nested in a form. When the form is submitted, the value in the text field will be saved as a "customName" property in the ModelBean. The "#{" and "}" designate that a Value Binding expression is used. The Value Binding expression tells the framework that the "customName" is a JavaBean property defined in the Model Data under the key "modelBean". The framework's Managed Bean facility knows, based on the WEB-INF/faces-config.xml
configuration file, the bean name to class mapping and instantiates the bean if it doesn't already exist.
The Character Combat example creates a ModelBean object. ModelBean includes a set of accessor methods just like any other JavaBeans component. It also pre-populates a default list of characters as well as stores the user customized character.
The ModelBean is created using the Managed Bean Creation Facility and referenced by name in the JSF components defined in the view. The ModelBean illustrates how a bean can be used to store the value of the component.
In the example only the addition of new characters is supported. A nice extension to the application would allow deletion functionality by modifying the model and view to support this feature.
The JSP pages provide the UI for the web application. JSF provides two JSP tag libraries that expose the components to the page author. You can customize these components or create your own. The standard components render themselves as basic HTML 4.01, with the absolute minimum of JavaScript. This ensures that there is a high likely hood that your page will render as expected in all browsers. If you need support for other rendering types, such as WML, or SVG, JSF includes the concept of a RenderKit, which is a software module used by the components to render themselves into a particular client device type.
To use the built-in JSF tag libraries that contain the components, you will need to include the following tag directives in your JSP page:
|
Note that your JSF page will need to have all of the JSF tags enclosed in between the <f:view>
...</f:view>
tags so that the component tree is properly created.
Each HTML component can be customized using stylesheets. You can specify a generic styleClass or set specific style attribute values for the components.
Here are some examples from the Character Combat application that illustrate the above concepts:
The DataTable UIComponent can handle several different types of Data Model including java.util.List
and java.sql.ResultSet
, among others. It takes the data and lays it out in a customizable table. The component can also be customized using stylesheets.
In the Character Combat demo, a List is used as the underlying Data Model. Here is a code snippet:
|
As you can see in this example #{modelBean.dataList}
evaluates to a list of character entries stored in the "character" variable as defined in the <h:dataTable/>
tag. For each character in the list a new row is created and displayed according to the <h:column/>
tags.
The <f:facet/>
tag creates a special relationship between the component enclosed in the facet and its parent. This special relationship allows you to define components to be headers or footers. In our example, we use a facet to create header for our columns.
Since the <h:dataTable/>
is an HTML component, its style can be customized using stylesheets. In our example we show how you can use style attributes for several of the different <h:dataTable/>
properties. We import our stylesheet named "stylesheet.css" using the following code snippet in the JSP page header:
|
Here is how the rendered HTML DataTable will look:
For simple table layout you can use the <h:panelGrid/>
component. Unlike the DataTable component, PanelGrid does not take any underlying Data Model.
Here is an example of a two column table. A header is defined for the first row. The second row contains an InputText Field in the first column and a list of pull down items for the second column:
<h:panelGrid columnClasses="list-column-center, list-column-center" headerClass= "list-header" styleClass= "inputList-background" columns= "2"> <f:facet name="header"> <h:outputText value="Customize Character:"/> </f:facet> <h:inputText value="#{modelBean.customName}" /> <h:selectOneListbox value="#{modelBean.customSpecies}" required="true" size="1" > <f:selectItems value="#{modelBean.speciesOptions}"/> </h:selectOneListbox> </h:panelGrid> |
The InputText component is one way that you can use to get information submitted by the user. In our example we link the text field to our model by specifying the value attribute as follows:
<h:inputText value="#{modelBean.customName}" /> |
This InputText component is nested in a form. Once that form is submitted, the value in the field will be reflected in our model.
In the following image you can see the combination of an InputText in the PanelGrid layout component along with a set of CommandButtons and a ListBox:
The OutputText component can display information in various ways. You can, for instance, configure it to either escape HTML markup, changing all occurrences of angle brackets to either the appropriate <
syntax or pass through the markup unchanged. You can also apply various stylesheet styles to the component.
OutputText is used throughout the sample application to display the data from the model. In this example we are displaying the character's name:
|
You can use the SelectOneRadio to display a set of radio selections. You can include a set of radio select items as well as nested item groups. Here is how SelectOneRadio is used in our example:
<h:selectOneRadio layout="pageDirection" required="true" value="#{modelBean.firstSelection}"> <f:selectItems value="#{modelBean.allCharactersToSelect}" /> </h:selectOneRadio> |
The layout attribute tells the <h:selectOneRadio/>
component to layout vertically as opposed to the horizontal default layout. Our ModelBean "charactersToSelect" method returns a List of SelectItems which the SelectOneRadio knows how to display. This radio component is nested in a form. When the form is submitted, the selected radio item will be saved as the "currentSelection" property in the model.
CommandButton is an input component that can create Action Events. You can create ActionListeners to look for specific action events as the user is navigating through your JSF web application. You can also supply an action method binding for an application action to be invoked when the component is selected. This latter case is what we use in our sample:
<h:commandButton actionListener="#{modelBean.addCustomName}" value="Add Name"/> |
We will discuss actions in the next section since they are very important in navigation handling.
Here is how the set of RadioButtons along with the corresponding CommandButton wizards will be rendered:
The user must do two things to use this component:
We'll cover these steps in detail below. The details of the WizardButtons managed bean are beyond the scope of this article, but basically it has methods that enable or disable the next and back buttons based on the user's current position in the wizard pageflow.
<jsp:include>
mechanism. For each of the pages in the example application, you will see the following line at the bottom:
<jsp:include page="wizard-buttons.jsp"/> |
Looking at this page, we see it has the following markup:
<%@ page contentType="text/html" language="java" %> <%@ taglib prefix="f" uri="http://java.sun.com/jsf/core" %> <%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %> <f:subview id="wizard-buttons"> <h:panelGrid columns="2"> <h:commandButton value="< Back" action="back" disabled="#{wizardButtons.hasBack}" /> <h:commandButton value="#{wizardButtons.nextLabel}" action="next" disabled="#{wizardButtons.hasNext}"/> </h:panelGrid> </f:subview> |
<f:subview>
tag, just as in the parent page they must be children of the <f:view>
tag. Also, this example doesn't show it, but any template markup text that you want to include in a subview must be wrapped inside of an <f:verbatim>
tag.
You can see that this page just has a Panel Grid that lays out two buttons next to each other. The attributes for these buttons are tightly bound to the methods and properties exposed on the WizardButtons bean. These buttons and the bean are designed to work together. The back button has its value and action hard-coded. The next button gets its value from the WizardButtons bean and has its action hard-coded.
faces-config.xml
file. Here is a subset of the rules for the demo application. You can use these rules as a starting point for leveraging the wizard component in your own application.
<navigation-rule>
|
The Navigation Rules describe how to handle actions based on where the action was invoked as follows:
As you recall from the workflow diagram, the first page is special in that the workflow can cause the page to be reloaded. This happens when the user wants to add characters to the character table. This addition is implemented by placing a button in the page that causes the appropriate action to take place when the button is pressed.
On the "main.jsp" page, you'll see this button declaration:
<h:commandButton actionListener="#{modelBean.addCustomName}" value="Add Name"/> |
The actual Action Handler implementation is in the ModelBean.java
file. The "addCustomName" method adds the name to the table.
public void addCustomName(ActionEvent event) throws AbortProcessingException { if ((customName != null) && (!customName.trim().equals(""))) { customName = customName.trim(); //check to see if name already exists in list Iterator iter = dataList.iterator(); while (iter.hasNext()) { CharacterBean item = (CharacterBean) iter.next(); if (item.getName().equals(customName)) { reset(); return; } } //create new entry CharacterBean item = new CharacterBean(); item.setName(customName); item.setSpecies((SpeciesBean) speciesPropertyMap.get(customSpecies)); dataList.add(item); } } |
This Action Handler is invoked when the CommandButton with the ActionListener is invoked. The "addCustomName" method goes through a list of existing characters and if it doesn't find the new name in the list, it goes on to create a new Character entry.
If this Action Handler had a return value, that could be used by the Navigation Handler to determine the success of the action and where to navigate based on the outcome. In this particular example, no value is returned since the same page is redisplayed but with the addition of the new custom name entry. Action Handler and Navigation Handlers were designed to easily interoperate.
This article has introduced you to some of the features available in the JSF framework. The sample application demonstrated the use of components, navigation, action handlers, and stylesheets.
JSF can help you create complex and robust web applications easily. It is a standard Java web application framework several years in the making. As a standard it is well positioned to be adopted by users and tool vendors. JSF is a tool that can help you build a great web application using MVC principles.