Chatroom Application using DWR Java Framework

Exclusive offer: get 50% off this eBook here
DWR Java AJAX Applications

DWR Java AJAX Applications — Save 50%

A step-by-step example-packed guide to learning professional application development with Direct Web Remoting

$23.99    $12.00
by Sami Salkosuo | October 2008 | AJAX Java Open Source Web Development

DWR (Direct Web Remoting), is an Open Source Java framework, licensed under commercial-friendly Apache Software License v2 for building AJAX applications. DWR's main idea is to hide AJAX implementation details such as XMLHttpRequest from developers. Developers can concentrate on developing the application and business objects and leave AJAX details behind the scenes where they belong.

In this article by Sami Salkosuo, we discuss a Chatroom application which demonstrates the use of DWR. The Chatroom sample application is a very typical multi-user chatroom. The functionalities of this sample include a list of online users, automatic refresh of chat text, and the ability to send messages to the chat room.

Starting the Project and Configuration

We start by creating a new project for our chat room, with the project name DWRChatRoom. We also need to add the dwr.jar file to the lib directory and enable DWR in the web.xml file. The following is the source code of the dwr.xml file.

<?xml ver sion="1.0" encoding="UTF-8"?>
<!DOCTYPE dwr PUBLIC
"-//GetAhead Limited//DTD Direct Web Remoting 2.0//EN"
"http://getahead.org/dwr/dwr20.dtd">
<dwr>
<allow>
<create creator="new" javascript="Login">
<param name="class" value="chatroom.Login" />
</create>
<create creator="new" javascript="ChatRoomDatabase">
<param name="class" value="chatroom.ChatRoomDatabase" />
</create>
</allow>
</dwr>

The source code for web.xml is as follows:

<?xml ver sion="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.
com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.
sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_
5.xsd" id="WebApp_ID" version="2.5">
<display-name>DWRChatRoom</display-name>
<servlet>
<display-name>DWR Servlet</display-name>
<servlet-name>dwr-invoker</servlet-name>
<servlet-class>
org.directwebremoting.servlet.DwrServlet
</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>activeReverseAjaxEnabled</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dwr-invoker</servlet-name>
<url-pattern>/dwr/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
</web-app>

Developing the User Interface

The next step we do is to create files for presentation: style sheet and HTML/JSP files. The style sheet, loginFailed.html, and index.jsp files are required for the application. The source code of the style sheet is as follows:

body{
margin:0;
padding:0;
line-height: 1.5em;
}

b{font-size: 110%;}
em{color: red;}

#topsection{
background: #EAEAEA;
height: 90px; /*Height of top section*/
}

#topsection h1{
margin: 0;
padding-top: 15px;
}

#contentwrapper{
float: left;
width: 100%;
}

#contentcolumn{
margin-left: 200px; /*Set left margin to LeftColumnWidth*/
}

#leftcolumn{
float: left;
width: 200px; /*Width of left column*/
margin-left: -100%;
background: #C8FC98;
}

#footer{
clear: left;
width: 100%;
background: black;
color: #FFF;
text-align: center;
padding: 4px 0;
}

#footer a{
color: #FFFF80;
}

.innertube{
margin: 10px; /*Margins for inner DIV inside each column (to provide padding)*/
margin-top: 0;
}

Our first page is the login page. It is located in the WebContent directory and it is named index.jsp. The source code for the page is given as follows:

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Book Authoring</title>

<script type='text/javascript' src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original='/DWRChatroom/dwr/interface/Login.js'></script>
<script type='text/javascript' src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original='/DWRChatroom/dwr/engine.js'></script>
<script type='text/javascript' src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original='/DWRChatroom/dwr/util.js'></script>  

<script type="text/javascript">

function login()
{
  var userNameInput=dwr.util.byId('userName');
  var userName=userNameInput.value;
  Login.doLogin(userName,loginResult);
}

function loginResult(newPage)
{
  window.location.href=newPage;
}

</script>
</head>
<body>
<h1>Book Authoring Sample</h1>
<table cellpadding="0" cellspacing="0">
<tr>
<td>User name:</td>
<td><input id="userName" type="text" size="30"></td>
</tr>
<tr>
<td>&nbsp;</td>
<td><input type="button" value="Login" onclick="login();return false;"></td>
</tr></table>
</body>
</html>

The login screen uses the DWR functionality to process the user login (the Java classes are presented after the web pages). The loginResults function opens either the failure page or the main page based on the result of the login operation.

