Converting our MyFaces 1.1.x + Tomcat 6 JSP webapps to ICEFaces + MyFaces 1.1.x + Facelets + Tomcat 6 webapps

This document describes a proposal to upgrade our web applications by using Icefaces and Facelets. Implicitly this involves no longer using JSP. Icefaces is a JSF component library that provides many components that would be useful to our development. These components are all Ajax enabled. Facelets is a "view technology" that replaces JSP with xhtml and a parser+renderer that fully understands the JSF lifecycle and component tree and is thus able to avoid the trademark incompatibilites between JSF and JSP. Facelets also provides a page templating facility as well as the ability to create composite components. These will be discussed in more detail later.

Why upgrade?

We are currently using a handful of component libraries: Tomahawk, Ajax4JSF, and Jenia. We would like to solidify on a single solution and none of the currently used libraries represent a complete solution for us. Tomahawk is no longer in active development, Ajax4JSF (and its sub-project, RichFaces) do not provide a file upload component, and Jenia simply does not provide enough components to be worth while anymore. Icefaces comes closest to meeting all our component requirements.

Historically, JSF and JSP have never played well together. The basis of this incompatibility is that JSF renders pages in a multistage lifecycle and builds component trees out of the pages. JSP (which is used to compile the jsp pages still) is unaware of both the lifecycle and the component tree. As a result, several restrictions are placed on the content of the JSP page: you can't use JSTL's <c:forEach> and <c:choose> tags, and you have to wrap almost all plain html in <f:verbatim> tags to ensure that everything gets rendered in the correct order on the screen.

Previously we've had to work around these limitations but that is no longer the case. Two solutions have presented themselves recently. With the introduction of JSF 1.2 and JSP 2.1, many of the aforementioned incompatibilites have been resolved. The upgrade to MyFaces 1.2.0 would entail rewriting parts of all of our custom JSF components.

Facelets, on the other hand, remains fully compatible with both JSF 1.1 and 1.2, allowing us to put off that upgrade until later. It provides a templating facility that is similar to tiles, but the thing that separates Facelets from tiles and from just using JSF 1.2, is it's ability to create and use composite components. Look here for an article on using Facelets with JSF.

Pros:

Cons:

We lose some functionality by upgrading to ICEFaces that we will have to find another way to implement. Alas, we apparently can't have it all.

How do we Upgrade?

Upgrading to Icefaces and Facelets requires several steps:
  1. Getting all the depependencies.
  2. Updating web.xml
  3. Use a new URL
  4. Updating faces-config.xml
  5. Update and rename all jsp files
    1. Replace <n:forEach> with <ui:repeat>
    2. Replace <n:exitDialog>
    3. Replace <n:floatingDialog>
    4. Update dialog CSS
    5. Replace <n:selectEnumItems>
    6. Replace Tomahawk tags
    7. Replace Ajax4JSF tags

1. Getting all the depependencies

Unfortunately, Icefaces is not in a maven repository. In order to use it we will have to add more files to our internal maven repository. I've already added files to our local repository and created pom files for them. These files are:

Each project has to update it's pom.xml to include a dependency on all 3 icefaces jar files because they are not all required in general, but they are in our use case.

<dependency>
	<groupId>org.icefaces</groupId>
	<artifactId>icefaces</artifactId>
	<version>1.6.1</version>
</dependency>

<dependency>
	<groupId>org.icefaces</groupId>
	<artifactId>icefaces-comps</artifactId>
	<version>1.6.1</version>
</dependency>

<dependency>
	<groupId>org.icefaces</groupId>
	<artifactId>icefaces-facelets</artifactId>
	<version>1.6.1</version>
</dependency>

2. Updating web.xml

You must update your web.xml file to add the following servlets and mappings:

<servlet>
	<servlet-name>Persistent Faces Servlet</servlet-name>
	<servlet-class>com.icesoft.faces.webapp.xmlhttp.PersistentFacesServlet</servlet-class>
	<load-on-startup> 1 </load-on-startup>
</servlet>

<servlet>
	<servlet-name>Blocking Servlet</servlet-name>
	<servlet-class>com.icesoft.faces.webapp.xmlhttp.BlockingServlet</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>

<!-- Persistent Faces Servlet Mapping -->

<servlet-mapping>
	<servlet-name>Persistent Faces Servlet</servlet-name>
	<url-pattern>*.iface</url-pattern>
</servlet-mapping>

<servlet-mapping>
	<servlet-name>Persistent Faces Servlet</servlet-name>
	<url-pattern>/xmlhttp/*</url-pattern>
</servlet-mapping>

<servlet-mapping>
	<servlet-name>Blocking Servlet</servlet-name>
	<url-pattern>/block/*</url-pattern>
</servlet-mapping>

Icefaces uses 2 servlets as part of its scheme to implement Ajax. You can learn more about it at their site.

Additionally, you must add a context parameter to web.xml that tells JSF what the default filename suffix of a JSF page is:

<context-param>
	<param-name>javax.faces.DEFAULT_SUFFIX</param-name>
	<param-value>.xhtml</param-value>
</context-param>

3. Use a new URL

Your new webapp will have an extension of .iface instead of .faces! Update your bookmarks, and any context parameters in your web.xml that reference the old url.

4. Updating faces-config.xml

In order to use Facelets, you must replace JSF's default view handler by adding this to your faces-config.xml:

<application>
	<view-handler>com.icesoft.faces.facelets.D2DFaceletViewHandler</view-handler>
</application>

5. Update and rename all jsp files

Both Icefaces and Facelets require that your documents be written in valid xhtml. This was not actually a requirement of plain JSP. Two things are necessary to effect this change. 1) remove all taglib includes (i.e. the <%@ taglib uri=....%> and add xml namespace definitions e.g. in the html or body tag like:

<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:f="http://java.sun.com/jsf/core"
  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:ice="http://www.icesoft.com/icefaces/component"
  xmlns:ui="http://java.sun.com/jsf/facelets"
  xmlns:n="http://sss.evla.nrao.edu/jsf"
  xml:lang="en"
  lang="en"
>
2) rename all .jsp files to .xhtml.

In addition to these simple changes, you must also ensure that you are no longer using any tags from other 3rd party libraries that we are no longer going to be using. This will of course vary by application quite a bit and could easily take up the bulk of the effort to upgrade.

You should also probably link to the default icefaces theme stylesheet. It should probably be the first stylesheet you specify.


<link rel="stylesheet" type="text/css" href="./xmlhttp/css/xp/xp.css"/>

Please refer to this tutorial for a getting started guide to using Icefaces. It covers both Icefaces and a small section on Facelets.

5.1 Replace <n:forEach> with <ui:repeat>

This is a simple search and replace. All attributes of the tags are the same

5.2 Replace <n:exitDialog>

The exitDialog does not work with Icefaces. In order to replace this component, we have to do several things: stop including the behaviour.js javascript, replace the exitDialog with a ice:panelPopup + a separate exit button that triggers the dialog if necessary, and update the backing beans to be able to toggle the dialog.

Here is an example from the RCT:

<ice:menuItem
	value="Exit"
	action="#{catPage.exit}"
	styleClass="menuBarButton"
/>

...

<ice:panelPopup
	visible="#{catPage.showExitDialog}"
	rendered="#{catPage.showExitDialog}"
	styleClass="exitDialog"
>
	<f:facet name="header">
		You may have unsaved data!
	</f:facet>

	<f:facet name="body">
		<ice:panelGroup>
			<ice:commandButton
				value="Discard Changes & Exit"
				action="#{catPage.forceExit}"
				styleClass="warningDialogConfirm"
			/>

			<ice:commandButton
				value="Save Changes & Exit"
				action="#{catPage.saveAndExit}"
				styleClass="warningDialogConfirm"
			/>

			<ice:commandButton
				value="Cancel"
				action="#{catPage.closeExitDialog}"
				styleClass="warningDialogCancel"
			/>
		</ice:panelGroup>
	</f:facet>
</ice:panelPopup>

Pay special attention to the Bold Italicized text. The exit method checks to see if there are unsaved catalogs. If so, it doesn't exit, but instead toggles the showExitDialog property. Please review edu.nrao.sss.tools.resrccat.pagestate.CatalogsPage for the details of the backing bean methods. Of Primary interest is the ability for the saveAndExit and forceExit methods to redirect the user's browser to a new page after completing their actions.

It is also important to note that the visible and rendered attributes of the <ice:panelPopup> tag must both be set in order for the popup to operate as expectd!

5.3 Replace <n:floatingDialog>

In general, a very similar process can be used to replace the floatingDialog tags. Replace the dialog content itself with a panelPopup and create a new button or link that toggles a (newly made) backing bean property to display the dialog. Any cancel button in that dialog can just re-toggle the same property.

5.4 Update dialog CSS

The previous dialog tags rendered a wrapper div that contained a link and another div (that contained the content of the popup dialog). The wrapper div had this css styling: position: relative so that the position of the popup could be: position:absolute;top:0;left:0 and the dialog would popup on top of the link that opened it as expected.

When using a regular ice:commandLink and ice:panelPopup to acheive the same effect, we lack the wrapper div that had relative positioning. To compensate, you can wrap the link and popup tags in a div or span yourself and style the wrapper with relative positioning. There are examples of this in the RCT.

5.5 Replace <n:selectEnumItems>

The n:selectEnumItems tag no longer works (because of facelets). This is a fairly simple thing to fix.

NOTE: The following example is out dated. There is a better way to do this now. See the Errata secion and/or talk to Brian!

Just make a backing bean method that returns a Collection<javax.faces.model.SelectItem> that contains the list of elements you want in your drop down menu.

An example backing bean method from the RCT:

import edu.nrao.sss.util.EnumerationUtility;
import edu.nrao.sss.webapp.SelectItemCollectionFactory;
import javax.faces.model.SelectItem;

...

private static Collection<SelectItem> telescopeTypes = null;
public Collection<SelectItem> getAvailableTelescopeTypes()
{
	if (this.telescopeTypes == null)
	{
		this.telescopeTypes = SelectItemCollectionFactory.createFrom(
			EnumerationUtility.getSharedInstance().getSelectableSetFor(TelescopeType.class)
		);
	}

	return this.telescopeTypes;
}

And the associated XHTML:

<ice:selectOneMenu
	value="#{ma.telescope}"
	valueChangeListener="#{catPage.currentCatalogAction.catalogChanged}"
	partialSubmit="true"
>
	<f:selectItems value="#{ma.availableTelescopeTypes}"/>
</ice:selectOneMenu>

5.6 Replace Tomahawk tags

Tomahawk tags begin with t:. The 2 tomahawk tags that we use are t:div and t:fileUpload. Please review the Icefaces website for documentation on how to use their fileUpload tag. To replace t:div, simply remove the t:. We can use plain div tags now!

There are, unfortunately, some caveats to that. If the div contains a styleClass attribute, change it to class. If it has a rendered attribute, you must remove that attribute and wrap the div in a n:if tag to acheive the same effect. Depending on the context, other solutions may be available.

5.7 Replace Ajax4JSF tags

This can be more complicated. I'll cover a few common examples. You can replace a4j:commandXYZ with ice:commandXYZ as long as you remember to remove any oncomplete and reRender attributes of the tag. Please note any component that has an oncomplete attribute as these will require extra handling. Talk to Brian about them. You can replace most a4j:support tags by removing the tag entirely, changing its parent tag to the equivalent icefaces tag, and adding the partialSubmit="true" attribute to it. a4j:outputPanel, a4j:page, and a4j:status tags can just be removed.

Errata