LAB-5558: Developing Revolutionary Web Applications, Using Ajax Push or Comet

Expected Duration: 120 minutes

Exercise 1: Build a Comet Chat Application (20 minutes)

 


In this lesson we are going to build a chat applicaton using comet.


Background Information

 

The term Ajax was invented to describe the technologies that provide background request-response between a web server and client browser. With Ajax, web applications can retrieve data from the server in the background, essentially preloading information that is revealed in response to a user gesture and giving the impression of a more responsive web page. Although the data appears to be downloaded asynchronously, the server responds to client requests — typically through the use of the XMLHttpRequest object.

Web protocols require that servers respond to browser requests. Ajax falls short for multi-user, interactive pages because one user won't see changes other users make until the browser sends a request. In order for a server to push data to a browser asynchronously, some design workarounds are required. These design strategies are broadly described by the term Reverse Ajax.

One strategy is to use browser polling, in which the browser makes a request of the server every few seconds. Another strategy, known as piggybacking, delays a server's page update until the next request is received from the browser, at which time the server sends the pending update along with the response.

A third strategy, and the one used in the example application presented in this article, is to design with long-lived HTTP connections between the browser and server. With this strategy, a browser does not poll the server for updates; instead, the server has an open line of communication it can use to send data to the browser asynchronously. Such connections reduce the latency with which messages are passed to the server. This technique is known as Comet — a play on words that can be appreciated by users of household cleansers.

The example application in this exercise provides a chat feature that enables users to publishmessages and let subscribed users see the messages updated on their browser.

The example application incorporates the following technologies. All are available for download.

  • Ajax – Techniques that provide background updating of web pages using a request-response model.
  • Comet – Techniques that enable a server to push data to client browsers through an HTTP open line of communication.
  • Dojo – An open-source DHTML toolkit written in JavaScript. The Dojo Toolkit includes many utilities that go beyond Ajax. For example, the dojox.comet module simplifies programming Comet applications.
  • Bayeux – A protocol for routing JSON-encoded events between clients and servers in a publish-subscribe model.
  • GlassFish – Open-source Java EE-compliant application server.
  • Grizzly – Open-source framework designed to help developers take advantage of the Java New I/O API (Java NIO API). Grizzly comes bundled with GlassFish application server, but it can be used separately.
More About the Technologies

Comet is a term coined by Alex Russell to describe applications where the server pushes data to the client over a long-lived HTTP connection. Comet uses the persistent connection feature in HTTP/1.1. This feature keeps alive the TCP connection between the server and the browser until an explicit 'close connection' message is sent by one or the other, or until a timeout or network error occurs. The technique enables the server to send a message to the client when an event occurs without waiting for the client to send a request.  Cometd is a scalable HTTP-based event routing bus that uses a push technology pattern known as Comet.

Grizzly is an HTTP framework that uses the Java NIO API to provide fast HTTP processing. Grizzly provides long-lived streaming HTTP connections that can be used by Comet, with support built on top of Grizzly's Asynchronous Request Processing (ARP). With Grizzly ARP, each Comet request doesn't monopolize a thread, and the availability of threads permits scalability.

Bayeux is a protocol for routing JSON-encoded events between clients and servers in a publish-subscribe model. Grizzly provides an implementation of Bayeux, which makes it easy to build Comet applications with dojox.comet. With the support for Cometd and Bayeux, an application can be developed using only Dojo (or Ajax component), and the application can be deployed in any server that supports the Bayeux protocol.  To build your application, configure GlassFish for Comet and configure your web application's web.xml file for the Grizzly Bayeux servlet. You can then use the dojox cometd publish and subscribe methods to send and receive Comet events. These techniques are described in more detail in the description of the application.   

Grizzly Architecture

Figure 1: Grizzly Architecture


Steps to Follow

 