If the login was unsuccessful, a very simple loginFailed.html page is shown to the user, the source code for which is as follows:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;
                                         charset=ISO-8859-1">
<title>Login failed</title>
</head>
<body>
<h2>Login failed.</h2>
</body>
</html>

The main page, mainpage.jsp, includes all the client-side logic of our ChatRoom application. The source code for the page is as follows:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Chatroom</title>
<link href="styles.css" rel="stylesheet" type="text/css" />
<%
   if (session.getAttribute("username") == null
         || session.getAttribute("username").equals("")) {
      //if not logged in and trying to access this page
      //do nothing, browser shows empty page
      return;
   }
%>
<script type='text/javascript' src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original='/DWRChatRoom/dwr/interface/Login.js'></script>
<script type='text/javascript' src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original='/DWRChatRoom/dwr/interface/ChatRoomDatabase.js'></script>
<script type='text/javascript' src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original='/DWRChatRoom/dwr/engine.js'></script>
<script type='text/javascript' src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original='/DWRChatRoom/dwr/util.js'></script>  

<script type="text/javascript">
dwr.engine.setActiveReverseAjax(true);

function logout()
{
  Login.doLogout(showLoginScreen);
}

function showLoginScreen()
{
  window.location.href='index.jsp';
}

function showUsersOnline()
{
  var cellFuncs = [
          function(user) {
 
            return '<i>'+user+'</i>';
          }
          ];
    Login.getUsersOnline({
    callback:function(users)
    {
      dwr.util.removeAllRows('usersOnline');
      dwr.util.addRows( "usersOnline",users, cellFuncs,
                                    { escapeHtml:false });
    }
    });
}

function getPreviousMessages()
{
    ChatRoomDatabase.getChatContent({
    callback:function(messages)
    {
      var chatArea=dwr.util.byId('chatArea');
      var html="";
      for(index in messages)
      {
         var msg=messages[index];
         html+=msg;
      }
      chatArea.innerHTML=html;
      var chatAreaHeight = chatArea.scrollHeight;
      chatArea.scrollTop = chatAreaHeight;
    }
    });

}

function newMessage(message)
{
  var chatArea=dwr.util.byId('chatArea');
  var oldMessages=chatArea.innerHTML;
  chatArea.innerHTML=oldMessages+message;  
  var chatAreaHeight = chatArea.scrollHeight;
  chatArea.scrollTop = chatAreaHeight;
}

function sendMessageIfEnter(event)
{
  if(event.keyCode == 13)
  {
    sendMessage();
  }
}

function sendMessage()
{
    var message=dwr.util.byId('messageText');
    var messageText=message.value;
    ChatRoomDatabase.postMessage(messageText);
    message.value='';
}
</script>
</head>
<body onload="showUsersOnline();">
<div id="maincontainer">

<div id="topsection">
<div class="innertube">
<h1>Chatroom</h1>
<h4>Welcome <i><%=(String) session.getAttribute("username")%></i></h4>
</div>
</div>

<div id="contentwrapper">
<div id="contentcolumn">
<div id="chatArea" style="width: 600px; height: 300px; overflow: auto">
</div>
<div id="inputArea">
<h4>Send message</h4>
<input id="messageText" type="text" size="50"
  onkeyup="sendMessageIfEnter(event);"><input type="button" value="Send msg"
                                                      onclick="sendMessage();">
</div>
</div>
</div>

<div id="leftcolumn">
<div class="innertube">

<table cellpadding="0" cellspacing="0">
  <thead>
    <tr>
      <td><b>Users online</b></td>
    </tr>
  </thead>
  <tbody id="usersOnline">
  </tbody>
</table>

<input id="logoutButton" type="button" value="Logout"
  onclick="logout();return false;"></div>

</div>

<div id="footer">Stylesheet by <a
  href="http://www.dynamicdrive.com/style/">Dynamic Drive CSS
Library</a></div>
</div>
<script type="text/javascript">
getPreviousMessages();
</script>

</body>
</html>

The first chat-room-specific JavaScript function is getPreviousMessages(). This function is called at the end of mainpage.jsp, and it retrieves previous chat messages for this chat room.

The newMessage() function is called by the server-side Java code when a new message is posted to the chat room. The function also scrolls the chat area automatically to show the latest message.

The sendMessageIfEnter() and sendMessage() functions are used to send user messages to the server. There is the input field for the message text in the HTML code, and the sendMessageIfEnter() function listens to onkeyup events in the input field. If the user presses enter, the sendMessage() function is called to send the message to the server.

