niceideas.ch
Technological Thoughts by Jerome Kehrli

Comet: having fun with the Java HTTP Stack

by Jerome Kehrli


Posted on Tuesday Nov 01, 2016 at 11:54PM in Web Devevelopment


Wikipedia's definition does a pretty great job in introducing comet:

Comet is a web application model in which a long-held HTTP request allows a web server to push data to a browser, without the browser explicitly requesting it.

This says it all. For a long time, Comet - which is more an umbrella term regrouping several techniques - was pretty much the only way to get as close as possible to Server Push in Web application, plain HTML/Javascript applications on top of the HTTP protocol.
All methods have in common that they rely on browser-native technologies such as JavaScript, rather than on proprietary plug-ins.

History

In the early days of the World Wide Web, the browser would make multiple server requests: one for the page content and one for each page component. Examples of page components include images, CSS files, scripts, Java applets, and any other server-hosted resource referenced in the page.

Ajax - Asynchronous JavaScript and XML - went a long way towards making this model evolve. It allowed far greater control over page content by providing the ability to send server requests for as little, or as much data as the browser needed to update. In addition, its asynchronous nature supported multiple simultaneous calls - even while other elements downloaded.
One problem that Ajax did not adequately solve was the issue of data synchronization between the client and server. Since the browser would not know if something had changed on the server, Web applications typically polled the server on a periodic basis to ask if new information was available. The only possible way as to use Polling where the browser would poll the serve rat regular intervals to find out about new events and updated data.

To circumvent this very limitation, developers started to imagine techniques aimed at getting closer to server push, either using the Forever Hidden iframe technique or the Long Polling XMLHttpRequest technique. Both these techniques are grouped under the umbrella term Comet or Bayeux Protocol.
Now of course these techniques have respective advantages and drawbacks that I will be discussing later in this article.

Comet

For many reasons, mostly robustness and universality of the solution, I am favoring the Forever Hidden iframe approach and I have designed quite some time ago a Comet framework making use of this technique to provide Server-Push to Plain HTML/javascript web applications.

The forever hidden iframe technique is the one I found most seducing for one very good and essential reason : it's the most robust one from a technical perspective. It has drawbacks of course in comparison with other techniques, but I still deem it the most solid one, and in some situations it was even the only one I could make work.
Its principle is straightforward to understand: an iframe is opened on a server resource and the HTTP response stream is never closed by the backend. The iframe keeps loading more javascript instructions as the server pushes them in the response stream.
Having said that, I have to admit ... It blocks a freaking amount of threads in the java backend, it shows the annoying loading icon on the browser, managing errors is a nightmare ... but it always works, in every situation, and at the end of the day considering the kind of business critical applications I usually work on, this is what matters the most to me.

Now of course, WebSockets tend to rend all Comet tecnniques kind of legacy.
But still, I end up deploying comet techniques instead of WebSockets in many circumstances. You may wonder why ?

WebSockets are no magic silver bullet

  • Most importantly, when we have to support plain HTTP connection and bad HTTP proxies which let a WebSocket being opened but don't let anything pass through
  • Implementing WebSockets efficiently on the server side is more complicated than Comet and requires most of the time specific libraries, sometimes pretty incompatible with some Application Servers
  • With WebSockets you are forced to run TCP proxies as opposed to HTTP proxies for load balancing
  • When I have to support old version of Internet Explorer, such as IE9, in banking institutions that have a bad tendency to use pretty old version of software
  • Many other reasons I am detailing below ...

The first problem above is the darkest one regarding WebSockets in my opinion. This proxy server issue is quite widespread.
Nonetheless, "You must have HTTPS" is the weakest argument against WebSocket adoption since I want all the web to be HTTPS, it is the future and it is getting cheaper every day. But unfortunately there are some context where we have to integrate our web application on plain HTTP and one should know that weird stuff will definitely happen you deploy WebSockets over unsecured HTTP.

lightweight, simple and robust Comet framework

This is the main rational behind the Lightweight and simple Comet Framework for Java that I am introducing here. I am using this framework, that I've designed long ago and somewhat maintained over the years, each and every time I encounter issues with WebSockets.
To be honest, I always consider it somewhat a failure since the standardization of the WebSocket specification should prevent me from reverting to this Comet Framework so often, but surprisingly it doesn't and I end up returning there pretty often.
Again, the forever hidden iframe Comet technique always works!

