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

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

Introduction | Exercise 0 | Exercise 1 | Exercise 2 | Exercise 3 | Exercise 4 | Summary
5. Introduction Background Information Steps to Follow Summary ## 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 If NetBeans is not already running, start it. Choose File > New Project (Ctrl-Shift-N) from the main menu. Under Categories, select Java Web. Under Projects, select Web Application and click Next. Type `slideshow` in the Project Name field. Note that the Context Path becomes `/slideshow`. Specify the Project Location to the exercise2 sub directory of this lab on your computer. 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. 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. 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. You should now see the js folder in your slideshow project as below: 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: In the Projects window right click the `slideshow` node (from exercise 1), and select New Other.... In the New File window select Web HTML and click Next In the New HTML File Window, enter the HTML file name index, and click Finish. Double click on the index.html file to open it in the editor. 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>
10. In the Projects window right click the `slideshow` node (from exercise 1), and select New Other.... In the New File window select Web Cascading Style Sheet and click Next In the New CSS File Window, enter the CSS file name chat, and click Finish. Double click on the chat.css file to open it in the editor. 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; }
15. In the Projects window right click the `slideshow` node (from exercise 1), and select New Other.... In the New File window select Web JavaScriptFile and click Next In the NewJavaScript File Window, enter the file name chat, select the slideshow web Folder, and click Finish. Double click on the chat.js file to open it in the editor. 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");
1. Edit the web.xml file to change the welcome page: double click on the web.xml file in the slideshow WEB-INF directory 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. 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>`
3. Every request sent to your `war` file's `context-path/cometd/` will be serviced by the Grizzly Bayeux runtime. Step 5: Run the Project Right click the slideshow node in the Projects window, and select Run. When you run the project, your browser should display the opening page of the dojo comet Sample Application (at http://localhost:8080/slideshow/). 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: The Comet chat code used in the example application was adapted from an example originally written by Greg Wilkins. Cometd framework and its Bayeux protocol support in Grizzly – Jean-Francois Arcand's blog Using the Grizzly Comet API – Sun document on developing web applications Dojo Toolkit – Dojo home page dojo.cometd – Cometd.org page at the Dojo Foundation Grizzly – Project Grizzly home page GlassFish Application Server – home page for the GlassFish Community The Aquarium – news from the GlassFish Community Enterprise Comet: Awaken the Grizzly! – article Asynchronous HTTP and Comet Architectures – article A Comparison of Push vs Pull Ajax – article Comet and Reverse Ajax: The Next Generation Ajax 2.0 – book Back to top Next exercise