Step 1: create a new Web Application project

  1. If NetBeans is not already running, start it.
  2. Choose File > New Project (Ctrl-Shift-N) from the main menu. Under Categories, select Java Web. Under Projects, select Web Application and click Next.

  3. Type chat  in the Project Name field. Note that the Context Path becomes /chat.

  4. Specify the Project Location to the exercise1 sub directory of this lab on your computer.

  5. Under Server, select GlassFish v3. GlassFish is a Java EE5-certified application server and is bundled with the Web and Java EE insallation of NetBeans IDE.

  6. Leave the Set as Main Project option selected and click Finish. The IDE creates the chat project folder. The project folder contains all of your sources and project metadata, such as the project's Ant build script. The chat project opens in the IDE. The welcome page, index.jsp, opens in the Source Editor in the main window. You can view the project's file structure in the Files window (Ctrl-2), and its logical structure in the Projects window (Ctrl-1).)

Step 2: Installing and Using Dojo With NetBeans IDE

There are three main ways to install Dojo, which you can read about in the book of Dojo. Netbeans 6.5 has JavaScript libraries bundled with it, which makes it easy to add the dojo libraries to your project.


In this lab we are not using the current bundled version because it does not contain dojox, which we need for the dojox.grid widget and dojox.cometd. If you want to use a different version of dojo than the bundled one (like in this HOL), another easy way to use Dojo with the NetBeans IDE is to download and extract the dojo JavaScript library, and then copy it into your Netbeans project web directory: .../web.  The dojo JavaScript library has already been downloaded and extracted for this HOL and is in the js  folder  in the hol\resources\dojo directory.

  1. In your file system copy the js  folder  from the <lab_root>\exercises\exercise1\dojo directory to your chat project's web directory \exercises\exercise1\chat\web. The js  folder contains the dojo javascript libraries.

  2. You should now see the js  folder in your chat project as below:

Step 3:

  1. In the Projects window right click the chat node (from exercise 1), and select New >  Other....

  2. In the New File window select Web HTML and click Next

  3. In the New HTML File Window, enter the HTML file name index, and click Finish.

  4. If the index.html file is not open , then Double click on the index.html file to open it in the editor. 

  5. Copy all of the html contents below and paste them to replace the contents of the index.html file in the editor.


    <html>
    <head>
        <title>chat</title>
        <style type="text/css">
            @import "js/dijit/themes/tundra/tundra.css";
            @import "js/dojo/resources/dojo.css";

        </style>

        <script type="text/javascript" src="js/dojo/dojo.js"
                djConfig="parseOnLoad: true"></script>
        <script type="text/javascript" src="chat.js"></script>
        <link rel="stylesheet" type="text/css" href="chat.css">
    </head>
    <body class="tundra">

        <h1>Chat</h1>
        <br>
        <div id="chatroom">
            <div id="join" >
                <label for="sendName" style="float: left; width: 100px; padding: 3px;">Name:</label>
                <input id="sendName" type="text" dojoType="dijit.form.TextBox">
                <button id="joinB" dojoType="dijit.form.Button">Join</button>
            </div>
            <div id="joined" class="hidden">
                <label for="sendText" style="float: left; width: 100px; padding: 3px;">Message:</label>
                <input id="sendText" type="text" dojoType="dijit.form.TextBox">
                <button id="sendB" dojoType="dijit.form.Button">Send</button>
                <button id="leaveB" dojoType="dijit.form.Button">Leave</button>
            </div> <br>
            <div id="chat">
                <div id="messageLog">Messages:</div>
            </div>
        </div>
    </body>
    </html>

  6. In the Projects window right click the chat node (from exercise 1), and select New >  Other....

  7. In the New File window select Web Cascading Style Sheet and click Next

  8. In the New CSS File Window, enter the CSS file name chat, and click Finish.

  9. Double click on thechat.css file to open it in the editor. 

  10. Copy all of the  contents below and paste them to replace the contents of the chat.css file in the editor.


    div
    {
        border: 0px solid black;
    }
    div#chatroom
    {
        background-color: #e0e0e0;
        border: 1px solid black;
        width: 45em;
    }
    div#chat
    {
        background-color: #f0f0f0;
        padding: 4px;
        border: 0px solid black;
    }
    div#input
    {
        clear: both;
        padding: 4px;
        border: 0px solid black;
        border-top: 1px solid black;
    }
    input#sendText
    {
        width:28em;
        background-color: #e0f0f0;
    }
    input#sendName
    {
        width:14em;
        background-color: #e0f0f0;
    }
    div.hidden
    {
        display: none;
    }
    span.alert
    {
        font-style: italic;
    }
       
  11. In the Projects window right click the chat node (from exercise 1), and select New > Other....

  12. In the New File window select Web JavaScriptFile and click Next

  13. In the NewJavaScript File Window, enter the  file name chat, select the chat web Folder, and click Finish.

  14. Double click on the chat.js  file to open it in the editor. 

  15. Copy the JavaScript contents below and paste them into the chat.js file in the editor. 


    dojo.require("dojox.cometd");

    var room = {  
        username: null,
        channel: "/chat/demo",
       
        join: function(name){       
            dojox.cometd.init("cometd");
            room.username=name;
            dojo.byId('join').className='hidden';
            dojo.byId('joined').className='';
            dojox.cometd.subscribe(room.channel, room, "chatCallback");
            dojox.cometd.publish(room.channel, {
                user: room.username,
                text: " has joined"
            });
        },
        leave: function(){
            if (room.username==null)
                return;
            dojox.cometd.unsubscribe(room.channel, room, "chatCallback");
            dojox.cometd.publish(room.channel, {
                user: room.username,
                text: " has left"
            });
            dojox.cometd.disconnect();
            // switch the input form
            dojo.byId('join').className='';
            dojo.byId('joined').className='hidden';
            room.username=null;
        },
        chat: function(text){
            dojox.cometd.publish(room.channel, {
                user: room.username,
                text: text
            });
        },
        chatCallback: function(message){ 
            if(!message.data){
                alert("bad message format "+message);
                return;
            } 
            var messageLog=dojo.byId('messageLog');      
            var from=message.data.user;
            var text=message.data.text;
            from+=":";
            // display the  chat text
            messageLog.innerHTML += "<br/><span class=\"from\">"+from+" &nbsp;</span><span class=\"text\">"+text+"</span>";
        },
       
        init: function(){
            dojo.connect(dojo.byId("joinB"),"onclick",function(){
                if (!dojo.byId("sendName").value.length) {
                    alert("Please enter Name");
                    return;
                }
                room.join(dojo.byId('sendName').value);
            });      
            dojo.connect(dojo.byId("sendB"),"onclick",function(){
                if (!dojo.byId("sendText").value.length) {
                    alert("Please enter Text");
                    return;
                }
                room.chat(dojo.byId("sendText").value);
            });      
            dojo.connect(dojo.byId("leaveB"),"onclick",function(){
                room.leave();
            });       
        }
    };

    dojo.addOnLoad(room, "init");
    dojo.addOnUnload(room,"leave");



