Client-Side Polling With Dynamic Faces

By Roger Kitain

The world of dynamic web applications offers various ways for a client and server to interact. Two approaches are particularly well suited for situations when information on the server changes frequently. These approaches are:

  • HTTP Streaming
  • Client Polling

In HTTP streaming, the client/server connection is left open for an extended period of time so that data is streamed from the server to the client. This approach is also known as server-side push, reverse Ajax, or comet. As information changes on the server, updates are pushed to the client.

With client polling, the browser periodically issues an XMLHttpRequest call to obtain new information from the server. For example, a client can send an XMLHttpRequest to the server every five seconds to get new information. This approach is also known as periodic refresh.

The Dynamic Faces framework brings the power of Ajax to traditional JavaServer Faces Technology (often abbreviated as JSF) applications. Ajax function calls are typically made in JavaScript, which can be unfamiliar to Java developers. With Dynamic Faces, you can add Ajax functionality to a JSF application with little or no JavaScript. Dynamic Faces provides a small "out of the box" JavaScript library that you can use with a JSF application.

This tip will show you how you can use Dynamic Faces to build a real-time, stock query application that does client-side polling. You'll see that you don't have to do much JavaScript coding. A package that contains the code for the sample application accompanies the tip. The code examples in the tip are taken from the source code of the sample (which is included in the package).

The Stock Query Application

This tip uses a stock query application to demonstrate client-side polling with Dynamic Faces.

First, let's take a look at the user interface (UI) for the application.

User Interface

Stock Query
Stock Query
 

The UI is pretty basic. You enter one or more space-delimited stock symbols in the Symbol text field and click the Search button. In response, the application displays a table of data pertinent to the stocks represented by the symbols you entered.

You enter proxy information in the Proxy Host and Proxy Port fields if you are behind a firewall.

The most interesting feature of the UI is the Streaming field. The choices are On or Off. If Streaming is set to On, the client polls the server, firing Ajax transactions every 10 seconds (or a specified time interval).

The Remote/Local field allows you to choose either Local or Remote. If you select Local, the application uses local data. This is the choice to make if a network connection is not available. If you select Remote, the application calls the Yahoo Stock Quoting service to get the stock data.

The size of the result table dynamically changes depending on the number of symbols that you enter.

Now let's take a look at the artifacts used in the application.

Artifacts

There are only three artifacts used in the application:

  • A JavaServer Pages technology (JSP) page
  • A JavaScript file
  • A JSF Managed Bean

JSP Page

Here's a snippet of the JSP page for the application, home.jsp, showing the relevant parts:

   <f:view>
   <html>
   <head>
   ...
   ...
   <jsfExt:scripts/>
   <script type="text/javascript">
   ...
   ...
   include_js('javascripts/stock-faces.js');
   </script>
   </head>
   <body>
     <h:form id="form" prependId="false">
     ...
     <h:panelGrid border="1" columns="1" 
        styleClass="panel-input-border">
             <h:panelGrid border="1" columns="7">
                 <h:outputText value="Symbol:"/>
                 <h:inputText id="symbol"/>
                 <h:commandButton id="search" value="Search"
                    onclick="DynaFaces.fireAjaxTransaction(
                    this, {});return false;"
               actionListener="#{bean.getStockInfo}" />
               ...
               <h:selectOneMenu id="streaming" value="Off" 
                  onchange="toggleStreaming()">
               ...
             </h:panelGrid>
     </h:panelGrid>  
     
     <h:panelGrid id="stockdata" border="1" columns="8"
       styleClass="panel-data-border" rendered="false">
     ...
   </body>
   </html>
   </f:view> 

