The ICEfaces Tutorial
Table of Contents


Lesson:Easy Ajax Push

ICEfaces Easy Ajax Push Tutorial

Overview

The essence of Ajax Push is the ability to trigger updates to the client from events or changes that occur on the server. This makes Ajax Push highly suitable for collaborative applications where one user does something that changes the current state of the application and other users are interested in seeing that change. There are many different ways to take advantage of Ajax Push to add collaborative features to your rich web application. From the developer's perspective, Ajax Push is exposed through the SessionRenderer API. This tutorial is designed to provide a simple and clear illustration of how to get started with the SessionRenderer API to add Ajax Push to your application.

The project we are going to build in this tutorial is a page that contains simple, adjustable counters. The counter values are displayed on the page and buttons are provided that can be used to increment or decrement the counter values. The finished project looks like this:

Running application

The application consists of two counters. One is an application-scoped counter that can be adjusted and seen by all users. The second counter is session-scoped so changes to the value of this counter are restricted to the individual user. The goal is use the SessionRenderer API so that any modifications to the application-scoped counter result in Ajax Push updates being sent to all application users.

This tutorial will discuss the following topics:

 

Tools and Environment

You can develop ICEfaces applications using the tools of your choice. To make life a bit easier, ICEsoft also provides project integration plugins for a number of popular IDEs.

http://www.icefaces.org/main/downloads/os-downloads.iface

For this tutorial, we'll be demonstrating how to build the application using NetBeans 6.5. We've installed the ICEfaces Project Integration plugin for NetBeans 6.5 which is available from the site listed above. We'll also be developing using Facelets so we recommend that you use the NetBeans Plugins manager to download and install the Facelets Support plugin.

The code in this tutorial is designed to run compatibly on JDK 1.4. so we have purposely avoided the use of annotations, concurrency utilities, etc.

 

Create a New Project

With NetBeans running and the appropriate plugins intalled, you are ready to create a new ICEfaces project:

 

Choose Project

The first step is to choose the appropriate project type:

  1. From the menus, choose File -> New Project...
  2. On the first screen of the New Project wizard, choose the Java Web category and the Web Application project.
  3. Click Next >
 

Name and Location

Then select an appropriate name and location for the project:

  1. Provide a name (e.g. ajaxpush) and location for your new NetBeans project.
  2. Click Next >
 

Server and Settings

Select the application server and related settings:

  1. For this example, we'll choose Tomcat 6.0
  2. Click Next >
 

Frameworks

For our test application, we want the ICEfaces and Facelets framework support:

  1. From the list of frameworks provided, choose ICEfaces.
  2. Select the ICEfaces configuration option "Concurrent DOM View".
  3. If you'd like to design your project visually, you can choose Visual Web ICEfaces instead. As we don't really have a complex visual requirement and we are focusing on Ajax Push, the ICEfaces framework is suitable for this tutorial.

  4. If you've downloaded and installed the Facelets Support plugin, you should select the checkbox for that framework as well.
  5. Click Finish

Once you've completed the project wizard, you should have an initial project in NetBeans that looks like this:

 

Creating the Managed Beans

Our first task is to create the backing beans that will be used to hold the state of the counters. First we'll start with a generic counter:

 

Create a New Java Package

  1. Right-click on Source Packages, select New, then select Java Package...
  2. In the Name and Location dialog, type in a new package name (e.g. org.icefaces.tutorial).
  3. Click Finish
 

Create a New Counter Class

First we create a class file for our Counter:

  1. Under Source Packages, right-click on the newly created package, select New, then select Java Class...
  2. Type in a new class name (e.g. Counter).
  3. Click Finish.
 

Implement the Counter Class

Now we are going to provide the logic of our Counter bean. We'll use a simple integer member variable as the current counter value, provide getter and setter methods for the value, and also add a couple of methods for incrementing and decrementing the value. The methods will be used as JSF actionListeners so they need to take an ActionEvent as a parameter.