Edit the web.xml file to change the welcome page:

  1. double click on the web.xml file in the chat WEB-INF directory

  2. Click on the Pages tab. Change the Welcome File from index.jsp to index.html .



Step 4: Configuring for Comet

The goal of this part of exercise 1  is to configure Glassfish for comet and your web project for the Grizzly Cometd servlet.

  1. Enabling Bayeux in GlassFish

    To enable Bayeux on GlassFish, add the lines shown in red below to your Web application's web.xml file:

    Code Sample from:  web.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" ...>


      <servlet>
        <servlet-name>Grizzly Cometd Servlet</servlet-name>
        <servlet-class>
          com.sun.grizzly.cometd.servlet.CometdServlet
        </servlet-class>
        <init-param>
         <param-name>expirationDelay</param-name>
         <param-value>-1</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
      </servlet>
      <servlet-mapping>
       <servlet-name>Grizzly Cometd Servlet</servlet-name>
       <url-pattern>/cometd/*</url-pattern>
      </servlet-mapping>

      <session-config>
          <session-timeout>
                30
          </session-timeout>
      </session-config>
      <welcome-file-list>
            <welcome-file>index.html</welcome-file>
      </welcome-file-list>
    </web-app>

    Every request sent to your war file's context-path/cometd/ will be serviced by the Grizzly Bayeux runtime.


  2. Enable Comet support in Glassfish if you haven't done so in exercise 0.
    See exercise 0 for detail on how to enable comet support in Glassfish if you haven't done so.

Step 5: Run the Project 

  1. Right click the  chat node in the Projects window, and select Run.

  2. When you run the project, your browser should display the opening page of the dojo comet Sample Application (at http://localhost:8080/chat/). 
  3. Open another browser, preferably with different profile or different brand of browser, and type http://localhost:8080/chat/ to access the application. Enter name to join the chat and then enter some message.  

    Comet chat page, which allows the users to chat at the same time.

    Different Browsers Sharing Photos in Comet chat
Step 6: Use Firebug to see the JSON Messages

Firebug is Firefox plugin which allows you to debug, and monitor CSS, HTML, and JavaScript live in any web page.  In this lab you can use it to see the HTTP Request and Response content in order to see the JSON messages exchanged during the chat session. In Firefox , Select Tools > Firebug > Open Firebug.



Enabling Firbug Panels

To work on Javascript code or to study the network action on a site, you need to enable one or more Firebug panels. If you select the Console, Script, or Net panels, you will see the tab is grey and the panel says "disabled". Each panel tab has a small menu control for enabling the panel. All the panels can be enabled or disabled using the context menu (right click) on the Firebug status bar icon.

See  Enabling Firebug for more information.

Click on the Console panel and select Enabled.



In the Chat Application type in a name and click Join .  You should see the HTTP Posts as shown below. Click to expand a Post Request , then select  the Post tab , you should see the Bayeux JSON message  sent as shown below.  When you join a the chat session multiple json messages are sent between the browser client and the glassfish servlet to perform a handshake. This is explained more in understanding the code below.  The image below shows the JSON message that user carol has joined.




Click to expand a Post Request , then select  the Response tab , you should see the Bayeux JSON message received as shown below.



Click to expand the last Post Request , then select  the Post tab , you should see the Bayeux JSON message as shown below.  This is the long-polling HTTP "keep alive"  Request which remains  blocked until a message in the chat session is sent (or until it times out).



Understanding the code


 Using dojox.cometd


Loading Base Dojo and Required Modules Into the Application

This script element: src="src/dojo/dojo.js" loads the base dojo script that gives you access to the Dojo functionality, other dojo modules are loaded as required with the the dojo.require function explained later. 

Code Sample from:  index.html

<script type="text/javascript" src="js/dojo/dojo.js"></script>
<script type="text/javascript" src="chat.js"></script>

The rest of the JavaScript code for this application is in the file chat.js. Note that NetBeans IDE 6.5  has a very capable JavaScript editor, shown below editing the chat.js file:




In chat.js, the application uses the dojo.require function (similar to the import directive in Java) to specify which Dojo modules to load.

Code Sample from:  chat.js


dojo.require("dojox.cometd");


Dojo is organized into three major layers: Dojo Core, Dijit, and DojoX. DojoX builds on Dojo Core and provides newer extensions to the Dojo Toolkit. DojoX cometd implements a Bayeux protocol client for use with a Bayeux server.
 
Initializing a Connection Between the Dojo Client and the Grizzly Bayeux Servlet

Upon loading the chat application, a user can enter a username and join a chat session, as shown in Figure 3.



Figure 3: User Login Page
 

When a user clicks the Join button, the join JavaScript function is called. In the join function, the call to dojox.cometd.init initializes a connection to the given Comet server — in this case with the GlassFish Grizzly Bayeux servlet. Note that "cometd" is the URL-pattern for the Grizzly Cometd servlet configured in the web.xml file for the application.

Code Sample from:  chat.js

var room = {
...
    join: function(name){

      dojox.cometd.init("cometd");
      dojox.cometd.subscribe("/chat/demo", room, "chatCallback");
      dojox.cometd.publish("/chat/demo",
         {  user: room.username,
            text: " has joined"});
    }


The dojox.cometd.subscribe line subscribes the chatCallback callback function to the /chat/demo channel. Whenever a message is sent to the /chat/demo channel, the chatCallback function is called. The dojox.cometd.publish line publishes a message that the user whose name was entered with the Join button has joined the /chat/demo channel. All subscribers to the /chat/demo channel receive the message.

Using Firebug you can see that dojox.cometd.init("cometd") sends the following with a POST to the server:


message=
[ {
     "version":"1.0","minimumVersion":"0.9",
    "channel":"/meta /handshake",
     "id":"0",
     "supportedConnectionTypes":["long-polling", "callback-polling"]
  } ]


A Bayeux client initiates a connection negotiation by sending a message to the "/meta/handshake" channel. A Bayeux server must respond to a handshake request with a handshake response message in the body content of the response.  The response looks like this:


[ {
   "channel":"/meta/handshake",
     "version":"1.0",
    "supportedConnectionTypes":["long-polling","callback-polling"],
    "minimumVersion":"0.9","id":"0",
    "clientId":"xtY0HrhJ/YkXetV1CWaRSw==",
    "successful":true,
    "advice":{"reconnect" :"retry", "interval":0,"multiple-clients":false},
    "authSuccessful":true
}]


When you join a the chat session multiple json messages are sent between the browser client and the glassfish servlet to perform a handshake, which is specified in the Bayeux protocol

Using Firebug you can see that  dojox.cometd.publish("/chat/demo",{  user: room.username,text: " has joined"}) 
sends the following with a POST to the server:


message=[
 {  "channel":"/meta/subscribe",
    "subscription":"/chat/demo",
    "clientId":"xtY0HrhJ/YkXetV1CWaRSw==",
     "id":"2" },
 {  "data":{"user":"carol","text":" has joined"},
     "channel":"/chat/demo",
     "clientId":"xtY0HrhJ/YkXetV1CWaRSw==",
     "id":"3"}
]


The response looks like this:


[
  {"channel":"/meta/subscribe",
  "successful":true,
  "clientId":"xtY0HrhJ/YkXetV1CWaRSw==",
  "subscription":"/chat/demo",
   "id":"2"},
   {"channel":"/chat/demo",
   "successful":true,
   "clientId":"xtY0HrhJ/YkXetV1CWaRSw==",
    "id":"3"},
 { "channel":"/chat/demo",
    "data":{"text":" has joined","user":"carol"},
    "id":"3",
    "clientId":"xtY0HrhJ/YkXetV1CWaRSw=="}
]




Publishing the Next Slide for the Comet chat

When the user clicks the Send button, a JavaScript function is called that publishes the message, as shown in Figure 4.



Figure 4: Viewing the Next Slide
 
Code Sample from:  index.html
<button id="sendB" dojoType="dijit.form.Button">Send</button>
 

The JavaScript function that is called when the user clicks the Send button is shown in the following code sample. This function calls room.chat, which publishes the message.

Code Sample from:  chat.js
var room = {

..
dojo.connect(dojo.byId("sendB"),"onclick",function(){
if (!dojo.byId("sendText").value.length) {
alert("Please enter Text");
return;
}
room.chat(dojo.byId("sendText").value);
});
 

The function room.chat, shown in the following code sample, calls dojox.cometd.publish to publish the message text(which it receives as an input argument) to the /chat/demo channel. All subscribers to the /chat/demo channel receive this message.

Code Sample from:  chat.js

var room = {
channel: "/chat/demo",
...

chat: function(text){
dojox.cometd.publish(room.channel, {
user: room.username,
text: text
});
},
...
}
 

When a message is published to a Bayeux channel on the server, it is delivered to all clients subscribed to that channel — in this case, to the /chat/demo channel.

In the room.join function described earlier, dojox.cometd.subscribe("/chat/demo", room, "chatCallback") was called to subscribe the chatCallback callback function to the /chat/demo channel. The chatCallback function, shown in the following code sample, is called with the published message as an input argument. The chatCallback function updates the browser page by setting the messageLog dom element innerHTML to the text from the published message . This  updates the browser page with the message text that was published.

Code Sample from:  chat.js

var room = {
...
chatCallback: function(message){
if(!message.data){
alert("bad message format "+message);
return;
}
var messageLog=dojo.byId('messageLog');
var from=message.data.user;
var text=message.data.text;
from+=":";
// display the chat text
messageLog.innerHTML += from+" &nbsp;"+text;
},
...
}

   



Summary

 

The example application demonstrates the use of Dojo, Comet, Bayeux,  running on the GlassFish application server.



See Also

For more information see the following resources:


 

Back to top
Next exercise