The HTML code includes the chat area of specified size and with automatic scrolling.

Developing the Java Code

There are several Java classes in the application.

The Login class handles the user login and logout and also keeps track of the logged-in users. The source code of the Login class is as follows:

package chatroom;

import java.util.Collection;
import java.util.List;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.directwebremoting.ScriptSession;
import org.directwebremoting.ServerContext;
import org.directwebremoting.ServerContextFactory;
import org.directwebremoting.WebContext;
import org.directwebremoting.WebContextFactory;
import org.directwebremoting.proxy.ScriptProxy;

public class Login {

   public Login() {
   }
   
   public String doLogin(String userName) {
      UserDatabase userDb=UserDatabase.getInstance();
      if(!userDb.isUserLogged(userName)) {
         userDb.login(userName);
         WebContext webContext= WebContextFactory.get();
         HttpServletRequest request = webContext.getHttpServletRequest();
         HttpSession session=request.getSession();
         session.setAttribute("username", userName);
         String scriptId = webContext.getScriptSession().getId();
         session.setAttribute("scriptSessionId", scriptId);
         updateUsersOnline();
         return "mainpage.jsp";
      }
      else {
         return "loginFailed.html";
      }
   }
   
   public void doLogout() {
      try {
         WebContext ctx = WebContextFactory.get();
         HttpServletRequest request = ctx.getHttpServletRequest();
         HttpSession session = request.getSession();
         Util util = new Util();
         String userName = util.getCurrentUserName(session);
         UserDatabase.getInstance().logout(userName);
         session.removeAttribute("username");
         session.removeAttribute("scriptSessionId");
         session.invalidate();
      } catch (Exception e) {
         System.out.println(e.toString());
      }
      updateUsersOnline();
   }
   
   private void updateUsersOnline() {
      WebContext webContext= WebContextFactory.get();
      ServletContext servletContext = webContext.getServletContext();
      ServerContext serverContext = ServerContextFactory.get(servletContext);
      webContext.getScriptSessionsByPage("");
      String contextPath = servletContext.getContextPath();
      if (contextPath != null) {
         Collection<ScriptSession> sessions =
                            serverContext.getScriptSessionsByPage
                                            (contextPath + "/mainpage.jsp");
         ScriptProxy proxy = new ScriptProxy(sessions);
         proxy.addFunctionCall("showUsersOnline");
      }
   }
   
   public List<String> getUsersOnline() {
      UserDatabase userDb=UserDatabase.getInstance();
      return userDb.getLoggedInUsers();
   }
}

The following is the source code of the UserDatabase class

package chatroom;

import java.util.List;
import java.util.Vector;

//this class holds currently logged in users
//there is no persistence
public class UserDatabase {
   
   private static UserDatabase userDatabase=new UserDatabase();
   
   private List<String> loggedInUsers=new Vector<String>();
   
   private UserDatabase() {
}
   
   public static UserDatabase getInstance() {
      return userDatabase;
   }
   
   public List<String> getLoggedInUsers() {
      return loggedInUsers;
   }
   
   public boolean isUserLogged(String userName) {
      return loggedInUsers.contains(userName);
   }
   
   public void login(String userName) {
      loggedInUsers.add(userName);
   }
   
   public void logout(String userName) {
      loggedInUsers.remove(userName);
   }
}

The Util class is used by the Login class, and it provides helper methods for the sample application. The source code for the Util class is as follows:

package chatroom;

import java.util.Hashtable;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.directwebremoting.WebContext;
import org.directwebremoting.WebContextFactory;

public class Util {
   
   public Util() {
      
   }
   
   public String getCurrentUserName() {
      //get user name from session
      WebContext ctx = WebContextFactory.get();
      HttpServletRequest request = ctx.getHttpServletRequest();
      HttpSession session=request.getSession();
      return getCurrentUserName(session);
   }

   public String getCurrentUserName(HttpSession session) {
      String userName=(String)session.getAttribute("username");
      return userName;
   }
}

The logic for the server-side chat room functionality is in the ChatRoomDatabase class. The source code for the ChatRoomDatabase is as follows:

package chatroom;

import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Vector;

import javax.servlet.ServletContext;

import org.directwebremoting.ScriptSession;
import org.directwebremoting.ServerContext;
import org.directwebremoting.ServerContextFactory;
import org.directwebremoting.WebContext;
import org.directwebremoting.WebContextFactory;
import org.directwebremoting.proxy.ScriptProxy;