Anyway, this Lightweight and simple Comet Framework for Java is pretty handy and I am presenting it here and making the sources available for download.

Summary

1. Server Push with HTTP

As stated above, even with the emergence of the WebSocket, and recently its specification, there is still room for a Comet framework based on the forever iframe techniques. We will be discussing this rational in length below but before it seems appropriate to remind some key aspects of HTTP.

1.1 Standard HTTP Model

The Hypertext Transfer Protocol (HTTP), an application protocol (OSI layer 7), is the foundation of data communication for the World Wide Web.
HTTP functions as a request-response protocol in the client-server computing model. A web browser, for example, may be the client and an application running on a computer hosting a website may be the server. The client submits an HTTP request message to the server. The server, which provides resources such as HTML files and other content, or performs other functions on behalf of the client, returns a response message to the client. The response contains completion status information about the request and may also contain requested content in its message body.

HTTP is an application layer protocol designed within the framework of the Internet protocol suite. Its definition presumes an underlying and reliable transport layer protocol, and Transmission Control Protocol (TCP) is commonly used.

HTTP Request and Response

An HTTP client initiates a request by establishing a Transmission Control Protocol (TCP) connection to a particular port on a server (typically port 80). An HTTP server listening on that port waits for a client's request message. Upon receiving the request, the server sends back a status line, such as "HTTP/1.1 200 OK", and a message of its own. The body of this message is typically the requested resource, although an error message or other information may also be returned.

HTTP Sequence Diagram

Persistent connections

In HTTP/0.9 and 1.0, the connection is closed after a single request/response pair. In HTTP/1.1 a keep-alive-mechanism was introduced, where a connection could be reused for more than one request. Such persistent connections reduce request latency perceptibly, because the client does not need to re-negotiate the TCP 3-Way-Handshake connection after the first request has been sent. Another positive side effect is that in general the connection becomes faster with time due to TCP's slow-start-mechanism.

Surprisingly, but importantly, event though HTTP/1.1 introduced this mechanism of persistent connections, there is no way a server backend could make use of the existing surviving TCP connection to push data to the browser, due to the fundamentally request/response design of the HTTP Protocol.
With HTTP, even HTTP/1.1 - at least from the HTTP perspective, independently of the underlying, persistent or not, TCP connection - the connection must be first established by the client to the server. There's no way of a server contacting a web client.
We'll discuss below what are the techniques to circumvent this limitation.

1.2 AJAX and RIA

AJAX

Ajax (Asynchronous JAvascript and XML) is a set of web development techniques using many web technologies on the client-side to create asynchronous Web applications. With Ajax, web applications can send data to and retrieve from a server asynchronously (in the background) without interfering with the display and behavior of the existing page.
Ajax enables web applications to change content dynamically without the need to reload the entire page.
In practice, modern implementations commonly substitute JSON for XML due to the advantages of being native to JavaScript

RIA

A Rich Internet Application (RIA) is a Web application designed to deliver the same features and functions normally associated with deskop applications. RIA application ar most of the time single-page application, i.e. full Javascript applications where the Javascript application engine is downloaded to the browser side with the main, single and only HTML Page of the application.
All the front-end features of the applications are then implemented as DOM manipulations to create and manipulate screens, panels, widgets, etc. by the javascript code.

One distinguishing feature of an RIA (in contrast to other Web-based applications) is the client engine that intermediates between the user and the application server. The client engine downloads when the RIA launches. The engine can be augmented during subsequent operation with additional downloads in which the engine acts as a browser extension to handle the user interface and server communications.

RIAs generally split the processing across the Internet/network divide by locating the user interface and related activity and capability on the client side, and the data manipulation and operation on the application server side. This approach allows the client system to handle local activities, calculations, reformatting and so forth, thereby lowering the amount and frequency of client-server traffic, especially as compared to the client-server implementations built around so-called thin clients.

AJAX just uses a combination of:

  • A browser built-in XMLHttpRequest object (to request data from a web server)
  • JavaScript and HTML DOM (to display or use the data)

RIA and Ajax form a breakthrough in the sense that finally Web application can behave as Desktop applications and provide the same kind of User Experience to users from within a Web browser.

RIA limitations

Rich Internet Applications using Ajax suffer however from a limitation: the (apparent) impossibility, due to the HTTP design mentioned above, to have asynchronous messages not only from the client application, but also from the backend server to the UI client application.
Again, everything the server returns to the client should be initiated by a previous client request, right ?

