Jim Driscoll's Blog

Notes on Technology and the Web

Comet based TicTacToe in Atmosphere

leave a comment »

About a year ago, I gave a talk at JavaOne (and blogged about) writing a Comet powered TicTacToe (naughts and crosses to you Anglophones) game using the Grizzly Comet APIs.

In preparation for writing a Comet app with JSF 2, I thought I’d revisit that application and update it to use the newest Atmosphere APIs.

Atmosphere is a multiplatform Comet framework that allows you to write once, run anywhere on a number of Java based web containers. Sure, you could do the same thing using Servlet 3.0, but working on older servers like Tomcat 6 was something I wanted to support, so I chose that instead.

Since this program doesn’t really do anything that I haven’t already covered in previous blogs, except for the atmosphere calls, let me go over them quickly, and then just attach the program for you to look at.

Also – a quick note: This program works fine on Tomcat 6, but there seems to be a bug when running it on the latest build of Glassfish v3. I haven’t tested it on any other platforms.

The entirety of the Atmosphere API calls are held in the TTTHandler class, just as I’d previously put the Grizzly calls there. This class is responsible for doing two things: setting up a response when a GET request is received, and setting up the response when a POST comes in as well. The GET call is made by a hidden iframe, just as in my previous Comet examples from a year ago. That GET remains unsatisfied until a POST comes from an Ajax request triggered by a move. Then, the responses are sent to all GET requests. Let’s look at the code:

  34        public AtmosphereEvent onEvent(
  35                AtmosphereEvent<HttpServletRequest, HttpServletResponse> event) throws IOException {
  37            HttpServletRequest req = event.getRequest();
  38            HttpServletResponse res = event.getResponse();
  40            res.setContentType("text/html");
  41            res.addHeader("Cache-Control", "private");
  42            res.addHeader("Pragma", "no-cache");
  43            if (req.getMethod().equalsIgnoreCase("GET")) {
  44                // Junk for IE and Safari to chew on
  45                res.getWriter().write("<!-- Comet is a programming technique that enables web " +
  46                        "servers to send data to the client without having any need " +
  47                        "for the client to request it. -->\n");
  48                res.getWriter().flush();
  49                event.suspend();
  50            }

This first code snippet is the onEvent method of the handler. In this first part, we receive the event method call on any request. After a bit of setup, we check if it's a GET request. When it is, we send a bit of data back, and then call suspend on line 49. That pauses the response before it closes. Now, let's see what happens on a POST:

  51            if (req.getMethod().equalsIgnoreCase("POST")) {
  53                // There are better ways to do this, but it's the simplest way to
  54                // ensure that there is data consistency
  55                synchronized (game) {
  56                    int cell = -1;
  57                    String cellStr = req.getParameter("cell");
  58                    PrintWriter writer = res.getWriter();
  59                    writer.println("cell is '" + cellStr + "'");
  60                    if (cellStr == null) {
  61                        writer.println("error - cell not set");
  62                        return event;
  63                    }
  64                    try {
  65                        cell = Integer.parseInt(cellStr);
  66                    } catch (NumberFormatException nfe) {
  67                        writer.println("error - cellStr not an int: " + cellStr);
  68                        return event;
  69                    }
  70                    if (!game.turn(cell)) {
  71                        writer.println("warning - invalid move");
  72                    }
  73                    writer.println(game.getJSON());

When we receive a POST, we do the basic game logic, primarily using a different class that we've used for that. We return the move to the POST request - though that's primarily for debugging.

  75                    Broadcaster bc = event.getBroadcaster();
  77                    String response = game.getJSON();
  79                    // broadcast the updated game state
  80                    bc.broadcast(response);
  82                    writer.flush();

Next, we get a Broadcaster instance, and use it to send the game information to all the waiting GET requests that have set suspend, on line 49.

  98        public AtmosphereEvent onMessage(
  99                AtmosphereEvent<HttpServletRequest, HttpServletResponse> event) throws IOException {
 102            // Client closed the connection.
 103            if (event.isCancelled()) {
 104                return event;
 105            }
 107            String response = (String) event.getMessage();
 108            response = "<script type='text/javascript'>parent.chImg(" + response + ")</script>\n";
 109            PrintWriter writer = event.getResponse().getWriter();
 110            writer.write(response);
 111            writer.flush();
 113            if (!event.isResumedOnTimeout()) {
 114                event.resume();
 115            }
 117            return event;
 118        }

This is the part were we're actually doing the heavy lifting of Comet. When a Broadcaster sends a broadcast, the onMessage method is called, with the message sent passed to it.

What happens after that is simple - we retrieve the message, and use it to write out a response to the client, which, if you recall, is patiently waiting to place that text into the iframe, where it will be evaluated by the browser. Lastly, we resume the connection on line 114. This closes and flushes the connection - meaning that the client will have to make a new GET request.

Hope that covers the basic differences to the Comet TicTacToe for the Atmosphere API. You can download the full code here, as a Netbeans project. Feel free to ask any questions below.

(This article originally published on my java.net blog on July 18, 2009.)


Written by jamesgdriscoll

February 9, 2010 at 9:26 PM

Posted in comet, Java

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: