SitePen Support
Orbited

Using REST Channels in Dojo

by Kris ZypNovember 12th, 2008

REST Channels is a Comet protocol for data notifications designed to integrate with a REST architecture. REST Channels leverages the semantics of HTTP messaging and provides notifications of the creation, update, and deletion of resources using REST’s resource identifiers (URI) in order to provide real-time views of data from web clients.

Dojo 1.2 includes an implementation of REST Channels that integrates with Dojo’s comprehensive support for RESTful architectural style to provide a consistent amd straightforward approach to working with live data, even utilizing Dojo’s Data Notification API to broadcast changes all the way to widgets with little effort. REST Channels isn’t a REST architecture component itself, but is designed to complement REST architecture, providing a Comet mechanism that works with REST, not against it, and Dojo’s implementation exemplifies this synergy.

Dojo’s REST Channels implementation is the dojox.cometd.RestChannels module and can be used in three different ways. First, it can be used as a Bayeux transport, equating resource identifiers to PubSub channels or topics, allowing resources to be conduits for messaging. Second, the module can be used to provide data updates for the JsonRestStore, updating the local cache of objects and triggering notification events for widgets listening to the store. Finally, the module can also be used standalone for directly requesting data and subscribing to updates. These different usages are not mutually exclusive, RestChannels can be used with JsonRestStore, with cometD, and directly, all within the same page.

RestChannels + JsonRestStore

To use RestChannels in conjunction with JsonRestStore, first load the RestChannels module after JsonRestStore and then by default any new stores will be enabled to support real-time data:

dojo.require("dojox.data.JsonRestStore");
dojo.require("dojox.cometd.RestChannels");
var weatherStore = new dojox.data.JsonRestStore({target:"/weather"});

When we retrieve data through our store and trigger a server request, the request will automatically have a subscription header added to it, indicating that the client wishes to retrieve the requested data and subscribe to that data as well. But first, the RestChannels module will create a Comet connection to the server to receive any data updates for changes in this data resource or any other data resource that we subsequently subscribe to. By default, the RestChannels module connects to /channels for updates. This Comet connection is then associated with subscription requests by a client id header. The initial request would look like:

POST /channels
X-Create-Client-Id: 487852768969

Then the HTTP request for the data resource that was triggered by the JsonRestStore could look like:

GET /weather/84070
X-Subscribe: *
X-Client-Id: 487852768969

And if the server can accept the subscription, it should respond with a normal response and include a X-Subscribed: OK header. When one of the subscribed resources is modified, the server can then send a notification message on the /channels connection. For example:

{
   "event": "PUT",
   "source": "/weather/84070",
   "result": {
      "sky": "Sunny",
      "temperature": 42
    }
}

Using the RestChannels enhanced JsonRestStore doesn’t require any changes. JsonRestStore already supports Dojo’s notification API, and any updates received from the server are automatically delivered to listeners (of onNew, onSet, and onDelete) through the notification interface on the JsonRestStore. For example, to use the JsonRestStore with a grid widget, so that notifications update the user interface:

grid.setStore(weatherStore);

And that’s it! The grid is already built to handle notifications, REST Channels messages are automatically routed through the JsonRestStore infrastructure.

RestChannels + cometD

To use RestChannels module as a Bayeux transport with Dojo’s cometD system, you simply must load RestChannels prior to loading the cometD module:

dojo.require("dojox.cometd");
dojo.require("dojox.cometd.RestChannels");

This will cause the RestChannels module to register itself as a Bayeux transport, and it will then be advertised as one of the available transports when Bayeux performs transport negotiation with a server. If RestChannels is chosen as the transport for Bayeux communication, it will connect to the server on the channels target URL, and listen for messages. When a subscription is requested, this is translated to a HEAD request (so that content is downloaded when subscribed) against the target topic/URL. If we subscribed via cometD to /chat/room1:

dojox.cometd.subscribe("/chat/room1", function(data){
  // executed when a message is received from this URL
  alert(data);
});

This would trigger a subscription request to the server:

HEAD /chat/room1
X-Client-Id: 487852768969
X-Subscribe: *

When notification messages are received from the server, the result of each message is then delivered to a each subscriber. For example, if the following message was received from the server:

{
   "event": "message",
   "source": "/chat/room1",
   "result": "hello"
}

This would trigger the alert with “hello” in the callback function above.

Configuring RestChannels

By default, there is a single RestChannels instance created when RestChannels is loaded. This instance is intended to correspond to the connection with the origin server. All subscriptions to this server should have their notifications delivered through this connection so that only one HTTP connection is used. Additional instances of RestChannels can be created for cross-site servers.

There are a number of options that can be configured on a RestChannels instance. The default instance for the origin server can be accessed at dojox.cometd.RestChannels.defaultInstance. To use an alternate URL for connecting to the server for notifications (instead of /channels):

dojox.cometd.RestChannels.defaultInstance.url = "/someOtherUrl";