You can type or copy the following into the editing tab for the Counter.java file::

  1. Under Source Packages, right-click on the newly created package, select New, then select Java Class...
  2. Type in a new class name (e.g. Counter).
  3. Click Finish.
package org.icefaces.tutorial;

import javax.faces.event.ActionEvent;

public class Counter {

    private int count;

    public Counter() {
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public void increment(ActionEvent event) {
        count++;
    }

    public void decrement(ActionEvent event) {
        count--;
    }
}
      

Your project should now look something like this:

 

Create a New ApplicationCounter Class

Since multiple users can potentially access and modify the value of our application-scoped counter, we need to create a version that guards against concurrent access. So we'll create an ApplicationCounter that extends our basic Counter:

  1. Under Source Packages, right-click on the package, select New, then select Java Class...
  2. Type in a new class name (e.g. ApplicationCounter).
  3. Click Finish.
 

Implement the ApplicationCounter Class

You can type or copy the following into the editing tab for the Counter.java file::

package org.icefaces.tutorial;

import javax.faces.event.ActionEvent;

public class ApplicationCounter extends Counter {

    public ApplicationCounter() {
    }

    public synchronized void setCount(int count){
        super.setCount(count);
    }

    public synchronized void increment(ActionEvent event) {
        super.increment(event);
    }

    public synchronized void decrement(ActionEvent event) {
        super.decrement(event);
    }
}
      

Your project should now look something like this:

 

Define Managed Beans

To make use of our classes as JSF managed beans, we need to declare them in our faces-config.xml file:

  1. Under WEB-INF, open the faces-config.xml file for editing.
  2. Switch to the XML view (rather than PageFlow).
    1. To create the session-scoped bean:

    2. Right-click in the editor and choose JavaServer Faces -> Add Managed Bean..
    3. Give the bean a unique name (e.g. sessionCounter).
    4. Type or browse for the correct class (e.g. org.icefaces.tutorial.Counter).
    5. Choose the correct scope. For this bean, 'session'.
    6. Add a description (this is optional).
    7. Click Add.

      To create the application-scoped bean:

    1. Right-click in the editor and choose JavaServer Faces -> Add Managed Bean..
    2. Give the bean a unique name (e.g. applicationCounter).
    3. Type or browse for the correct class (e.g. org.icefaces.tutorial.ApplicationCounter).
    4. Choose the correct scope. For this bean, 'application'.
    5. Add a description (this is optional).
    6. Click Add.

The faces-config.xml file should now look something like this:

 

Creating the Pages

Now we need to create our user interface that we can bind our bean values and methods to. When the project was automatically created, the ICEfaces pluging created a couple of JSP files for us (forward.jsp and welcomeJSF.jsp). By adding the Facelets Support plugin, it created a couple of starting facelets pages (template.xhtml and template-client.xhtml). For this tutorial, we're going to use Facelets so we'll be concentrating on the .xhtml resources.

With Facelets, the typical approach is to create a template (template.xhtml) that can be used to dynamically include content. Since this isn't designed to be a tutorial on Facelets, we'll simply describe the minium steps required to get our counters up and running.

 

Configure Facelets Support

To use Facelets with ICEfaces, there are a couple of configuration settings that we need to apply. ICEfaces uses it's own copy of the Facelets library which is included as part of the ICEfaces integration plugin. To ensure that the library is applied to the project:

  1. Right-click on your project and choose Properties.
  2. Choose Libraries from the Categories list.
  3. If the "Facelets ICEfaces Run-Time 1.7.2" libray is already in the list of compile-time libraries, the project is correctly configured. If it's not in the list:

    1. Click the Add Library... button.
    2. Choose "Facelets ICEfaces Run-Time 1.7.2" from the list.
    3. Click the Add Library... button.

    In either case, the complete set of libraries should look something like this:

    When using Facelets with ICEfaces, the ICEfaces framework uses a custom JSF ViewHandler implementation. This must be specified in the faces-config.xml file:

