Developing for Comet

by Joe WalkerMarch 21st, 2008

There are 3 styles of API that are relevant when considering Comet services:

  • Traditional API Style. This API is the most obvious. If you want to set the text of a button, you call
    field.setValue(43.5) or something similar. It’s possible to imagine complex APIs like the SWT library made available remotely. This type of API does not need Comet, unless the trigger for wanting to alter the UI is asynchronous.
  • Message Passing Style. This API style is very simple—typically just a subscribe() method to declare an interest in some topic, and a publish() method to declare some information to a topic. Some messaging APIs (notably JMS) add a large number of classes around this simple concept, however at its core, it’s as easy as those 2 methods.
  • Synchronized Data Style. This style involves some data cache on the server that is synchronized with a similar data cache on the client (or clients).

There are pros and cons to all these API styles. I’m going to compare them with notes as to how they are supported in DWR. Unless noted otherwise the code below works in DWR version 3, milestone 1.

Traditional API Style

The most obvious API style is fairly heavyweight in that there could be a lot to learn, but it’s flexible, and easy to understand what is going on.

One of the things that we pride ourselves on with DWR is a minimalist API, so how do we go about supporting this interface style? We certainly don’t want to invent a widget toolkit to control remotely. The route we have taken involves replicating currently existing JavaScript APIs in Java. The first API that we’ve cloned is the TIBCO GI API, and I hope to have the Dojo Toolkit API working remotely before too long, although it might end up going into 3.1.

Here’s an example from the Java version of the GI API that’s in DWR 3 milestone 1. If you are familiar with the TIBCO GI JavaScript API, this will be a strange deja-vu of seeing stuff you are familiar with used from Java. When this code is run, DWR generates JavaScript on the fly, and sends it using Comet/reverse Ajax to all the browsers looking at ticketcenter.html.

import jsx3.GI;
import jsx3.app.Server;
import jsx3.gui.*;
import jsx3.xml.*;
 
// ... Then further down
// A CDF document is how GI stores data on the client
// This code simply populate a new CDF document with server-side data
CdfDocument cdfdoc = new CdfDocument("jsxroot");
for (Call call : calls) {
    cdfdoc.appendRecord(new Record(call));
}
 
// DWR code to find the browsers looking at a page
ServerContext serverContext = ServerContextFactory.get();
Collection<ScriptSession> sessions = 
	serverContext.getScriptSessionsByPage("ticketcenter.html");
 
// Get a handle onto the GI Server object so we can push
// the CDF doc into the client side cache, and repaint the table
Server tc = GI.getServer(sessions, "ticketcenter");
tc.getCache().setDocument("callers", cdfdoc);
tc.getJSXByName("listCallers", Matrix.class).repaint(null);

Many people will be more familiar with the Dojo Toolkit API. We hope to be able to create a shared text editor with code along these lines (this API is not part of DWR 3 milestone 1).

Firstly the Dojo Toolkit code in the browser in edit-demo.html:

<script>dojo.require("dijit.InlineEditBox");</script>
<script src="/dwr/interface/Server.js"></script>
 
<span id="myedit" dojoType="dijit.InlineEditBox"
	onChange="Server.update(arguments[0])">
Some text that many users can edit
</span>

And then the server code in Java that uses a DWR/Dojo Toolkit integration:

import org.dojotoolkit.proxy.dijit.Dijit;
import org.dojotoolkit.proxy.dijit.Editor;
 
public class Server {
  public void update(String newValue) {
    // DWR functions to find the users viewing a given page
    Collection&lt;ScriptSession&gt; sessions =
        WebContextFactory.get().getScriptSessionsByPage("edit-demo.html");
 
    // Proxy API generated from the GI API.
    Dijit dijit = new Dijit(sessions);
    Editor editor = dijit.byId('myedit', Editor.class);
    editor.setValue(newValue);
  }
}

These 2 examples make use of ‘drapgen’, a tool that takes a JavaScript API, introspects it, and automatically generates a Java API, which looks similar to the JavaScript API. When this Java API is executed the original JavaScript is generated, which is sent to the client asynchronously.

DWR also contains a small server-side version of the DOM that’s built into all web browsers:

ServerContext serverContext = ServerContextFactory.get();
Collection<ScriptSession> sessions = serverContext.getAllScriptSessions();
 
Window window = new Window(sessions);
window.alert("Hello, World");

DWR also contains a lower level API that allows you to execute arbitrary JavaScript code.

ScriptProxy proxy = new ScriptProxy(sessions);
proxy.addFunctionCall("window.writeln", new Date());

What about other Ajax libraries? We’ve already got a server side version of Scriptaculous.Effect. I’d like to see how easy it would be to create a server-side version of YUI, Ext and maybe jQuery too. I also wonder if it’s possible to create a remote version of the GWT API. So DWR could link to classes compile against the GWT API, but execute them at runtime for asynchronous delivery.

Message Passing Style

Message passing’s strengths are its simplicity and reduction of dependencies. In the Ajax world, the OpenAjax Hub is an excellent place to start thinking about an API provider.

During development of the OpenAjax Hub we developed a client written using TIBCO GI to which we published data using both DWR and Lightstreamer without any changes to the GI code. We just plugged a different data source into the hub.

Version 3 contains 2 new ways to use a message passing API style. The DWR hub is accessible from any thread. Messages published to the hub will be sent to any browser that has subscribed to that topic using the OpenAjaxHub.

Person joe = new Person("Joe Public");
HubFactory.get().publish("people", joe);

Subscribing to broadcasts made by browsers is just as easy:

HubFactory.get().subscribe("people", new MessageListener() {
    public void onMessage(MessageEvent message) {
        Person sarah = message.getData(Person.class);
        log.info(sarah.getName());
    }
});

Also in version 3, DWR contains an integration with JMS, so you can use a JMS API to achieve just what has been achieved above. I will skip over the code here as people that care about JMS will probably be able to imagine it anyway, and those that don’t will just be horrified that it takes twice as many lines to do just the same thing. If you are really interested you can take a look at the demo example in the DWR repo.

Synchronized Data Style

Many client-side APIs have data stores that serve as the M in an MVC framework. Perhaps the simplest style of API would be one in which some server side data cache was automatically synchronized with a browser or browsers on some page. Given some setup the API might then be as simple as:

dataStore.add(person);

DWR does not support this style yet, but we hope to add it as part of the work to integrate more deeply with various widget toolkits.

Which Style is Best?

The advantages of message passing style include:

  • Low coupling between data sources and data syncs. This makes systems more robust and more testable.
  • Good inter-language interoperability—SOAP has evolved from an RPC model to a more message based model predominantly due to interoperability issues.

The advantages of the traditional style include:

  • Tighter coupling, so you rely less on documentation to know what options exist rather than a more formalized API. Message passing could be called extreme dynamic typing, so if you like statically typed APIs, you’re more likely to like the traditional API style.
  • Less rich API. Remote API models may have a very broad range of possibilities. While message models may be as broad, they typically are not.

The advantages of a synchronized data style include:

  • It requires very little thought or understanding—the API might even be an API like the Java collections API that you know already.
  • It is possible for an advanced API to conserve bandwidth by grouping changes, and even pruning multiple updates to the same element into a single update.

On the DWR project, we don’t believe that there is a ‘right’ answer to the question ‘which of these alternatives is best’, so we are extending our support for all of them.

4 Responses to “Developing for Comet”

  1. Kris Zyp Says:

    Great article, I have been thinking about this same thing and the relationships between these different models. Article coming soon…

  2. Comet Daily » Blog Archive » Pub/Sub’s relationship with REST and RPC Says:

    [...] RPC. REST and RPC also have a relationship with the publish and subscribe architecture. Joe Walker recently discussed three major API styles for Comet: traditional, message-passing, and data synchronization. These API styles correlate to these three [...]

  3. Comet Daily » Blog Archive » Persevere Adds Comet Support Says:

    [...] that a client can not only directly access persisted data from the server, it can maintain a live synchronization with the persisted [...]

  4. maha Says:

    I have an application that runs in SWT Browser.
    The Web pages I load and the Browser I load them in are totally under my control.
    The Browser is implemented in SWT API. Can I expect the Comet to Work fine?


Copyright 2015 Comet Daily, LLC. All Rights Reserved