You can also choose which resources should be automatically subscribed when retrieved. By default, any request will be subscribed, but you can define that only requests that start with a path of /weather/ should be subscribed:

dojox.cometd.RestChannels.defaultInstance.autoSubscribeRoot = "/weather/";

Using RestChannels Standalone

RestChannels can also be used as a standalone module. We can directly interact with the RestChannels API. The primary functions of interest are get(), subscribe(), and receive(). The open() method is automatically called as needed, so it is generally not necessary to call this directly. The get() method works very similar to dojo.xhrGet() except that it subscribes to the target resource (the URL is the first argument, the XHR named parameters are the second argument). The receive() method is called whenever an update is received from the server, and when using RestChannels standalone, you will generally want to connect/listen to this function. Let’s look at an example, first creating an alias for the default local instance of RestChannels:

var channels = dojox.cometd.RestChannels.defaultInstance;
// retrieve the current weather conditions (subscribed 
// to it as well), and update the UI
channels.get("/weather/84070").addCallback(updateUI);
// when updates are received, update the UI 
dojo.connect(channels, "receive", function(message){
   updateUI(message.result);
});
 
function updateUI(weatherReport){
  // update the UI based on the weather report
}

Notice we can use the same user interface function for both the GET response and the update listener, allowing a for a single consistent user interface handler.

In Dojo 1.2 when a connection is lost (due to network outage, computer sleeping, etc), if and when the connection is restored, RestChannels will attempt to resubscribe to all of the monitored resources by using HEAD requests with an X-Subscribe-Since header (indicating how far back in time to get updates). However, one of the changes in RestChannels for Dojo 1.3 is that when a connection is restored, by default RestChannels will resubscribe using GET requests without an X-Subscribe-Since header. While this is less efficient because it results in a full download of all the previously requested data, it is much easier to implement on the server (the retroactive subscription mechanism of X-Subscribe-Since can be difficult to implement). In Dojo 1.3, the full download behavior can be controlled by the reloadDataOnReconnect option, which is true by default.

Client Generated Ids

In order maximize performance, the REST Channels relies on client provided ids to associate HTTP GET/subscription requests with Comet notification requests. This eliminates the need for extraneous requests to get a server-assigned id before creating subscriptions; both GET/subscription requests and the Comet notification connection can be sent simultaneously. The Dojo implementation of REST Channels uses Math.random() to create a client id. While duplicate ids between clients are rare, browsers typically seed the pseudo-random generator with the current time. This means if two browsers are seeded at exactly the same millisecond and have the same number of random calls prior to execution, then a duplicate id is possible (though still rare).

If you expect a large number of simultaneous connections with a REST Channels based application, you can improve the uniqueness of the ids to prevent any possibility of duplicate ids. The easiest way to do this is to use the server-generated id from the session cookie. With J2EE servers, this is the JSESSIONID (with PHP it is PHPSESSID). Combining the session id with Math.random() provides an id without any realistic chance for collision. In Dojo 1.2, the client identifier was stored in dojox._clientId, but Dojo 1.3, this is a public property, dojox.rpc.Client.clientId. To update the client identifier with a high-quality unique value:

dojo.require("dojo.cookie");
dojox.rpc.Client.clientId += dojo.cookie("JSESSIONID");

Conclusion

Dojo’s implementation of REST Channels provides comprehensive integration with Dojo’s client REST capabilities. REST Channels is designed to be straightforward to implement on the server with maximum scalability by following REST principles, and with Dojo 1.3 it is even easier. Alternately, you can use the existing REST Channels capabilities of Persevere on the server. With Dojo’s existing data and notification APIs, many widgets are already written to handle real-time updates, and RestChannels makes it very easy to utilize Comet updates from the server to keep client-side data synchronized and have notifications delivered all the way to the user interface automatically.

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]
SitePen, Inc. Comet Services