public class ChatRoomDatabase {

   private static List<String> chatContent = new Vector<String>();

   public ChatRoomDatabase() {

   }

   public void postMessage(String message) {
      String user = (new Util()).getCurrentUserName();
      if (user != null) {
         Date time = new Date();
         StringBuffer sb = new StringBuffer();
         sb.append(time.toString());
         sb.append(" <b><i>");
         sb.append(user);
         sb.append("</i></b>:  ");
         sb.append(message);
         sb.append("<br/>");
         String newMessage=sb.toString();
         chatContent.add(newMessage);
         postNewMessage(newMessage);
      }
   }

   public List<String> getChatContent() {
      return chatContent;
   }

   private ScriptProxy getScriptProxyForSessions() {
      WebContext webContext = WebContextFactory.get();
      ServletContext servletContext = webContext.getServletContext();
      ServerContext serverContext = ServerContextFactory.get(servletContext);
      webContext.getScriptSessionsByPage("");
      String contextPath = servletContext.getContextPath();
      if (contextPath != null) {
         Collection<ScriptSession> sessions = serverContext
               .getScriptSessionsByPage(contextPath + "/mainpage.jsp");
         ScriptProxy proxy = new ScriptProxy(sessions);
         return proxy;
      }
      return null;
   }

   public void postNewMessage(String newMessage) {
      ScriptProxy proxy = getScriptProxyForSessions();
      if (proxy != null) {
         proxy.addFunctionCall("newMessage",newMessage);
      }
   }
}

The Chatroom code is surprisingly simple. The chat content is stored in a Vector of Strings. The getChatContent()method just returns the chat content Vector to the browser.

The postMessage()method is called when the user sends a new chat message. The method verifies whether the user is logged in, and adds the current time and username to the chat message and then appends the message to the chat content.

The method also calls the postNewMessage() method that is used to show new chat content to all logged-in users. Note that the postMessage() method does not return any value. We let DWR and reverse AJAX functionality show the chat message to all users, including the user who sent the message.

The getScriptProxyForSessions() and postNewMessage() methods use reverse AJAX to update the chat areas of all logged-in users with the new message.

And that is it! The chat room sample is very straightforward and basic functionality is already in place, and the application is ready for further development.

Testing the Chat

We test the chat room application with three users: Smith, Brown, and Jones. We have given some screenshots of a typical scenario in a chat room here.

Both Smith and Brown log into the system and exchange some messages. Both users see empty chat rooms when they log in and start chatting.

Chatroom Application using DWR Java Framework

The empty area that is above the send message input field is reserved for chat content. Smith and Brown exchange some messages as is seen in the following screenshot:

Chatroom Application using DWR Java Framework

The third user, Jones, joins the chat and sees all the previous messages in the chat room. Jones then exchanges some messages with Smith and Brown. Smith and Brown log out from the system leaving Jones alone in the chat room (until she also logs out). This is visible in the following screenshot:

Chatroom Application using DWR Java Framework

Summary

This sample application showed how to use DWR in a chat room application. This application makes it clear that DWR makes development of these kind of collaborative applications very easy.

DWR itself does not even play a big part in the applications. DWR is just a transparent feature of the application. So developers can concentrate on the actual project and aspects such as persistence of data and a neat user interface, instead of the low-level details of AJAX.

 

 

DWR Java AJAX Applications A step-by-step example-packed guide to learning professional application development with Direct Web Remoting
Published: October 2008
eBook Price: $23.99
Book Price: $39.99
See more
Select your format and quantity:

About the Author :


Sami Salkosuo

Sami Salkosuo is a Software IT Architect at IBM Software Group in Finland. He has over ten years of experience in Java, web, and integration technologies.

Sami has written several articles for IBM developerWorks and he is also a coauthor of an IBM Redbook: Portalizing Domino Applications.

Books From Packt

DWR Java AJAX Applications
DWR Java AJAX Applications

EJB 3 Developer Guide
EJB 3 Developer Guide

Apache JMeter
Apache JMeter

Swing Extreme Testing
Swing Extreme Testing

Object-Oriented JavaScript
Object-Oriented JavaScript

ZK Developer’s Guide
ZK Developer’s Guide

OpenCms 7 Development
OpenCms 7 Development

Service Oriented Architecture with Java
Service Oriented Architecture with Java


 

No votes yet

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
t
n
e
n
f
Z
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software