X
  • Sun
    October 26, 2007

Client-Side Polling With Dynamic Faces

Guest Author

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.

Join the discussion

Comments ( 4 )
  • Nihar Bhatt Wednesday, December 19, 2007

    Hi,

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


  • Nihar Bhatt Wednesday, December 19, 2007

    Hi....

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


  • Ed Ort Thursday, December 20, 2007

    Thanks for the positive feedback.


  • guest Monday, June 9, 2008

    HTML Syntax: NOT allowed


Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.