ICEFaces is a JSF component library that adds a unique approach to AJAX: it renders a DOM (Document Object Model) on the serverside, and delivers changes to that DOM to a client via AJAX push. What this means is that data can be updated from one client, and the updates can be reflected almost immediately – with no client intervention – on other clients.
The ICEFaces component suite is fairly complete, including updates to the “normal” JSF components to use AJAX and a few other capabilities. For example, the inputText component, which renders <input type=”text” />, is now aware of user roles, and can participate in partial submits (so that the server gets data as it’s entered, rather than waiting until a full page submit is executed.)
The component suite also adds styling, a menu bar, a connection status widget, effects (I.e., highlights, pulses, fades), a progress bar, a file upload widget, charts, and a capable set of panels.
However, all this comes at a price: ICEFaces works with JSF 1.2, but its Java EE support is not complete. This tutorial shows how to deploy ICEFaces in a Java EE container, and how to retain the ease of development and deployment that EJB 3 gives you – even without EJB3.
The problem is that ICEFaces 1.6 and 1.7 still require some older mechanisms to work. Therefore, a web application that uses ICEFaces needs to use the Servlet 2.4 application descriptor, rather than Servlet 2.5.
Relying on Servlet 2.4 makes no difference whatsoever, until you realise that – according to the spec, and as implemented in the RI for Java EE – web applications that use Servlet 2.4 do not get the benefit of injected resources. This means that accessing EJBs has to be done the “old way” – if at all.
For example, a managed bean in a Servlet 2.5 application might refer to a Stateless Session EJB with code similar to the following:
MyManagedBean.java:
import com.tss.ejb.SLSBLocal;
public class MyManagedBean {
@EJB
SLSBLocal slsblocal;
public String getValueFromEJB() {
return slsblocal.getValue();
}
}
In Servlet 2.4, however, the annotation is ignored. This can be upsetting, until one finds the workarounds – namely, Spring, which manages to provide us everything we normally want from a local EJB without an EJB being involved at all.
First, let’s create a web.xml for an ICEFaces application. Create a web application in your favorite environment, specifying (or supplying) JSF 1.2. Then, add the ICEFaces requirements to web.xml, which means your web.xml should look like the following:
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<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>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.jspx</url-pattern>
</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>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.faces</url-pattern>
</servlet-mapping>
<listener>
<listener-class>
com.icesoft.faces.util.event.servlet.ContextEventRepeater
</listener-class>
</listener>
</web-app>
Now we need to do some work to create a managed bean that updates the DOM. This example uses a clock (using java.util.Date) and a “refresh count,” meaning the total number of times a link has been selected. Most of this can be found in the ICEFaces Developer Guide, but here’s the source for faces-config.xml and TimeBean.java:
faces-config.xml:
<?xml version='1.0' encoding='UTF-8'?>
<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"
version="1.2">
<managed-bean>
<managed-bean-name>renderManager</managed-bean-name>
<managed-bean-class>com.icesoft.faces.async.render.RenderManager</managed-bean-class>
<managed-bean-scope>application</managed-bean-scope>
</managed-bean>
<managed-bean>
<managed-bean-name>timebean</managed-bean-name>
<managed-bean-class>com.tss.beans.TimeBean</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>renderManager</property-name>
<value>#{renderManager}</value>
</managed-property>
</managed-bean>
</faces-config>
TimeBean.java:
package com.tss.beans;
import com.icesoft.faces.async.render.IntervalRenderer;
import com.icesoft.faces.async.render.RenderManager;
import com.icesoft.faces.async.render.Renderable;
import com.icesoft.faces.webapp.xmlhttp.PersistentFacesState;
import com.icesoft.faces.webapp.xmlhttp.RenderingException;
import java.util.Date;
public class TimeBean implements Renderable {
static int refreshCount = 0;
int interval = 1000;
PersistentFacesState state;
IntervalRenderer clock;
public TimeBean() {
init();
}
private void init() {
state = PersistentFacesState.getInstance();
}
public int getRefreshCount() {
return refreshCount;
}
public void setRefreshCount(int refreshCount) {
this.refreshCount = refreshCount;
}
public Date getNow() {
return new Date();
}
public String refresh() {
refreshCount++;
return null;
}
public void setRenderManager(RenderManager renderManager) {
clock = renderManager.getIntervalRenderer("clock");
clock.setInterval(interval);
clock.add(this);
clock.requestRender();
}
public PersistentFacesState getState() {
return state;
}
public void renderingException(RenderingException renderingException) {
if (clock != null) {
clock.remove(this);
clock = null;
}
}
}
Lastly, we create an example.jsp, invoked as “example.iface”, that looks like this:
example.jsp:
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="f" uri="http://java.sun.com/jsf/core" %>
<%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %>
<%@ taglib prefix="ice" uri="http://www.icesoft.com/icefaces" %>
<%@ taglib prefix="comp" uri="http://www.icesoft.com/icefaces/component" %>
<f:view>
<html>
<head>
<title>ICEFaces Example</title>
</head>
<body>
<h:form>
<comp:outputConnectionStatus/><br/>
Time: <comp:outputText value="#{timebean.now}"/><br/>
Refresh Count: <comp:outputText value="#{timebean.refreshCount}"/><br/>
<comp:commandLink value="Refresh" action="#{timebean.refresh}"/>
</h:form>
</body>
</html>
</f:view>
Now, if this application is deployed into an application server, two client sessions (say, one in IE, and the other in Firefox) should be able to hit the page, and both will display a ticking clock – and if one selects the “Refresh” link, the counter will update on both browser windows.
This is being done by sending DOM updates from the server, using AJAX Push. As the DOM updates are very small, the bandwidth consumed is negligible – but remember that bandwidth isn’t entirely free, so to be responsible, you still have to measure the network traffic to make sure that you’re not exceeding your limits.
The trouble with EJBs is related directly to the use of Servlet 2.4. This revision of the specification relies on EJB2, not EJB3, and using the EJB3 syntax and constructs is problematic at best. Since EJB2 is the “bad old way,” requiring remote and home interfaces, it’s probably easier to drink the Spring Kool-aid – which gives you almost all of the advantages with only a little more configuration, thanks to Spring 2.5’s configuration changes.
So we aren’t defining an EJB so much as a bean, even though we’re doing everything that an EJB would (or could) do. In this example, we’re going to ignore transactions, because their inclusion doesn’t change anything about the application or deployment.
Our interface and implementation look like the following:
Hello.java:
package com.tss.beans;
public interface Hello {
String sayHello(String name);
}
HelloImpl.java:
package com.tss.beans;
import java.util.Date;
public class HelloImpl implements Hello {
public String sayHello(String name) {
return "Hello, "+name+" ("+new Date()+")";
}
}
(You can probably see for yourself why transactions aren’t important for this bean.)
Now it becomes important to provide Spring configuration to the web.xml, in the form of a context-param and two listeners. Here is what we add to web.xml:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
We also need a new faces-config.xml, to allow Spring as a name resolver. Our faces-config.xml should now look like this:
faces-config.xml:
<?xml version='1.0' encoding='UTF-8'?>
<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"
version="1.2">
<application>
<variable-resolver>org.springframework.web.jsf.DelegatingVariableResolver</variable-resolver>
</application>
</faces-config>
Now some of the fun stuff comes from Spring. First, let’s look at the applicationContext.xml file, which belongs in WEB-INF:
applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:annotation-config />
<bean id="renderManager" class="com.icesoft.faces.async.render.RenderManager" scope="singleton" />
<bean id="timebean" class="com.tss.beans.TimeBean" lazy-init="true" scope="session">
<property name="renderManager" ref="renderManager"/>
</bean>
</beans>
It’s very important that every bean that uses the renderManager reference have lazy-init=”true”. The reason is because the PersistentFacesState isn’t necessarily ready when the bean is loaded, which will allow one render, but won’t provide any live updates.
Once these changes have been applied, you can reinvoke the example.iface page in two browsers, and watch them both update roughly every second, and the refresh count matches between them, as it was in the non-Spring version.
One thing worth noting, however, is that with Spring 2.5, our configuration can get simpler. It’ll require some changes to TimeBean.java, but they’re well worth it.
We need to change the setRenderManager() method. Let’s rename it to “initClock()” and remove the parameters, since we won’t need it. We also want to add the @PostConstruct annotation, so that this method will be called immediately after the bean is instantiated.
@PostConstruct
public void initClock() {
System.out.println(renderManager);
clock = renderManager.getIntervalRenderer("clock");
clock.setInterval(interval);
clock.add(this);
clock.requestRender();
}
Of course, now we’re missing the “renderManager” reference. Let’s add that, with the @Autowired annotation:
@Autowired
RenderManager renderManager;
Once again, let’s look at the meat of our applicationContext.xml:
<bean id="renderManager" class="com.icesoft.faces.async.render.RenderManager" scope="singleton" />
<bean id="timebean" class="com.tss.beans.TimeBean" lazy-init="true" scope="session" />
There’s nothing explicitly tying the two beans together: Spring is detecting an autowired property in TimeBean, then looking for a unique instance of that class in its configuration, and injecting it. This makes configuration a snap.
Now you’ve seen how to use ICEFaces with JSF 1.2, which is fairly normal and documented, and you’ve also seen how to use Spring 2.5 along with ICEFaces, using ICEFaces’ best value proposition, the Direct-to-DOM rendering from server to client.