1.3 Comet : getting closer to Server-Push

Again, the web (well that's HTTP in the end, isn't it ?) was not designed to allow web servers to make connections to web browsers, so it can be tricky to get data to a browser in a timely manner

Polling and Pigyback

Polling is the most obvious solution to the problem. This is where the browser makes a request of the server at regular and frequent intervals, say every 3 seconds, to see if there has been an update to the page. It's like a 5 year old in the back of the car shouting 'are we there yet?' every few seconds.
Polling is simple to implement, however it can easily overload a server.

Piggyback is a variant of polling, really close to it in the end. With the piggyback option, the server, having an update to send, waits for the next time the browser makes a connection and then sends it's update along with the response that the browser was expecting.

Comet

Reverse Ajax with polling or piggyback is very limited: it does not scale and does not provide low-latency communication (when events arrive in the browser as soon as they arrive on the server). Comet is a web application model where a request is sent to the server and kept alive for a long time, until a time-out or a server event occurs. When the request is completed, another long-lived Ajax request is sent to wait for other server events. With Comet, web servers can send the data to the client without having to explicitly request it.

Comet is essentially a concept: being able to send data from the server to the client. In a standard HTTP Ajax request, data is sent to the server. Reverse Ajax can be simulated to issue an Ajax request, in specific ways that are covered in this article, so the server can send events to the client as quickly as possible (low-latency communication).

There are different techniques for Comet :

  • Multi-part XMLHttpRequest
  • Long-Polling XMLHttpRequest, a.k.a Reverse Ajax
  • Forevew iframe

Each of these techniques has advantages and drawbacks, more on that in the next section.

1.4 Websockets

I can only point out again that WebSockets are no magic silver bullet :

  • Most importantly, when we have to support plain HTTP connection and bad HTTP proxies which let a WebSocket being opened but don't let anything pass through
  • Implementing WebSockets efficiently on the server side is more complicated than Comet and requires most of the time specific libraries, sometimes pretty incompatible with some Application Servers
  • With WebSockets you are forced to run TCP proxies as opposed to HTTP proxies for load balancing
  • When I have to support old version of Internet Explorer, such as IE9, in banking institutions that have a bad tendency to use pretty old version of software
  • Web browsers allow huge numbers of open WebSockets. The infamous 2 connections per host limit does not apply to WebSockets. Instead a far bigger limit holds (255 in Chrome and 200 in Firefox). This blessing is also a curse. It means that end users opening lots of tabs can cause large amounts of load and consume large amounts of continuous server resources.
  • WebSockets and HTTP/2 transport are not unified. HTTP/2 is able to cope with the multiple tab problem much more efficiently than WebSockets. A single HTTP/2 connection can be multiplexed across tabs, which makes loading pages in new tabs much faster and significantly reduces the cost of polling or long polling from a networking point of view. Unfortunately, HTTP/2 does not play nice with WebSockets. There is no way to tunnel a WebSocket over HTTP/2, they are separate protocols.
  • WebSockets give the illusion of reliability. But of course the Internet is a very unreliable place. Laptops go offline, you turn on airplane mode when you had it with all the annoying marketing calls, Internet sometimes does not work. This is sometimes harder to manage with web sockets than it can be with Comet techniques.

There are very valid technical reasons many of the biggest sites on the Internet have not adopted them. Twitter use HTTP/2 + polling, Facebook and Gmail use Long Polling.
Saying WebSockets are the only way and the way of the future, is wrongheaded. HTTP/2 may end up winning this battle due to the huge amount of WebSocket connections web browsers allow, and HTTP/3 may unify the protocols.

Again, in several contexts i have simply not been able to make use of WebSocket due to the impossibility for them to pass through a proxy or the poor support from a specific browser versions. And again, Comet with the forever iframe technique simply always works.

2. Comet - various techniques

In streaming mode, one persistent connection is opened. There will only be a long-lived request since each event arriving on the server side is sent through the same connection. Thus, it requires on the client side a way to separate the different responses coming through the same connection.

HTTP Comet Request and Response

The big advantage of Comet is that each client always has a communication link open to the server. The server can push events on the clients by immediately committing (completing) the responses when they arrive, or it can even accumulate and send bursts. Because a request is kept open for a long time, special features are required on the server side to handle all of these long-lived requests.

HTTP Comet Sequence Diagram

