How-to: Pure-Tomcat Comet Chat

Last semester I built a web-based research game for my internship that involves live multi-player interaction, runs on Tomcat, and lives in an eApps VM. It also uses Comet instead of traditional polling.

You can visit an in-development build of the game (v2 is not quite complete) here and see the the Java source here.

Now I don’t recommend the Tomcat framework for building a Comet web app. The dispatcher.forward() method that handles JSP responses is too slow (1s!) to be practical. On the other hand, if you have no need to serve JSP pages via servlets, you can get good performance.

I am assuming you have Tomcat set up, and know your way around JSP/Servlets somewhat. Then we are ready to put together Comet chat.

First you need to switch your Tomcat instance over to the proper connector. See this tutorial from IBM, under Comet.

Next, you want to look at the details of the Tomcat Advanced IO spec. The example chat servlet code they give can be compiled with only a couple of tweaks.

What the servlet does is register a response when a client first connects, and then holds on to it. When it reads a message from a client, it adds the message to a list of unsent messages and notifies a separate, designated thread that then sends the messages to each long-polled response (i.e., each connected client).

In my version (ChatServlet.java) I hacked out the HTML responses on BEGIN and END, as we want to keep markup out of the servlet. I also modified the MessageSender class to close the PrintWriter after sending a message. This is necessary to employ the sort of daisy-chaining of Comet requests that is compatible with all modern browsers, and even some obsolete ones, including IE6. (tangent: if you know how to tell Chrome “It’s ok, we’re using Comet” so it doesn’t give the hourglass, please share!)

Now, for the client side, we will use the standard Ajax XMLHttpRequest. This is straightforward to do. Just remember that upon loading you want to send an empty message to the server to connect and receive back messages sent by other chatters. Furthermore, for every response you receive, you need to re-connect by sending a new empty message (daisy-chaining).

This is my client: (chat.jsp).

You will have to add the servlet to your deployment descriptor, of course, before it will run. My code assumes you locate your servlet at relative path /Chat.

Here’s the whole Eclipse project: (CometServletChat.tar.gz).

You can play with my version here.

This implementation is fast and scalable, but doesn’t guarantee that any messages will actually reach any clients. To fix this, you could modify the servlet to retain all chat messages and include them in each response. Then clients could poll at reasonable intervals between messages received to check if there was a missed message.

An improvement that is easily made is to have the client keep a single request open for the entire session and use a second request to send. Don’t forget to change send() to call the callback function when request.readyState >= 3 and remove the daisy-chaining send() call in receive(). And in the servlet, remove the line writer.close() from MessageSender.run(). This results in fewer chat messages getting lost, but only works in Firefox. See this guide for streaming client solutions that work in other browsers.

Tags: , ,

Please leave a comment

  1. Giuseppe Says:

    Hello, can u help me to understand why my chat doesn’t work please?

  2. Peter Says:

    Nice explanation, thanks.

  3. sdadad Says:

    your chat example doesn’t work

  4. Angus Says:

    I was getting UnsupportedOperation exception on event.setTimeout. To make run used try/catch then it works ok. There are other problems but for a simple sample it is really useful.

  5. Given Says:

    Thanks a lot. It works beautifully 🙂

  6. Anteneh Says:

    After half day googling… this one saved my life…. Thanks

  7. seb Says:

    It doesn’t work for me on eclipse perhaps the AJAX is thre problem to send data to client i m not sure

Leave a Comment