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

Getting Started with ICEfaces in Workshop for WebLogic

by Tom Stamm
07/16/2007

Abstract

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.

Introduction

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.

Datatable-Runtime
Figure 1. A sample customer data table

Installing ICEfaces Support

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.

Creating an ICEfaces-enabled Project

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.

Using ICEfaces to Display Data

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.

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

Adding Data Using an Asynchronous Request

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.

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

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

Customer Form Runtime
Figure 5. The customer input form at runtime

Figure 6 shows the dialog that appears after a customer has been added.

Customer Dialog Runtime
Figure 6. The customer notification dialog at runtime

Troubleshooting

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.

Download

Wrapping Up

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.

References

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.