Here are some things to notice in the code snippet:

  • <jsfExt:scripts/> is the standard tag to include for Dynamic Faces applications. It includes the Dynamic Faces JavaScript library.

  • The include_js('javascripts/stock-faces.js'); line is a utility function that loads the application's JavaScript file, stock-faces.js.

  • The h:commandButton tag has an onclick JavaScript event handler attached to it. The event handler, DynaFaces.fireAjaxTransaction, sends an Ajax request to the server when the button is clicked. The actionListener specified by #{bean.getStockInfo} is then executed on the server. What's significant here is that any view or JSF component manipulation done on the server happens using Ajax.

  • The "streaming" option is a h:selectOneMenu component that has an onchange JavaScript event handler.

  • A h:panelGrid tag with an id of "stockdata" is a placeholder the dynamic table of stock data. The attribute rendered is set to "false", meaning that the table is not initially rendered. However, the application code sets the attribute to true when there is stock data to return.

JavaScript File

Here is the JavaScript file, stockfaces.js, for the application:

   var pollId;
   
   /** Delay between requests to the server when polling. */ 
   var pollDelay = 10000;
   
   /** Start polling the server */
   function start() {
       pollId = setInterval(poll, pollDelay);
   }  
   
   /** Stop polling the server */
   function stop() {
       clearInterval(pollId);
   } 
   
   function poll() {
       queueEvent();
       DynaFaces.fireAjaxTransaction(null, {});
   }
   
   function queueEvent() {
       var actionEvent =
           new DynaFaces.ActionEvent("search",
           DynaFaces.PhaseId.INVOKE_APPLICATION);
       DynaFaces.queueFacesEvent(actionEvent);
       return false;
   }
   
   function toggleStreaming() {
       var menu = document.getElementById("streaming");
       var idx = menu.selectedIndex;
       var streaming = menu[idx].value;
       if (streaming == "Off") {
           stop();
       } else if (streaming == "On") {
           start();
       }
   }

Here's what the JavaScript code in the file does:

  • The polling delay, that is, the time interval between calls to the server, is set to 10 seconds.

  • The start() function initiates the server polling.

  • The stop() function stops server polling.

  • The poll() function queues up a server-side JSF action event. It then fires an Ajax request to the server using the Dynamic Faces library.

  • The queueEvent() function queues up a server-side JSF action event using the Dynamic Faces library. The action event is processed during the standard JSF lifecycle processing as the Ajax request flows to the server.

  • The toggleStreaming() function toggles the value of the "streaming" menu control.

JSF Managed Bean

Here's a snippet of the JSF managed bean, Bean.java, showing the relevant parts:

   /**
    * This bean has methods to retrieve stock information from 
    * the Yahoo quote service.
    */
   public class Bean {
   
       private static final String SERVICE_URL =
          "http://quote.yahoo.com/d/quotes.csv";
       /**
        * Action method that is used to retrieve stock 
        * information. This method uses two helper methods - one 
        * to get the stock information, and the other to 
        * dynamically build the "data" components for the UI.
        */
       public void getStockInfo(ActionEvent ae) {
   ...
   ...                       
          stockData = getStockData(symbols);                       
          buildUI(stockData);
   ...
    }
    
    /**
     * Helper method to get the stock data (remotely).
     */
    private String[] getStockData(String[] symbols)
        throws IOException, MalformedURLException {
        String[] data = new String[symbols.length];
        for (int i=0; i<symbols.length; i++) {
            StringBuffer sb = new StringBuffer(SERVICE_URL);
    ...
    ...
        }
        return data;
    }
    
    /**
     * Helper method to dynamically add JSF components to 
     * display the data.
     */
    private void buildUI(String[] stockData) {
        FacesContext context = 
            FacesContext.getCurrentInstance();
        UIForm form = 
            (UIForm)context.getViewRoot().findComponent("form");
        UIPanel dataPanel = 
            (UIPanel)form.findComponent("stockdata");
    ...
    ...
        // Create and add components with data values
    
        // Symbol
        ...
        dataPanel.getChildren().add(outputComponent);
        
        // Name
        ...
        dataPanel.getChildren().add(outputComponent);
        
        // Open Price (if any)
        ...
        dataPanel.getChildren().add(outputComponent);
        ...
        ...
        }
        dataPanel.setRendered(true);
    }

