Published on dev2dev (http://dev2dev.bea.com/)
http://dev2dev.bea.com/pub/a/2007/06/icefaces-workshop.html
See this if you're having trouble printing code examples
by Tom Stamm
07/16/2007
ICEsoft's ICEfaces framework provides a way to Ajax-enable standard JavaServer Faces (JSF) applications without writing custom client-side Javascript code. This is a short introduction to using the ICEfaces tooling add-on for BEA Workshop for WebLogic. This tutorial covers installing the ICEfaces tooling, creating an ICEfaces-enabled project, and using ICEfaces and JSF to easily create an Ajax-enabled application to display and enter data. Because ICEfaces is adding Ajax capabilities to the regular JSF programming model, it is assumed that you have some familiarity with JSF.
The ICEfaces framework builds on the JSF component model to provide a server-centric way of creating rich Internet applications. During the regular JSF component lifecycle, events are handled, components are created, and an HTML DOM rendering of the page is created on the server. When using ICEfaces, the server components can respond to actions and cause pieces of the DOM to be changed and rerendered. Then, rather than reloading the entire page, the changed parts of the DOM are sent back to the client and only the corresponding sections of the page are updated. This can happen via an asynchronous request, initiated by the client, or by a server "push." Using a Javascript debugger such as FireBug, you can watch the messages that are sent back and forth during these updates. ICEfaces provides implementations of all core JSF components, as well as some others that take advantage of asynchronous updates. In this tutorial, you'll use a tab control, a data table that can do dynamic sorting and pagination, and a modal pop-up dialog. Figure 1 shows what the finished application will look like.

Figure 1. A sample customer data table
ICEfaces tooling is provided as an update to Workshop for WebLogic 10.1, and must be installed
separately via an Eclipse update. To do this, run the software update tool in Workshop (found under
Help->Software Updates-> Find and Install). Select "Search for new features to install," and
click Next. Click New Remote Site, and enter the URL
http://dev2dev.bea.com/eclipse/icefaces-tooling/. Make sure that the new site is
selected, and then click Finish. On the next wizard, make sure the archive file is selected, and then
click Next. Accept the terms of the license, and click Next, and then Finish. When the Feature
Verification screen appears, click Install All. After the update is finished, you will be
prompted to restart Workshop for the changes to take effect.
Note: Because this update is adding a fragment to an existing plug-in, Workshop must be restarted
with the -clean option. This can either be added to the command line used to start
Workshop, or to the <BEA Home>/workshop_10.1/workshop4WP/workshop4WP.ini file
before the -vmargs line.
The ICEfaces tooling update includes a WTP project facet that installs the ICEfaces runtime libraries into a project and makes the required changes to the project's configuration files. The simplest way to create an ICEfaces-enabled project is to run the New Dynamic Web Project wizard (choose File->New->Project..., and then select Web/Dynamic Web Project as the project type). Enter a project name, and in the Configurations drop-down, select either ICEfaces Project or ICEfaces Project with WLS 10.0. The latter option will use WebLogic library modules for everything except ICEfaces itself. Click Next to review the facet selections, and then Next again to go through the individual library selections. For ICEfaces you will need to click the Add... button and download the latest ICEfaces library from the BEA Web site. Finally, click Finish to create the project.
You can now open the generated web.xml to see that some changes have been made to
support ICEfaces; in particular, the Persistent Faces Servlet and the Blocking Servlet entries have
been added. These provide the server-side DOM rendering and asynchronous communications used by the
ICEfaces runtime, respectively. A mapping has been added so that requests matching the pattern
*.iface will be routed to the ICEfaces servlet instead of the regular JSF servlet. This
can be updated to *.faces or *.jsf, and the Sun JSF servlet can be
removed, so that all faces requests will be handled by ICEfaces. This tutorial will use the
default configuration and the .iface extension.
ICEfaces also requires that JSPs use XML namespace syntax when importing tag libraries, instead
of the JSP taglib import directive. The ICEfaces runtime cannot parse JSP directives, and will throw
an error if any are encountered. For example, the default page in your new Web application
(/WebContent/pages/welcome.jsp) has the following opening tag:
<f:view xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ice="http://www.icesoft.com/icefaces/component">
Other tag libraries can be imported in a similar manner. The JSP editor currently generates only JSP taglib import directives, so if you drag a tag from a library in the palette that is not already imported, you must remove the generated import directive and add an equivalent XML namespace declaration. When creating new JSPs in this project, you can select the New ICEfaces JSP template in the new JSP wizard to generate pages using this syntax.
At this point, you can run /WebContent/index.jsp, which will redirect to
/WebContent/pages/welcome.iface so that the welcome page will be executed using the
ICEfaces servlet. The default contents of the welcome page is not that exciting, so now you'll add
some functionality.
The first thing you'll add is a data table. This will be very similar to the JSF data table, and,
in fact, the ICEfaces dataTable tag is a superset of the JSF HTML dataTable
tag. However, by using the ICEfaces extensions and a few extra tags, this table will have features like
sorting and scrolling between pages without requiring a full page refresh.
To support this example, you must first import some files (found in imports.zip in
the Download section) into your project. Right-click your project in the
Project Explorer, select Import->Import..., and choose Archive File. In the next screen, browse
to imports.zip, select all files, make sure that the Into Folder: textbox is showing
your project name, and click Finish. This will import the packages beans and
businessObjects into your project's src directory, as well as some
stylesheets and images into the WebContent directory.
The classes in the businessObjects package are simple POJOs that represent Customer
data. The beans.CustomerBean class will be registered as a JSF bean, and provides some
sample Customer data, as well as some application control logic. In a real application this bean
could query a database for the Customer data, but here it is all hard-coded. After importing the
source code, the CustomerBean must be registered in the faces configuration file. To do
this, open /WebContent/WEB-INF/config/faces-config.xml. Right-click Managed Beans, and
select New Managed Bean. Enter customers as the name of the bean, change the scope
to session, and enter beans.CustomerBean as the bean type. Click Finish,
and then save the file.
Now, open the welcome page (/WebContent/pages/welcome.jsp). For this example, all of
the functionality will be contained in this page, and all interaction with the server will happen
asynchronously, with no page refreshes, after the initial page load. To get started, delete
everything inside the body tags, so that you can start with an empty page.
An important part of pages that use ICEfaces widgets is the stylesheet reference:
<link href="./xmlhttp/css/xp/xp.css"
rel="stylesheet"
type="text/css" />
This stylesheet is part of the ICEfaces runtime and provides default styles for some of the
widgets. Because it is part of the runtime, it does not need to be copied into each ICEfaces webapp.
Most tags have style and styleClass attributes so that you can override
the defaults, but this example sticks with the provided styles. Note that it is served up
by the xmlhttp servlet, which is also part of the ICEfaces runtime. Because the
Workshop tooling is not aware of what this servlet has access to, you'll see a warning that the
stylesheet cannot be found; this can be safely ignored.
The interface for our application will have two tabs: one to display data, and one that contains
a form to add data. This is provided by the ICEfaces panelTabSet tag. A
panelTabSet must be wrapped in a form tag, so first drag a
form tag from the ICEfaces section of the palette onto the page. The form does not need
an id or any other attributes. Then drag a panelTabSet tag and drop it inside the form.
A dialog will pop up. Click the "..." button next to "Tab items" to add tabs. We'll need one with
an id of viewTab labeled View, and one with an id of addTab labeled
Add. Also, set the style class to componentPanelTabSetLayout. The resulting code
should look like this:
<ice:form>
<ice:panelTabSet var=""
styleClass="componentPanelTabSetLayout">
<ice:panelTab id="viewTab" label="View">
</ice:panelTab>
<ice:panelTab id="addTab" label="Add">
</ice:panelTab>
</ice:panelTabSet>
</ice:form>
You can add some text in each tab and run the page to verify that it's working. Make sure you
either invoke the page as /pages/welcome.iface, or run /index.jsp, so that
it will be executed by the ICEfaces servlet.
Now you'll add a data table to the first tab that will databind to the list of
Customer objects provided by the backing bean. The table will include clickable headers
that will sort the data, and will have a paginator control to move through pages of data. The final
result can be found in the Download section within project.zip.
The completed project can be imported into a clean workspace by using File->Import, selecting
"Existing Projects into Workspace," and choosing the "Select archive file" option to select the
downloaded zip file. This section will go over some of the major points in the project.
To create the table, drag a dataTable tag onto the first tab in the tabset. When the
table dialog pops up, choose the customers property of the customers JSF
managed bean as the enumerable, and name the iteration variable "customer." The dialog should
automatically recognize the type as businessObjects.Customer. This is the variable that
will be referred to in the table columns. Click Next, and select the first,
id, and last properties to display, and then click Next to order them. This
will generate a basic table, similar to a plain JSF dataTable. You can also select some of the
address properties to display if you want. Using the smart editor, set the id of the table to
custTable so that you can refer to it later, and set the rows attribute to
5 so that it will not show too many rows at once.
Now you can add sorting. Drag a commandSortHeader tag into the header facet of the
id column, call the column name "id," and click the "arrow" checkbox so that the sort
order will be indicated. Next, move the outputText tag with the column name inside the
commandSortHeader tag. Do the same for the last column, naming it
"LastName," so that the table will be sortable for customer id and last name. ICEfaces depends on
the backing bean to enable sorting; the bean keeps track of what column the data should sort on, and
knows how to sort the data based on that column and which direction it should be sorted. When the
customers property is accessed by the data table, it will return a presorted list. In
this example, the bean is simply sorting an internal list; if it was executing a query against a
database, it might specify the sort in the query. To wire this up you need to databind the
sortColumn attribute of the data table to the sortColumnName property of
the backing bean, and the sortAscending attribute to the sortAscending
property of the backing bean. This can be done with the smart editor by clicking the button next to
each property, and browsing to the bean.
|
The table should now look something like this:
<ice:dataTable rows="5"
value="#{customers.customers}"
var="customer"
id="custTable"
sortColumn="#{customers.sortColumnName}"
sortAscending="#{customers.sortAscending}">
<h:column>
<f:facet name="header">
<ice:panelGroup>
<ice:commandSortHeader columnName="id" arrow="true">
<h:outputText value="Id"/>
</ice:commandSortHeader>
</ice:panelGroup>
</f:facet>
<h:outputText value="#{customer.id}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="First"/>
</f:facet>
<h:outputText value="#{customer.first}"/>
</h:column>
<h:column>
<f:facet name="header">
<ice:panelGroup>
<ice:commandSortHeader columnName="LastName" arrow="true">
<h:outputText value="Last"/>
</ice:commandSortHeader>
</ice:panelGroup>
</f:facet>
<h:outputText value="#{customer.last}"/>
</h:column>
...
</ice:dataTable>
The last component of the view tab will be adding pagination to the table. Select the
dataTable tag, and in the properties view, set the rows to 5 so that it will have a
limited number of rows per page. Next, drag a dataPaginator tag below the data table.
In the dialog, set the For field to the id of the table (in this case, custTable),
and check the Paginator checkbox. (If this checkbox is left unchecked, this control can
be used to display information like "Page 1 of 12" instead of buttons to enable switching pages.)
Clicking the "..." button next to the Navigator attribute will let you specify either images or
text for each of the controls (first, next, last, previous). After filling this out, the tag
will look something like this:
<ice:dataPaginator id="custTableScroller"
for="custTable"
paginator="true">
<f:facet name="first">
<h:graphicImage style="border:none;"
url="/css/xp/css-images/arrow-first.gif"/>
</f:facet>
<f:facet name="previous">
<h:graphicImage style="border:none;"
url="/css/xp/css-images/arrow-previous.gif"/>
</f:facet>
...
</ice:dataPaginator>
The images referenced here are copied directly into the webapp, but are also included in the
runtime and could be referenced as ./xmlhttp/css/xp/css-images/x.gif. However, this
does not work well with the Workshop JSP design view, since it does not know how to translate that
path and find the images in the runtime jar, so you'll use local copies in the example so that the
design view will work correctly.

Figure 2. The customer data table in the design view
Again, the completed code is available project.zip. Now you can run the app again
and try out sorting and pagination.
The second tab will contain a form to add customers to the list. For this example, you'll just add the customers to a list maintained by the backing bean, in a real application this would save the data in a database or some other backing store. To demonstrate the asynchronous nature of ICEfaces, the data will be sent in an asynchronous request, and a modal dialog will be displayed to notify the user that the new customer was added.
In the second tab of the tabset, drag out a panelGrid tag, and make it two columns
by three rows. The columns and rows are implicit, the children of a panelGrid is a list
of panelGroup tags, and the row/column layout is determined based on the number of
columns in the grid. For this case, each row will have a label and a text box, corresponding to the
first and last name properties of the customer. You can add more rows to fill in the new customer's
address by databinding to the customers.newCustomer.address.* properties. The last row
will have a commandButton to actually save the data:
<ice:panelGrid columns="2" width="100%">
<ice:panelGroup>
<f:verbatim>First Name:</f:verbatim>
</ice:panelGroup>
<ice:panelGroup>
<ice:inputText value="#{customers.newCustomer.first}"/>
</ice:panelGroup>
<ice:panelGroup>
<f:verbatim>Last Name:</f:verbatim>
</ice:panelGroup>
<ice:panelGroup>
<ice:inputText value="#{customers.newCustomer.last}"/>
</ice:panelGroup>
<ice:panelGroup>
<ice:commandButton
actionListener="#{customers.customerSaved}"
value="Add"/>
</ice:panelGroup>
<ice:panelGroup></ice:panelGroup>
</ice:panelGrid>
The customers backing bean has a property newCustomer, which is a
placeholder for a Customer object that is being added. The input text boxes here are
data-bound to the properties on that object. The backing bean also has an action listener method
customerSaved that will be invoked when the command button is clicked. This will "save"
the new customer, and reset the newCustomer object so that the form will be
cleared.

Figure 3. The customer input form in the design view
Now you just need to implement the dialog to notify the user that the customer was added. ICEfaces
provides a panelPopup tag that uses styles to simulate a dialog in a regular HTML DOM.
It has a visible attribute that can be data-bound to a property on a backing bean. The
customers bean exposes a property that you can use here, that will be set to
true when a user is added; it will be reset to false when the dialog is
dismissed.
Because of the scoping of the modal dialog, it must be contained in its own form
tag. So, first create a new form below the form that is wrapping the data table. Next, drag a
panelPopup tag inside this form. In the properties view, bind the visible
attribute to the popupVisible property of the customers backing bean. A
panelPopup uses two facets—header and body—to set the
title bar and body contents of the dialog. Put a message in each, and in the body, add a
commandButton tag that will invoke the closeNewCustomerPopup action
listener of the backing bean. The code should look something like this:
<ice:form>
<ice:panelPopup visible="#{customers.popupVisible}"
modal="true">
<f:facet name="header">
<f:verbatim>Customer Added</f:verbatim>
</f:facet>
<f:facet name="body">
<ice:panelGrid columns="1" width="100%">
<ice:panelGroup>
<ice:outputFormat
value="Customer {0} {1} added with id {2}">
<f:param value="#{customers.lastCustomer.first}"/>
<f:param value="#{customers.lastCustomer.last}"/>
<f:param value="#{customers.lastCustomer.id}"/>
</ice:outputFormat>
</ice:panelGroup>
<ice:panelGroup>
<ice:commandButton
actionListener="#{customers.closeNewCustomerPopup}"
value="Close"/>
</ice:panelGroup>
</ice:panelGrid>
</f:facet>
</ice:panelPopup>
</ice:form>
It will also be rendered in the design view, as shown in Figure 4.

Figure 4. The customer notification dialog in the design view
Run the app again and you should be able to add customers, and then switch back to the first tab to see the changes reflected in the data table without doing a full browser refresh. Figure 5 shows the input form.

Figure 5. The customer input form at runtime
Figure 6 shows the dialog that appears after a customer has been added.

Figure 6. The customer notification dialog at runtime
One thing to keep in mind is that because ICEfaces keeps a rendered DOM on the server, which gets updated during the JSF lifecycle, it is easy for this DOM to get out of sync during iterative development. When this happens, you will get errors on the server about illegal DOM hierarchy requests. Generally, you can republish the application, or, if that fails, restart the server, to get around these errors.
Another thing to watch out for when doing ICEfaces development is that all JSPs are valid XML. The ICEfaces parser will not accept any JSP directives.
One last issue with JSF in general is to make sure page requests are made with the appropriate
extension—in this case, *.iface. If the request is made as *.jsp, then the
ICEfaces servlet will not be invoked, and the JSP will not be executed correctly.
This example application should provide a good first look at what the ICEfaces framework can do. Many other controls are available in ICEfaces, as well as other techniques, such as partial form submits, that were not covered here.
Another way to get more familiar with ICEfaces is to take a plain JSF application, and replace the JSF HTML tags with their equivalent ICEfaces tags. In some cases, that simple step can give the application some Ajax functionality without any extra work. Several examples and tutorials are included in the ICEfaces distribution that can be imported into an ICEfaces-enabled webapp inside Workshop.
Tom Stamm is a Staff Engineer on the Workshop team at BEA Systems, working on Beehive and Web Services tooling. Prior to Workshop, Tom contributed to several releases of WebLogic Portal.
Return to Dev2Dev.