Skip navigation.

CometD JavaScript Subscription

JavaScript CometD API: Subscribing and Unsubscribing

Channels

The Bayeux specification defines the concept of a channel: it is like a messaging topic where interested parties can subscribe to receive information published onto the channel.
There are 3 types of channels:

  • meta channels
  • service channels
  • normal channels

A channel looks like a directory path such as /meta/connect (a meta channel; all meta channels starts with the prefix /meta/), or /service/chat (a service channel; all service channels starts with the prefix /service/) or /foo/bar (a normal channel).

Meta Channels

Meta channels are created by the Bayeux protocol itself.
It is not possible to subscribe to meta channels: the server will reply with an error message. However, it is possible to listen to meta channels (see below the difference between subscribing and listening).
It makes no sense to publish messages to meta channels: only the Bayeux protocol implementation creates and sends messages on meta channels.
Meta channels are useful on the client to listen for error messages like handshake errors (for example because the client did not provide the correct credentials) or network errors (for example to know when the connection with the server has broken or when it has been re-established).

Service Channels

Service channels are used in the case of request/response style of communication between client and server (as opposed to the publish/subscribe style of communication or normal channels).
While subscribing to service channels yields no errors, this is a no-operation for the server: the server ignores the subscription request.
It is possible to publish to service channels, with the semantic of a communication between a specific client (the one that's publishing the message on the service channel) and the server.
Service channels are useful to implement, for example, private chat messages: in a chat with userA, userB and userC, userA can publish a private message to userC (without userB knowing about) using service channels.

Normal Channels

Normal channels have the semantic of a messaging topic and are used in the case of publish/subscribe style of communication.
Usually, it is possible to subscribe to normal channels and to publish to normal channels; this can only be forbidden using a security policy on the Bayeux server.
Normal channels are useful to implement broadcasting of messages to all subscribed clients, for example in case of a stock price change.

Subscribers versus Listeners

The JavaScript CometD API has 2 APIs to work with channel subscriptions:

  • addListener() and the correspondent removeListener()
  • subscribe() and the correspondent unsubscribe()

The addListener() method:

  • must be used to listen to meta channel messages
  • may be used to listen to service channel messages (you may also use subscribe())
  • should not be used to listen normal channel messages (use subscribe() instead)
  • does not involve any communication with the Bayeux server, and as such can be called before calling handshake()
  • is synchronous: when it returns, you are guaranteed that the listener has been added

The subscribe() method:

  • must not be used to listen to meta channels messages (otherwise the server will return an error)
  • may be used to listen to service channel messages (you may also use addListener())
  • should be used to listen to normal channel messages
  • involves a communication with the Bayeux server and as such cannot be called before calling handshake()
  • is asynchronous: it returns immediately, well before the Bayeux server has received the subscription request

Note
Calling subscribe() does not mean that you have completed the subscription with the server when subscribe() returns.

Both addListener() and subscribe() return a subscription object that must be passed to, respectively, removeListener() and unsubscribe():

// Some initialization code
var subscription1 = cometd.addListener('/meta/connect', function() { ... });
var subscription2 = cometd.subscribe('/foo/bar/', function() { ... });

// Some de-initialization code
cometd.unsubscribe(subscription2);
cometd.removeListener(subscription1);

A common pattern of utilization is to handle the subscription code in an idempotent method, like this:

var _subscription;

// The idempotent method
function _refresh()
{
    _appUnsubscribe();
    _appSubscribe();
}

function _appUnsubscribe()
{
    if (_subscription) 
        cometd.unsubscribe(_subscription);
    _subscription = null;
}

function _appSubscribe()
{
    _subscription = cometd.subscribe('/foo/bar', function() { ... });
}

The same of course applies also for addListener()/removeListener().

The point is that you have to be careful in your application: you have to remove your subscriptions, in order to avoid to leak functions, or to execute functions more than once (since you could erroneously bind the same callback twice).
Refer also to the Primer for a discussion about using idempotent methods.

How do subscribe() and unsubscribe() behave in case the Bayeux server is not reachable (due to network failures or because the server crashed) ?
In subscribe() the local listener is first added to the list of subscribers for that channel, then the server communication is attempted. If the communication fails, the server will not know that it has to send messages to this client and therefore on the client the local listener (although present) will never be invoked.
In unsubscribe(), the local listener is first removed from the list of subscribers for that channel, then the server communication is attempted. If the communication fails, the server will still send the message to the client but there will be no local listener to dispatch to.

Listeners/Subscribers Exception Handling

If a listener or subscriber function throws an exception (for example, calls a method on an undefined object, etc.), then the error message is logged (at level "debug").
However, there is a way to intercept these errors by defining the global listener exception handler, that is invoked every time a listener or subscriber throws an exception:

cometd.onListenerException = function(exception, subscriptionHandle, isListener, message)
{
    // Uh-oh, something went wrong, disable this listener/subscriber
    // Object "this" points to the CometD object
    if (isListener)
        this.removeListener(subscriptionHandle);
    else
        this.unsubscribe(subscriptionHandle);
}

It is be possible to send messages to the server from the listener exception handler.
If the listener exception handler itself throws an exception, this exception is logged at level "info" and the CometD implementation will not break.
Note that a similar mechanism exists for extensions, see here.

Wildcard Subscriptions

It is possible to subscribe to several channels at once using wildcards, like this:

cometd.subscribe("/chatrooms/*", function(message) { ... });

A single asterisk has the meaning of matching a single channel segment, so in the example above it will match channels /chatrooms/12 and /chatrooms/15, but not /chatrooms/12/upload.
To match multiple channel segments, use the double asterisk:

cometd.subscribe("/events/**", function(message) { ... });

With the double asterisk, the channels /events/stock/FOO and /events/forex/EUR will match, as well as /events/feed and /events/feed/2009/08/03.

The wildcard mechanism works also for listeners, so it is possible to listen to all meta channels like this:

cometd.addListener("/meta/*", function(message) { ... });

By default, subscriptions to the global wildcards /* and /** result in an error, but this behavior can be changed by specifying a custom security policy on the Bayeux server.

The wildcards can only be specified as last segment of the channel, so these are invalid subscriptions: /**/foo or /foo/*/bar.

Meta Channel List

These are the meta channels available in the JavaScript CometD implementation:

  • /meta/handshake
  • /meta/connect
  • /meta/disconnect
  • /meta/subscribe
  • /meta/unsubscribe
  • /meta/publish
  • /meta/unsuccessful

Each meta channel is notified when the correspondent Bayeux message is handled by the JavaScript Cometd implementation.
The /meta/unsuccessful channel is notified in case of any failure.

By far the most interesting meta channel to subscribe to is /meta/connect, because it gives the status of the current connection with the Bayeux server. In combination with /meta/disconnect, it can be used, for example, to display a green "connected" icon or a red "disconnected" icon on the page, depending on the connection status with the Bayeux server.

This is a common pattern using the /meta/connect and /meta/disconnect channels:

var _connected = false;

cometd.addListener('/meta/connect', function(message)
{
    // if (cometd.getStatus() === 'disconnecting' || cometd.getStatus() === 'disconnected')
    if (cometd.isDisconnected()) // Available since 1.1.2
    {
        return;
    }
    var wasConnected = _connected;
    _connected = message.successful;
    if (!wasConnected && _connected)
    {
        // Reconnected
    }
    else if (wasConnected && !_connected)
    {
        // Disconnected
    }
});

cometd.addListener('/meta/disconnect', function(message)
{
    if (message.successful)
    {
        _connected = false;
    }
}

One small caveat with the /meta/connect channel is that /meta/connect is also used for polling the server.
Therefore, if a disconnect is issued during an active poll, the active poll is returned by the server and this triggers the /meta/connect listener.
The initial check on the status verifies that is not the case before executing the connection logic.

Another interesting usage of meta channels is when there is an authentication step during the handshake.
In this case the registration to the /meta/handshake channel can give details about, for example, authentication failures.