  4. Under WEB-INF, open the faces-config.xml file for editing.
  5. Switch to the XML view (rather than PageFlow).
  6. If necessary, add the D2DFaceletViewHandler to the application section of the configuration file by copying or typing the following:
  7.    <application>
            <view-handler>com.icesoft.faces.facelets.D2DFaceletViewHandler</view-handler>
        </application>
       
  8. Under WEB-INF, open the faces-config.xml file for editing.

When you are done, the faces-config.xml file should look something like this. Note that it's possible for more than one ViewHandler to be configured. They are designed to delegate responsibilities appropriately.

 

Edit the Facelet Pages

By including the Facelets framework plugin when the project was initially created, two Facelet files are automatically created for you. We'll edit those files now:

  1. Open up the template-client.xhtml file for editing. This file provides the viewable content based on the configuration described in the template (template.xhtml).
  2. Add the namespace for the ICEfaces components to the html tag. We need to add this so that we can use ICEfaces components (e.g. ) in the page.
  3.    <html xmlns="http://www.w3.org/1999/xhtml"
          xmlns:ui="http://java.sun.com/jsf/facelets"
          xmlns:h="http://java.sun.com/jsf/html"
          xmlns:ice="http://www.icesoft.com/icefaces/component">
       
  4. Change the title definition from:
  5.       <ui:define name="title">
             Facelets
          </ui:define>
       

    to

          <ui:define name="title">
             Ajax Push Counter
          </ui:define>
       
  6. Change the body definition from:
  7.       <ui:define name="body">
             Hello from the Facelets client template!
          </ui:define>
       

    to

           <ui:define name="body">
                 <ice:form>
                     <ice:panelGrid columns="3">
                         <ice:outputText value="Application counter:" />
                         <ice:outputText value="#{applicationCounter.count}" />
                         <ice:panelGroup>
                             <ice:commandButton value="-"
                                                actionListener="#{applicationCounter.decrement}"/>
                             <ice:commandButton value="+"
                                                actionListener="#{applicationCounter.increment}"/>
                         </ice:panelGroup>
    
                         <ice:outputText value="Session counter:" />
                         <ice:outputText value="#{sessionCounter.count}" />
                         <ice:panelGroup>
                             <ice:commandButton value="-"
                                                actionListener="#{sessionCounter.decrement}"/>
                             <ice:commandButton value="+"
                                                actionListener="#{sessionCounter.increment}"/>
                         </ice:panelGroup>
    
                     </ice:panelGrid>
                 </ice:form>
             </ui:define>
       

The body content now provides a number of JSF components including a form, a panelGrid (for layout), outputText for showing the values of our counters, and commandButtons for modifying the values of the counters. The values and methods use the JSF expression language to bind to the backing beans we described in our faces-config.xml.

 

Running without Ajax Push

 

Preparing to Run

Before running the application, you should ensure that the default URL that NetBeans attempts to use for launching your browser is correct. By default, the ICEfaces project attempts to use the JSP page via the ICEfaces extension (welcomeJSF.iface). You should change this to use the default facelets page with the proper extension (template-client.iface).

  1. Right-click on the project name (e.g. ajaxpush) and choose Properties.
  2. Select the Run category.
  3. Ensure that the Relative URL value points to your Facelets page and uses the virtual ICEfaces extension (e.g. template-client.iface).
 

Running the Application

If the tutorial is your main (or only) NetBeans project, then you should be able to Run Main Project, using either the green arrow button in the toolbar, the F6 key, or choosing Run -> Run Main Project from the menu. Your browser should automatically launch using the following URL:

http://localhost:8080/ajaxpush/template-client.iface

And the application should look something like this:

Clicking the buttons allows you to increment and decrement the counter values. Opening a second browser shows the the application counter is shared, but the session counters are not. There are two very important things to note at this point:

 

Adding Ajax Push with SessionRenderer

There are basically two parts to using the SessionRenderer API.

The SessionRenderer API supports this in a simple and straight-forward manner.

public static void addCurrentSession(final String groupName)
public static void removeCurrentSession(final String groupName)
public static void render(final String groupName)
     

Since we want everyone to be able to access the application-scoped counter, we're going to keep it simple and just add everyone to a single, global group. The easiest way to do this is to have the group membership logic in the constructor of the session-scoped bean. That way, when our session-scoped bean is created, it's added to a group.

Since we only want to do this for our session-scoped counter, and not our application-scoped counter, we'll create another subclass of Counter that does this for us.

 

Create a new SessionCounter class