This JSF Managed Bean has an action method, getStockInfo, that does two things:

  • It uses a helper method, getStockData, to contact the Yahoo Stock Quote service (as defined by SERVICE_URL) to retrieve stock data for all the symbols.

  • It uses a helper method, buildUI, to build JSF components (from the stock data) and it adds the JSF components to the JSF component view. After all the components have been created and added, the action method sets the rendered attribute to true on the stockdata JSF component.

The action method, getStockInfo, is called when the Search button is pressed. It is also called as the result of an Ajax poll request. This is because each client poll queues an action event tied to this event handler. Refer to the queueEvent method in the stock-faces.js JavaScript file.

Running the Sample Code

A sample package accompanies this tip that demonstrates the techniques covered in the tip. You can deploy the sample package on any web container that supports the Servlet 2.5 API, JavaServer Pages (JSP) Technology 2.1, and JavaServer Faces Technology 1.2. These instructions assume that you are using GlassFish.

To install and run the sample:

  1. If you haven't already done so, download and install GlassFish.

  2. Download the sample application for the tip and extract its contents. You should now see the newly extracted directory as <sample_install_dir>/client-poll-dfaces, where <sample_install_dir> is the directory where you installed the sample application. For example, if you extracted the contents to C:\\ on a Windows machine, then your newly created directory should be at C:\\client-poll-dfaces.

  3. Start GlassFish by entering the following command:

    <GF_HOME>/bin/asadmin start-domain domain1

    where <GF_HOME> is the directory where you installed GlassFish.

  4. Deploy the sample by copying

    <sample_install_dir>/client-poll-dfaces/stock-faces.war to <GF_HOME>/domains/domain1/autodeploy

  5. Open your browser to the URL: http://localhost:8080/stock-faces/. You should see the Stock Query Application UI.

    Stock Query
    Stock Query
     
  6. Enter one or more stock symbols delimited by a space, for example, JAVA LMT IBM. If you are behind a firewall, specify the pertinent proxy information in the Proxy Host and Proxy Port fields. Click the Search button. You should see a table of stock data displayed for the symbols you entered.

    Stock Query
    Stock Query
     
  7. Try different combinations of streaming and Local/Remote settings, and see what happens. You'll notice that if Streaming is set to On, you don't have to press the Search button. The stock symbols that you specified in the Symbol text field are automatically sent using the Ajax mechanism to the server. If you choose Local, the names and prices are simulated, so that the data will likely be different than the result of a Remote selection.

Summary

This tip demonstrated how you can combine JSF with Ajax to produce dynamic applications. This application illustrated two features of Dynamic Faces:

  • fireAjaxTransaction
  • Remote JSF event queuing from JavaScript

You can find out more about Dynamic Faces in the jsf-extensions project. Also see Ed Burns's blog Introducing Project Dynamic Faces.

About the Author

Roger Kitain is the JavaServer Faces technology co-specification lead. He has been extensively involved with server-side web technologies and products since 1997. Roger started working on JSF in 2001 as a member of the reference implementation team. He has experience with Java Servlet technology, JSP, and most recently has been involved with different rendering technologies for JSF.

Comments:

Hi,

First of All Thanks a lot for publishing this artical and it is also informative.

Posted by Nihar Bhatt on December 19, 2007 at 02:49 AM PST #

Hi....

Thanks a lot .... i run your sample successfully ... and it is really helpful to me...

Posted by Nihar Bhatt on December 19, 2007 at 02:55 AM PST #

Thanks for the positive feedback.

Posted by Ed Ort on December 20, 2007 at 12:45 AM PST #

HTML Syntax: NOT allowed

Posted by guest on June 08, 2008 at 07:04 PM PDT #

Post a Comment:
Comments are closed for this entry.
About

edort

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today