10 Responses to “Using REST Channels in Dojo”

  1. Subbu Allamaraju Says:

    What are those extension headers for? Why does this model overload HTTP with custom headers?

  2. Kris Zyp Says:

    Subbu,
    The reason for using headers is first of all, because REST channels (and just about any Comet protocol) fundamentally is trying simulate a full-duplex connection (like a TCP connection) over HTTP. The connection headers used to associate HTTP request into a single virtual connection are transport level information (and generally transport level information belongs in headers within HTTP). Also the subscription header is a header so that it doesn’t prevent the caching of requests. Granted, a GET/subscription request must go through caches, but the response can be cached for other GETs that don’t include a subscription request. Does that make sense?

  3. Neha Says:

    Hi

    I am a little confused by how all of this works.

    If I have a URI [/userstatus] that has a
    (GET) getStatus() (returns status of all users in the format { {”A”,”aCTIVE”}, {”B”,”Not a desk”}}
    (PUT) setStatus(String userName,String Status) (updates status of the particular user)

    How do I implement a JSONRestChannel that pub/subscribes to /userstatus
    such that all clients get updated (with a GET request) whenever a client “PUT”s a request to the /userstatus?

    I am using the following code

    //Server Side

    @Singleton
    @Path(”/userStatus”)

    public class UserStatus {

    @GET
    @Produces(”application/json”)

    public StatusInfoBean getStatus() {

    return statusInfoBean ;
    }

    @PUT
    @Consumes(”application/json”)
    public synchronized void setStatus(String user, String status) {
    ….
    }

    /*Client side code*/
    dojo.require(”dojox.cometd”);
    dojo.require(”dojox.rpc.Service”)
    dojo.require(”dojox.data.JsonRestStore”);
    dojo.require(”dojox.cometd.RestChannels”);

    store = new dojox.data.JsonRestStore({target:”/services/userStatus”});

    ..

    what do I do to publish / subscribe or do a GET/PUT to /userStatus?

  4. Neha Says:

    Kris,
    Any idea why in RestChannel.js , the subscribe call to cometd channel in the OPEN function is in the form of a POST message?

    Shouldn’t it be in the format like

    POST DATA
    dojo.io.script.jsonp_dojoIoScript5._jsonpCallback
    message [{"data":{"room":"/chat/demo","user":"j","chat":"sdfsdg","peer":undefined},"channel":"/service/privatechat","clientId":"1ffkkyswzx4op6qfnw","id":"5","timestamp":"Wed, 10 Dec 2008 17:06:19 GMT"}]

    GET REQUEST
    http://localhost:8080/cometd/cometd?message=%5B%7B%22data%22%3A%7B%22room%22%3A%22%2Fchat%2Fdemo%22%2C%22user%22%3A%22j%22%2C%22chat%22%3A%22sdfsdg%22%2C%22peer%22%3Aundefined%7D%2C%22channel%22%3A%22%2Fservice%2Fprivatechat%22%2C%22clientId%22%3A%221ffkkyswzx4op6qfnw%22%2C%22id%22%3A%225%22%2C%22timestamp%22%3A%22Wed%2C%2010%20Dec%202008%2017%3A06%3A19%20GMT%22%7D%5D&jsonp=dojo.io.script.jsonp_dojoIoScript5._jsonpCallback

  5. Kris Zyp Says:

    I am not sure I understand, looking at your example it looks like you are using this cross-domain, and therefore the parameters are in the form of standard query parameters. Is that a problem?

  6. david Says:

    I am using dojo, ArcGIS Server REST serivice and Python geoprocessors to create a service.

    I need to understand better and implement session ids in dojo and Python. Where do I find reference on this?

    Regards.
    David davidgshi@yahoo.co.uk

  7. Nancy Says:

    Hi Kris,

    I am using dojox.cometd for my project and do the conventional dojox.cometd.subscribe and publish on the client side. But in this I am registering to a channel/topic and not a ‘resource’. Also I have client-server communication established via Bayeux transport. Is there any advantage to use REST channels in such a scenario? If yes - I am looking to go with this approach but I am not sure how should I have the server side code for a particular resource exposed as rest, as I understand only the client side code part of it from your article.

  8. Kris Zyp Says:

    @Nancy: Some of the motivations for using REST Channels (AKA HTTP Channels) are described here: http://cometdaily.com/2008/05/13/http-channels-2/
    But briefly, the advantage is that you can integrate Comet capabilities with a RESTful architecture, which is particularly well-suited for applications built for providing a real-time view of data.
    Also, the protocol (that you would need to implement on the server) is described here:
    http://cometdaily.com/2008/09/02/rest-channels-http-channels-with-json-support/

  9. Nancy Says:

    If we have Bayeux cometd on the server side, and if we want to use rest channels on the client side (may be have to tweak rest channels to suit our needs) is it possible? We do not want to change the server side logic/code. Considering that- if we change/add javascript code on the client side on top of rest channels such that it can understand the bayeux publishes, then we can achieve functionality similar to rest channels but cometd as backend.

    The areas we will need to change would be:

    1. While subscribing for a resource - rest channels will need to establish/listen on a cometd connection instead of channels servlet.
    2. As rest channels expect a particular HTTP response/notification - we would ideally need to add the server side code to it. But is there a way where we can create a hack - so that we don’t have any change on the server side.
    3. Also when you make a GET /weather/84070, in our case- it would be a JAVA class mapped to weather which would return back the whole table. Not sure how ‘id’ would work. Is it necessary in case of JsonRestStore?

    These questions might sound little wague, as I am new to stores and rest channels in DOJO.
    Also wanted to mention that I have played around with Persevere - and got a brief idea of it, but as there is nothing we want to change on the server side, I would highly appreciate if you can help us on the client side to suit our needs.

    Thanks

  10. SitePen Blog » Using REST Channels with cometD Says:

    [...] integrates Comet-style asynchronous server sent messages with a RESTful data-oriented architecture. Dojo includes a REST Channels client module which integrates completely with Dojo’s JsonRestStore, allowing messages to be delivered [...]

Leave a Reply



Copyright 2014 Comet Daily, LLC. All Rights Reserved