  1. Under Source Packages, right-click on the package, select New, then select Java Class...
  2. Type in a new class name (e.g. SessionCounter).
  3. Click Finish.
 

Implement the SessionCounter class

The only thing we want to do differently in this counter is add the current session to our group. You can type or copy the following into the editing tab for the Counter.java file:

package org.icefaces.tutorial;

import org.icefaces.x.core.push.SessionRenderer;

public class SessionCounter extends Counter {

    public SessionCounter() {
        SessionRenderer.addCurrentSession("all");
    }
}
      

Your project should now look something like this:

For ICEfaces 1.7.1 and 1.7.2, the SessionRenderer API was considered experimental so the package for it reflects this (org.icefaces.x.core.push.SessionRenderer). For ICEfaces 1.8, the API is no longer considered experimental and resides with the rest of the Ajax Push APIs under com.icesoft.faces.async.render.

 

Adjust the Managed Bean Description

Now that we have a new bean implementation for our session-scoped counter, we need to update the JSF descriptor to make use of it.

  1. Under WEB-INF, open the faces-config.xml file for editing.
  2. Switch to the XML view (rather than PageFlow).
  3. Change the class of the session-scoped bean from Counter to SessionCounter:
  4.  
    <managed-bean>
      <description>A session-scoped counter.</description>
      <managed-bean-name>sessionCounter</managed-bean-name>
      <managed-bean-class>org.icefaces.tutorial.Counter</managed-bean-class>
      <managed-bean-scope>session</managed-bean-scope>
    </managed-bean>
       

    becomes

    <managed-bean>
      <description>A session-scoped counter.</description>
      <managed-bean-name>sessionCounter</managed-bean-name>
      <managed-bean-class>org.icefaces.tutorial.SessionCounter</managed-bean-class>
      <managed-bean-scope>session</managed-bean-scope>
    </managed-bean>
       

Your faces-config.xml file should now read like this:

 

Using SessionRender for Ajax Push

Our final step in adding Ajax Push features to our application counter requires modifications to the ApplicationCounter class. What we want is for updates to be pushed out whenever there is a change. To do this, you simply need to call the render method of the SessionRenderer when a notable change occurs.

public static void render(final String groupName)
  1. Type or copy the following changes to the ApplicationCounter class:
package org.icefaces.tutorial;

import javax.faces.event.ActionEvent;
import org.icefaces.x.core.push.SessionRenderer;

public class ApplicationCounter extends Counter {

    public ApplicationCounter() {
    }

    public synchronized void setCount(int count){
        super.setCount(count);
        SessionRenderer.render("all");
    }

    public synchronized void increment(ActionEvent event) {
        super.increment(event);
        SessionRenderer.render("all");
   }

    public synchronized void decrement(ActionEvent event) {
        super.decrement(event);
        SessionRenderer.render("all");
   }
}      

So whenever a change is made to the application-scoped counter, we issue a request for a render to the group "all" which, in our case, means every active session.

 

Running with Ajax Push

 

Running the Application

Now if you run the main project (using either the green arrow button in the toolbar, the F6 key, or choosing Run -> Run Main Project from the menu) and point two browsers at the application:

http://localhost:8080/ajaxpush/template-client.iface

Clicking the buttons for the application-scoped counter should provide a richer experience. Updates from interaction in one browser that result in a change to the application counter are now pushed out to other active users. And there you have it. Using the SessionRenderer API is an easy and straightforward way to add collaborative features to your ICEfaces applications.


The ICEfaces Tutorial
Table of Contents

Copyright 2006 ICEsoft Technologies Inc. All rights reserved.