We'll now briefly see the three common techniques used to achieve such Comet communication in Java.

2.1 Multi-Part XMLHttpRequest

This technique is to use the multi-part flag supported by some browsers (such as Firefox) on the XMLHttpRequest object.
An Ajax request is sent and kept open on the server side. Each time an event comes, a multi-part response is written through the same connection.

On the client side, XMLHttpRequest object must support the multi-part flag:

xhr = new XMLHttpRequest();
xhr.multipart = true; 
xhr.open('GET', 'ajax', true); 
xhr.onreadystatechange = function() { 
    if (xhr.readyState == 4) { 
        processResult(xhr.responseText); 
    } 
}; 
xhr.send(null);

The server side can use the AsyncContext API provided by the Servlet 3.0 specification:

protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
        throws ServletException, IOException { 

    // start the suspension of the request
    AsyncContext asyncContext = req.startAsync(); 
    asyncContext.setTimeout(0); 

    // send the multipart separator back to the client
    resp.setContentType("multipart/x-mixed-replace;boundary=\"" + boundary + "\""); 
    resp.setHeader("Connection", "keep-alive"); 
    resp.getOutputStream().print("--" + boundary); 
    resp.flushBuffer(); 

    // put the async context in a list for future usage
    asyncContexts.add(asyncContext); 
    // Then, whenever a message arrives, this list of asyncContext's can be browsed
    // and the message sent to their OutputStream
}

Advantages and disadvantages are, IMHO as follows:

  • Advantages: Only one persistent connection is opened. This is the Comet technique that saves the most bandwidth usage and backend resources.
  • Drawbacks: The multi-part flag is not supported by all browsers. Some widely used libraries, such as CometD in Java, reported issues in buffering. For example, chunks of data (multi-parts) may be buffered and sent only when the connection is completed or the buffer is full, which can create higher latency than expected. This is a showstopper in many cases

2.2 XMLHttpRequest a.k.a HTTP Long Polling

The long polling mode involves techniques that open a connection. The connection is kept open by the server, and, as soon as an event occurs, the response is committed and the connection is closed. Then, a new long-polling connection is reopened immediately by the client waiting for new events to arrive.

The server requires specific features on the server side to allow the request to be suspended. As soon as an event occurs, the server sends back the response in the suspended request and closes it, exactly like you close the output stream of a servlet response. The client then consumes the response and opens a new long-lived Ajax request to the server

function long_polling() { 
    xhr = new XMLHttpRequest();
    xhr.open('GET', 'ajax', true); 
    xhr.onreadystatechange = function() { 
        if (xhr.readyState == 4) { 
            processResult(xhr.responseText); 
            
            // next long polling phase
            long_polling();
        } 
    }; 
    xhr.send(null);
}

The server side can use the AsyncContext API provided by the Servlet 3.0 specification:

protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
        throws ServletException, IOException { 
    // start the suspension of the request
    AsyncContext asyncContext = req.startAsync(req, res); 
    asyncContext.setTimeout(0); 

    // put the async context in a list for future usage
    asyncContexts.offer(asyncContext); 
    // Then, whenever a message arrives, this list of asyncContext's can be browsed
    // and the message sent to their OutputStream
}

Advantages and disadvantages are, IMHO as follows:

  • Advantages: Easy to setup and use.
  • Drawbacks: Here as well some widely used libraries, such as CometD in Java, reported issues in buffering. For example, chunks of data (multi-parts) may be buffered and sent only when the connection is completed or the buffer is full, which can create higher latency than expected. This is a showstopper in many case.

2.3 Forever iframe

The Forever iframe technique involves a hidden iframe tag put in the page with its src attribute pointing to the servlet path returning server events. Each time an event is received, the servlet writes and flushes a new script tag with the JavaScript code inside. The iframe content will be appended with this script tag that will get executed.
Iframe stands for Inline Frame. It allows a Web page to embed one HTML document inside another HTML element. As one can imagine, it's a simple way to create a "mashup", whereby the page combines data from several sources into a single integrated document.

Normally, data delivered in HTTP responses is sent in one piece. The length of the data is indicated by the Content-Length header field.
With chunked encoding, the data is broken up into a series of blocks of data and transmitted in one or more "chunks" so that a server may start sending data before it knows the final size of the content that it's sending.
This is key to enable the iframe technique.

