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
- 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).)

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.
-
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: <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: <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>
|
-
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;
}
|
-
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+" </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:
-
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 .

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>
|
Every request sent to your war file's context-path/cometd/
will be serviced by the Grizzly Bayeux runtime.
-
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
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+" </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