Sign in using Google or Yahoo! id.  

Comet, HTML5 & Java

 Comments Share:   Twitter   Reddit   HackerNews   Facebook 

Comet is an umbrella terminology which covers various HTTP-push technologies. There are other names to this technology like Reverse Ajax and HTTP Server Push.

HTTP and HTTP-Push

The HTTP protocol is straightforward: a request from the client, and the server responds to that request and closes connection. This model does not give much space for developing connection-oriented-push facility.

History

The initial technology to overcome this limitation of the HTTP-protocol were made using Java Applets. Java Applets were able to open vanilla TCP-socket connections back to the server which were used to push information to the client whenever they were available.

When using Java Applets was not an option, developers usually reverted to polling. They were resource intensive repeat calls from the client to the web/application server whether or not it had new data to serve.

Comet

Comet applications can be broadly classified as:

  1. Streaming: The connection to the web/application server is kept open for a long time, the server pushes chunks of data as and when they become available.
  2. Long polling: This is similar to polling, but with a difference: the server holds the connection until it has data to push. Once the push is done, the client usually opens a new polling request.

Streaming Technologies

There has been no direct way of implementing streaming push in HTTP (since very recent times). As said earlier, historically people have been using Java Applets and some hacks including hidden IFrame. There have been some proprietary technologies by Netscape (now Mozilla) which were never accepted by other browsers.

One of standard in the upcoming HTML5 specification is Web Sockets. Web Sockets specification is bringing in a standard way to stream data from the server. The browsers supporting Web Socket define an implementation of this class:

[Constructor(in DOMString url)]
interface WebSocket {
  readonly attribute DOMString URL;
  // ready state
  const unsigned short CONNECTING = 0;
  const unsigned short OPEN = 1;
  const unsigned short CLOSED = 2;
  readonly attribute int readyState;
  // networking
  attribute EventListener onopen;
  attribute EventListener onmessage;
  attribute EventListener onclosed;
  void send(in DOMString data);
  void disconnect();
};

This API is surprisingly simple. To open a new connection:

var ws = new WebSocket("ws://www.websocket.org");

You may add various listeners:

ws.onopen = function(evt) { alert("Connection open ..."); };
ws.onmessage = function(evt) { alert( "Received Message:  "  +  evt.data); };
ws.onclose = function(evt) { alert("Connection closed."); };

Sending data is also equally easy:

ws.send("Hello Web Socket! Goodbye Comet!");
ws.disconnect();

Code modified from: http://www.kaazing.org/confluence/display/KAAZING/What+is+an+HTML+5+WebSocket

You will see that this specification defines a new protocol handle ws://. This means a Web Socket URL. There is also another protocol handle wss:// for secure connection which is defined.

Web Socket Internals

What HTTP communication happens internally:

Request:

GET /test HTTP/1.1
Upgrade: WebSocket
Connection: Upgrade
Origin: http://localhost/test
Host: localhost
Content-Length: 0

Response:

HTTP/1.1 101 Web Socket Protocol Handshake
Upgrade: WebSocket
Connection: Upgrade
Server: Resin/4.0.2
WebSocket-Location: ws://localhost/websocket
WebSocket-Origin: http://localhost/test
Content-Length: 0
Date: Fri, 08 May 2009 09:51:31 GMT

Example from: http://www.caucho.com/resin/examples/websocket-java/index.xtp

Backend Java Code for handling Web Socket

Unfortunately, we do not have a standard API in Java for handling Web Socket connections. Various application-server vendors have devised their own APIs for this.

Long Poll

The other approach of Comet is the Long Poll. Typical long-poll JavaScript code looks like:

function myPoll(){
    var url = "http://localhost:8080/comet/data.json"
    var request =  new XMLHttpRequest();
    request.open("GET", url, true);
    request.setRequestHeader("Content-Type","application/x-javascript;");
    request.onreadystatechange = function() {
        if (request.readyState == 4) {
        if (request.status == 200){
            if (request.responseText) {
            document.getElementById("someid").innerHTML = 
                    request.responseText;
            }
        }
        myPoll(); // Call same function after getting data!
        }
    };
    request.send(null);
}

In the backend, if you are using Servlets 3.0, it is pretty easy:

package org.wiztools.servlets30async;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 *
 * @author subhash
 */
@WebServlet(urlPatterns="/data.json", asyncSupported=true)
public class DataGeneratorServlet extends HttpServlet {

    private static final Logger LOG = Logger.getLogger(DataGeneratorServlet.class.getName());

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        final AsyncContext ctx = req.startAsync();
        ctx.start(new Runnable() {
            @Override
            public void run() {
                try {
                    ServletResponse resp = ctx.getResponse();
                    
                    resp.setContentType("application/json");
                    resp.setCharacterEncoding("UTF-8");

                    PrintWriter out = resp.getWriter();

                    // Wait till the data is available
                    // then write it to `out'

                    ctx.complete();
                }
                catch(IOException ex) {
                    LOG.log(Level.SEVERE, null, ex);
                }
            }
        });
    }
    
}
Posted on August 03, 2010 12:14 PM by Subhash Chandran
servlets comet ajax html5 java
blog comments powered by Disqus