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.
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.
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.
oncomplete
attribute of the Ajax4JSF tags.
This allowed you to execute arbitrary JavaScript as soon as the ajax call was
completed. It is being used to click the exit button after doing a save (i.e.
when the user does a "Save & Exit"). It also is used to open the details
dialog immediately after the user creates a new group or catalog and it is used
to do a save before following a link that takes you to another
application.<n:forEach>
with <ui:repeat>
<n:exitDialog>
<n:floatingDialog>
<n:selectEnumItems>
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>
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>
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.
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>
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:
2) rename all .jsp files to .xhtml.
<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"
>
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.
<n:forEach>
with <ui:repeat>
This is a simple search and replace. All attributes of the tags are the same
<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!
<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.
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.
<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 aCollection<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>
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.
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.
<a4j:include viewId=...>
with <ui:include src=...>
div
around the entire contents of the included file so that you can have a tag to
stick the namespace definitions in.
edu.nrao.sss.webapp.SelectItemCollectionFactory
when dealing with select menus of enumerations. If the enumeration you are
dealing with is not present, add it.