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

Expected Duration: 1220 minutes
Contacts: Carol McDonald,  Doris Chen, Justin Bolter

Exercise 2: Build a Comet Slideshow Application (20 minutes)

 

The example application in this exercise presents a photo slideshow that can be controlled by multiple users. Actions of one user affect the pages seen by other users. The application provides a chat feature that enables users to comment on photos and let other users see the comments.

 

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 slideshow in the Project Name field. Note that the Context Path becomes /slideshow.

  4. Specify the Project Location to the exercise2  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 slideshow project folder. The project folder contains all of your sources and project metadata, such as the project's Ant build script. The slideshow 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

In this lab we are not using the  version of dojo bundled with Netbeans 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 slideshow web directory <lab_root>\exercises\exercise2\slideshow\web. The js  folder contains the dojo javascript libraries.

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

  3. In your file system copy the images  folder  from the <lab_root>\exercises\exercise2\ directory to your slideshow web directory <lab_root>\exercises\exercise2\slideshow\web. The images  folder contains the images for the slideshow.

Step 3:

  1. In the Projects window right click the slideshow 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. 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>Comet Slideshow</title>

        <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>
        <h1>Comet Slideshow</h1>

        <div id="chatroom">
            <div id="chat">
                 <div id="messageLog"></div>
            </div>
            <div id="slide"></div>

            <div id="input">
                <div id="join" >
                    Name:&nbsp;<input id="sendName" type="text"/><input id="joinB" class="button" type="submit" name="join" value="Join"/>
                </div>
                <div id="joined" class="hidden">
                    <input id="previousB" class="button" type="submit" name="previous" value="Previous Slide"/>
                    <input id="nextB" class="button" type="submit" name="next" value="Next Slide"/><br>
                    Message:&nbsp;<input id="sendText" type="text"></input>
                    <input id="sendB" class="button" type="submit" name="join" value="Send"/>
                    <input id="leaveB" class="button" type="submit" name="join" value="Leave"/>
                </div>
            </div>
        </div>
        <p id=returnMsg></p>
    </body>
    </html>

  6. In the Projects window right click the slideshow 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 the chat.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
    {
      height: 5ex;
      overflow: auto;
      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.from
    {
      font-weight: bold;
    }

    span.alert
    {
      font-style: italic;
    }
       
  11. In the Projects window right click the slideshow 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 slideshow 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='';
            dojo.byId('sendText').focus();
            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';
            dojo.byId('sendName').focus();
            room.username=null;
        },     
        chat: function(text){
            dojox.cometd.publish(room.channel, {
                user: room.username,
                text: text
            });
        },
        next: function(text){
            dojox.cometd.publish(room.channel, {
                slide: text
            });
        },
        chatCallback: function(message){
            if(!message.data){
                alert("bad message format "+message);
                return;
            }
            var chat=dojo.byId('chat');
            var slide=dojo.byId('slide');
            var messageLog=dojo.byId('messageLog');
            var from=message.data.user;
            var text=message.data.text;
            var slideUrl=message.data.slide;
            from+=":";
            // display the next image, or chat text
            if(slideUrl){
                slide.innerHTML ="<img src='" + slideUrl + "' style='width: 540px; height: 380px;' />";
            }else{
                messageLog.innerHTML += "<span class=\"from\">"+from+"&nbsp;</span><span class=\"text\">"+text+"</span><br/>";
            }
            chat.scrollTop = chat.scrollHeight - chat.clientHeight;
        },
        //replace to make request to the slides web service
        loadSlides: function (){
            room.slideUrls=[
            "/slideshow/images/image0.jpg",
            "/slideshow/images/image1.jpg",
            "/slideshow/images/image2.jpg",
            "/slideshow/images/image3.jpg",
            "/slideshow/images/image4.jpg",
            "/slideshow/images/image5.jpg"];
        },
     
        init: function(){
            room.loadSlides();
            var i=room.slideUrls.length;

            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("nextB"),"onclick",function(){
                if (i>=room.slideUrls.length-1)
                    i=0;
                else
                    i++;
                room.next( room.slideUrls[i]);
            });
            dojo.connect(dojo.byId("previousB"),"onclick",function(){
                if (i<=0)
                    i=room.slideUrls.length-1 ;
                else
                    i--;           
                room.next( room.slideUrls[i]);
            }); 
            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 slideshow 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 exercise3 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.




Step 5: Run the Project 

  1. Right click the  slideshow 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/slideshow/).
  3. Open another browser, preferably with different profile or different brand of browser, and type http://localhost:8080/slideshow/ to access the application.
    Comet slideshow page, which allows the users to slideshow at the same time.

    Different Browsers Sharing Photos in Comet slideshow

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 slideshow application, a user can enter a username and join a slideshow 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.

Publishing the Next Slide for the Comet chat

When the user clicks the Next Slide button, a JavaScript function is called that publishes the URL for the next slide, as shown in Figure 4.



Figure 4: Viewing the Next Slide
 
Code Sample from:  index.html

<input id="previousB" class="button" type="submit" name="previous" value="Previous Slide"/>
<input id="nextB" class="button" type="submit" name="next" value="Next Slide"/><br>
 

The JavaScript function that is called when the user clicks the Next Slide button is shown in the following code sample. This function calls room.next, which passes the URL for the next slide. The function then increments the index for the next slide. The URLs for the slides are stored in the slideUrls array, which is loaded at initialization in the room.loadSlides() function.

Code Sample from:  chat.js
var room = {
..
dojo.connect(dojo.byId("nextB"),"onclick",function(){
if (i>=room.slideUrls.length)
i=0;
else
i++;
room.next( room.slideUrls[i]);
});
 

The function room.next, shown in the following code sample, calls dojox.cometd.publish to publish the next slide URL (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",
...
next: function(text){
dojox.cometd.publish(room.channel, {
slide: 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 slide dom element innerHTML to an HTML img tag with the slide URL from the published message "<img src='" + slideUrl + "'/>". This message updates the browser page with the image that corresponds to the slide URL that was published.

Code Sample from:  chat.js

chatCallback: function(message){
if(!message.data){
alert("bad message format "+message);
return;
}
var chat=dojo.byId('chat');
var slide=dojo.byId('slide');
var messageLog=dojo.byId('messageLog');
var from=message.data.user;
var text=message.data.text;
var slideUrl=message.data.slide;
from+=":";
// display the next image, or chat text
if(slideUrl){
slide.innerHTML ="<img src='" + slideUrl + "' style='width: 540px; height: 380px;' />";
}else{
messageLog.innerHTML += "<span class=\"from\">"+from+"&nbsp;</span><span class=\"text\">"+text+"</span><br/>";
}
chat.scrollTop = chat.scrollHeight - chat.clientHeight;
},


   

Loading the Slide URLs

The dojo.addOnLoad function enables you to call a function after a page has loaded and after Dojo has finished its initialization. This application uses dojo.addOnLoad to call the init function. The init function calls the loadSlides function, which initializes the slideUrls object, as shown in the following code sample.

Code Sample from:  chat.js
 
var room = {

slideUrls: null,
...

loadSlides: function (){
room.slideUrls=[
"/slideshow/images/image0.jpg",
"/slideshow/images/image1.jpg",
"/slideshow/images/image2.jpg",
"/slideshow/images/image3.jpg",
"/slideshow/images/image4.jpg",
"/slideshow/images/image5.jpg"];
},

init: function(){
room.loadSlides();
...
}
...
};

dojo.addOnLoad(room, "init");

 




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