In Forever iframe Streaming, a series of JavaScript commands is sent to a hidden iframe as a chunked block. As events occur, the iframe is gradually filled with script tags, containing JavaScript to be executed in the browser. Because browsers render HTML pages incrementally, each script tag is executed as it is received. The main advantage of the iframe method is that it works in every common browser (Don't worry. I'll be repeating that a few more times below).

Comet with iframe Principle

Iframes were never meant to serve as a control for handling streaming. As such, this approach has been criticized by some as being a bit of a "hack". While it is true that there are better alternatives emerging, it can still be a suitable solution in smaller-scale applications. In some cases, for the reasons described above when discussing other techniques (bad HTTP proxies, bad implementations of XMLHttpRequest) it is the single and only solution that always works.

Advantages and disadvantages are, IMHO as follows:

  • Advantages: This is the single and only solution that always works. Unlike using XMLHttpRequest, there are no limit in regards to the number of connections. One can open as many iframe as one wants as long as they are connected to different root contexts.
  • Drawbacks: The solution is really a hack, more complicated to put in place that others, requires a thorough framework approach with a constraining design for client application to be robust. The browser loading icon or animation is displayed while the stream is running (even though I've been told a few times that this may seem as an advantage).

I cannot stress enough how much the given advantage of the approach is important : at the end of the day, it doesn't matter that it's more complicated or seem kind of a hack. This solution is the only one that works no matter the browser, its version and the amount of intrusive proxies between the browser and the backend.

The reason behind that is not intuitive. As a matter of fact, while HTTP proxies sometimes take surprising decisions in regards to buffering of XMLHttpRequest originating HTTP requests, they most of the time don't tamper too much with iframe originating requests. This is due to what they believe is the nature of an iframe content : a portal embedded web page that should be displayed to the user as fast as possible.
In our case, by using the forever iframe technique we benefit from that.

3. A framework for Comet in Java

So, for the reason mentioned above, I implemented a lightweight, simple and robust Comet framework based on the iframe technique. I am using this framework over and over again every time I encounter one of the limitation of WebSockets that prevent me from using them, and my best guess is that I'll keep using it until HTTP/2 becomes implemented everywhere.

This Comet framework enables any kind of java Web application that would require such feature to push events from the Java backend side to the Javascript UI side. It is designed to work with RIA Single and Only Page applications but support refreshing the page or switching to another page through some custom, application-level development.

I am presenting below the architecture and the design of this Comet framework.

Download link is below :

comet_tennis_src_0.2.tar.gz

The Comet framework is packaged with a little demonstration application implementing a Tennis Game that two players can connect to and play against each other.

In order to deploy the project, one simply needs to have both Java and Apache Maven in the path and type :

mvn clean install
on a command line opened within the project root folder.

The resulting war will be put in the standard /target directory and can be copied to Apache Tomcat's webapp folder to be deployed. That's it. It's 100% standalone and doesn't require anything else to run other than a working Apache Tomcat instance.

3.1 Introducing the Tennis DemoApp

The demo application is a little tennis game where two players, different users on different computers can play against each other by connecting to it. I designed and implemented it as simple as possible, avoided usage of jQuery or other frameworks to emphasize the simplicity and the usage of the underlying Comet framework.
It consists in a few hundreds lines of code on both the UI side (HTML / Javascript) and backend side (Java).

In order to reach it and start a party, two users simply need to reach the comet application webapp root : http://server:port/comet-tennis/, most of the time on a local Tomcat : http://localhost:8080/comet-tennis/.

Tennis Game Screenshot

The game menu is the following :

  • Init new game : create a new game. The new game ID is then announced to the user. That game ID should be communicated using external means to the other player who whould use the item below to join the game.
  • Join game : the game ID to be joined should be provided in the next textbox.
  • Quit game : should be called by a player who wants to quit a game. He can as well close his browser.
  • Score : indication of the score of the current game.

The player who creates the game is always located on the left of the board while the player who joins the game is always located on the right.

When the game is launched, players can use UP and DOWN arrows on the keyboard to control the pad.
If a player moves his pad while the ball touches it, the ball will accelerate in the same direction as the pad movement, or decelerate from the opposite direction, depending on its current momentum.

The tennis demo application is packaged in the bundle provided above.

3.2 Overall Architecture

The architecture of the Comet Framework is willingly over simple. It doesn't make any usage of any external library (except log4j and apache commons-lang) and doesn't require any advanced API or latest specification, so no AyncContext, no nio nor anything alike.
The developer downloading this package is free to adapt it, and more importantly optimize it for his own content. But I wanted something able to work anywhere even in very old application servers.

Why a new framework instead of CometD or DWR or whatever already exists ?

This need for simplicity is at the very core rational of this simple framework. I needed something that only requires me to copy a few classes here, some javascript code there and that's it. It works...

Project layout

The bundle available for download above is an integrated Apache Maven project. In regards to java classes under src/main/java, it contains the framework under package ch.niceideas.common.http.comet and the Tennis Demo application under package ch.niceideas.cometTennis.
Web resources (2 javascript files) for the framework are located under src/main/resources while the Tennis Demo Application web resources are located under src/main/webapp.

Comet Streaming Initialization flow

Overall Comet iFame Architecture

The connection process works as follows (with numbers following above schema):

  1. First, when reaching the application backend, the browser downloads the single page of the application : index.html
  2. The index.html is displayed by the web browser
  3. The serverConnect script (simplified) reaches the CometMessageServlet with a special flag making that servlet return the comet_init_connection.js script.
  4. The script is executed in the browser and initlaizes the whole shebang, including creating the streaming iframe
  5. The iframe connects to the CometStreamServlet which will be used to push messages to the javascript contect of the iframe
  6. From there, the loading of the iframe never completes. New events are pushed to the iframe from the backend using the OutputStream from the Servlet Response as they arrive.

The whole design of the backend set of components is presented below.

3.3 Overall Design

The separation of concerns is enforced. Tennis application specific logic is well separated and located in an above layer of the Comet Framework. The framework knows nothing about that specific application which manipulates the framework from APIs and by implementing interfaces.

Comet Framework Design

Most essentials components are as follows:

  • Servlets : CometMessageServlet and CometStreamServlet are connected to the web side. CometMessageServlet handles messaging and connection logic. CometStreamServlet is solely used for the purpose of streaming events to the client UI side as they happen on the backend side.
  • MessageHandlers : handle messages from UI side. SystemMessageHandler is used to dispatch a message from a client to another client or broadcast a message to all clients. TennisMessageHandler captures tennis game related messages (application specific messages) and passed them to the TennisManager
  • Managers : handle business logic. CometManager handles the low level connection and comet business while TennisManager handles tennis game logic (application specific logic)
  • CometHandle : The CometHandler is really a wrapper around a specific client's OutputStream used to send messages to this specific client.

These are the four main component types of the Framework and application. We will now focus on each and every of these components are discuss their function, responsibility and behaviour.

Comet Stream reconnection

The Comet Stream is recreated every 30 seconds to avoid issues with proxies force-closing what they consider as a staled connection. Its also helpful to adress other problem that can occur with with the iframe of the HTTP connection as well as help control memory.
At the reconnection, a new iframe is created and the previous iframe is removed from the DOM, enabling the recovery of the memory is was using.

Notions of Stream ID and Client ID

There are two important notions throughout the framework:

  • Stream ID : the Stream ID is a string identifier that identifies the stream the client is connecting to with an iframe. A client can be connected to several streams using different iframes. The backend can handle several different streams.
  • Client ID : the client ID is a string identifier that identifies in a unique way a client connected to a stream.

3.4 Focus on Servlets

Servlets are the access points the UI side of the framework or application reaches from the backend:

  • CometMessageServlet : is used for messaging purpose. There are 2 different use cases : the management of the initial connection and initialization of the comet stream (server push stream) as well as purely messaging concerns.
  • CometStreamServlet : serves solely for the comet stream (server push of events).

Focus on Servlets Design

Messages sent from the UI to the backend can be of different kinds. Some business messages can be handled by specific Message Handler interpreting the message and implementing business actions from them, while some other messages can be actual messages to be sent to another connected client or broadcasted to all clients.
As a matter of fact, the CometMessageServlet supports as system of plugins in order to handle business messages as required by the business application on top of the Comet Framework. This is detailed below.

3.5 Focus on Message Handlers

The MessageHandlers subsystem is perhaps the only tricky aspect of the framework. I needed a way for the application to implement its own messages on top of the comet framework and still have a system messaging handler to support sending of messages from a client to another as well as broadcasting messages to all connected clients.

So the idea is the following : the client application can register as many MessageHandlers it needs to the CometMessageServlet by giving the set of classes of the message handlers to be used as the messageHandlers initialization parameter of the servlet.
For instance in the case of the Tennis Demo application, we have only one message handler registered in web.xml (see below). Multiple MessageHandler classes should be separated by a coma.

Snippet of web.xml file in Tennis Demo App:

...
    <servlet>
        <servlet-name>CometMessage</servlet-name>
        <servlet-class>ch.niceideas.common.http.comet.CometMessageServlet</servlet-class>
        <init-param>
            <param-name>messageHandlers</param-name>
            <param-value>ch.niceideas.cometTennis.message.TennisMessageHandler</param-value>
        </init-param>
    </servlet>
...

Whenever a message is received by the CometMessageServlet, it iterates through the list of registered message handlers asking each of them whether it accepts the message. As soon as a message handler returns true, its stops the loop and considers the message handled.
Developers can register as many message handlers they needs. All the message handlers will be processed in the same order as the classes declaration in the web.xml file above.
There are no constraints in regards to message formats, but messages need to be passed to the CometMessageServlet, using URL parameters in the form http://server:port/application/serverComet.message?streamId=XXX&clientId=XXX&message=XXX. Proper escaping should be considered in regards to the message content.

Focus on Messaging Design

There is one additional message handler hardcoded by the system and considered only last : the SystemMessageHandler which is used to send messages from a client to other clients.
The SystemMessageHandler is always called last (hardcoded) and accepts all messages passed to it. It expects a Stream ID, a message and an optional Client ID.

  • In case a Client ID is provided, it sends the given message to that specific client.
  • In case no Client ID is provided, it broadcasts the messages to all clients connected to the identified Stream.

The most important purpose of this Message Handling design is to be able to route different messages to different managers, thus enabling the client application to implement its own specific messages on top of the Comet Framework.

3.6 Focus on Comet Handle

The CometHandler is really just a wrapper around the OutputStream to be used to send a message to a client connected with its iframe on the CometStreamServlet. It also associates the related Stream ID and Client ID to the OutputStream

Focus on Handle Design

In addition, the CometHandler owns the monitor on which the Web Container thread calling the CometStreamServlet doPost method waits. It is indeed important to have that doPost method call never returning as long as we want the OutputStream to be kept opened.
For this reason the calling thread needs to be put on hold of a monitor and is released when we want to close the stream (every 30 seconds the stream is closed and recreated in anyway).

3.7 Focus on Managers

Managers implement the core business logic of the application.

  • CometManager : takes care of the Comet framework business : managing CometHandle and dispatching messages to single client or broadcast messages.
  • TennisManager : takes care of the Tennis Game business. The TennisManager relies on a Business Object Model implemented by the classes TennisGame, TennisBall and TennisPlayer

Focus on Managers Design

The CometManager owns a map of all CometHandle currently active and connected on available streams.

The TennisManage has a timer that periodically moves all stored TennisGames one period further. This increments the ball position, moving it one display unit forwards as well as the player pads and detects collisions between the ball and the walls or the player pads.
This is implemented by the period() methods.

3.8 Considerations for Client applications

Implementing a client application on top of the Comet framework is pretty straightforward:

  • Implement a MessageHandler class accepting the application specific messages. This class can well do the whole job or delegate to whatever makes sense.
  • Implement a handleServerMessage javascript function that need to be available in the main frame context on the UI side. That function needs to have the form handleServerMessage = function (msg) { ... } and will be passed business messages by the comet framework.

The business MessageHandler should throw a CometBusinessException or a subclass of it in case of a business error. Such exceptions are handled in a specific way by the framework and the content message is displayed to the user using the javascript alert function.

4. Conclusion

A stated in introduction and throughout this article, I do not believe the forever iframe technique and this Comet framework is the best or most elegant solution to achieve server push in HTTP.
But it really is the only one that always work, no matter the browser, no matter what crappy proxy is on the line.

I wish the situation would be different and WebSockets would suffer from less issues and always work as well, but in practice it's just not the case and I end up reverting to this technique pretty often. My best guess is that this will last until HTTP/2 is widely deployed.

I haven't mentioned any actual business use case in this article and I'd be happy to post some examples in comment to this article if I'm asked to. Let's just say that uses cases are so numberous, mostly around pushing notifications regarding asynchronous processing results or failures to the end user.

Download link :

comet_tennis_src_0.2.tar.gz



No one has commented yet.

Leave a Comment

HTML Syntax: Allowed