Asynchronous notifications from server to client.

Notification Protocol Description

HTTP as a transport mechanism is client-push only. There is no built-in, well supported mechanism for an HTTP server to pass events and notifications to a client.

For the File System Daemon, we have opted to use a per-server notification channel, that broadcasts notifications to clients that have requested a special URL. Each notification is given a key that allows the receiving clients to decide if they care about that notification. The notification message itself is a JSON-encoded serialized object, whose type is specified by a language-independent string.

A message on the notification channel looks like:

Key Type Length Serialized-Object\n
(See {@link NotificationMarshaller} for a complete explanation of each field)

The notification channel is available at the URL http://localhost:port/notification (where the port is the current FSD port). After the client has connected to that URL (authenticating with the FSD's secret), an HTTP 200 response is returned, with the content type application/vnd.ibm.jazz-json-notification-1.0.

Note:

  1. Clients should ignore notifications with an unknown key.
  2. Clients should ignore notifications with an unknown type.
  3. If an error is encountered while the client is parsing a message, discard that message and walk forward to the subsequent newline character. Alternatively, disconnect from the channel and reconnect.
  4. Notifications are queued. Slow clients may have a number of notifications piling up in their channel.

Handling Progress with the Notification Channel

A client can request progress reports about a call by adding the X-Request-Progress to a request (the header value is defined in {@link Header#REQUEST_PROGRESS}, and may change over time). The header value is the key that identifies the request.

After a client makes a request with the X-Request-Progress header it can expect to see messages about the request on the notification channel. The messages are delivered asynchronously (ie, the messages show up while the original request is still blocked waiting for the response).

Each progress report is encoded in a message with the type progress (defined in {@link ProgressNotificationParm#TYPE}). The serialized object has the following four fields:

taskName
(String) The name of the current task being executed.
subTask
(String) Some task happening within the current taskName.
progress
(Double) The progress, out of 100, that the monitor has completed at the time of message send.
cancelled
(Boolean) A flag indicating whether or not the task has been cancelled. A cancelled task will not complete, but should leave the FSD in a usable state.

After the request has completed, the FSD may send more notifications with the given key. Those should be considered stale and discarded.

Note that the programming model for progress in Eclipse assumes that progress is being consumed locally, and may be very chatty. To limit the chatter on the notification channel, progress notifications are sent at most every 100ms (per request).

Sample Progress Conversation

The following is a short conversation showing how the client and server communicate with a notification channel.

Client Joins the Notification Channel

To begin, the client joins the notification channel:

GET /notification/ HTTP/1.1
X-Secret-Key: 5D6A035927AA1883E10BF584A488EEA3
User-Agent: Jakarta Commons-HttpClient/3.0
Host: 127.0.0.1:57640

The FSD responds with an HTTP 200, indicating that the client will receive notifications on that channel.

HTTP/1.1 200 OK
Content-Type: application/vnd.ibm.jazz-json-notification-1.0

Client sends a request to the server

Now the client makes a request. The progress header is highlighted.

POST /service/com.ibm.team.filesystem.client.tests.internal.rest.services.IRestCancellationService/ProgressSynchronously HTTP/1.1
X-Secret-Key: 5D6A035927AA1883E10BF584A488EEA3
X-Request-Progress: auto.1220470793090.0
Host: 127.0.0.1:57640
Content-Length: 11
Content-Type: application/x-www-form-urlencoded

text=cheese

Server sends messages on the notification channel

The server sends repeated messages on the notification channel. The notification key is highlighted.

auto.1220470793090.0 progress 213 {
(unnecessary fields elided)
    "cancelled": false,
    "progress": 0.0,
    "subTask": null,
    "taskName": "cheese"
}
auto.1220470793090.0 progress 214 {
(unnecessary fields elided)
    "cancelled": false,
    "progress": 20.0,
    "subTask": null,
    "taskName": "cheese"
}
...

Server responds to request

The server will eventually respond to the request. From this point, any notifications that the server sends with this key should be considered stale.

HTTP/1.1 200 OK
Content-Type: text/json; charset=utf-8
Content-Length: 496

{"soapenv:Body":{"_eQualifiedClassNa...

Services Providing Notifications

If you want your service to provide notifications (outside of just progress), you have two things to do:
  1. Get the {@link IServerNotificationChannel} for your service on startup.
  2. Call {@link IServerNotificationChannel#queueNotification(String, String, IParameterWrapper)} to send notifications.

Getting the {@link IServerNotificationChannel}

If your service implements {@link IJSONReceiver}, the setController() method will be called when your service is registered:
public class MyRestService implements IMyRestService, IJSONReceiver {
    IServerNotificationChannel notifier;

    public void setController(IServerController controller, JSONHandler handler) {
        this.notifier = handler.getNotificationChannel();
    }
    ...

Note that the notifier remains valid over the life of the class, regardless of the number of listeners on that channel.

Sending Notifications

To send notifications, your service needs to know the key that listeners will be waiting on. You can either use a well-known key that is agreed upon at compile time, or you can dynamically assign a key. Dynamically assigning a key is more complex (the client needs to create it, then send it in a call to a service), but it allows for finer grained control.

Lifecycle of a Notification Channel

A client should maintain its connection to the notification channel whenever it expects to receive a notification. If the connection is broken, the client may reconnect immediately.

When the FSD is shutting down, it will send a shutdown notifiction to all of its listeners. After the client has received a shutdown notification on a connection it should not attempt to reconnect. The shutdown notification key can be found in {@link OrderlyShutdownNotification#KEY}, and the notification class is {@link OrderlyShutdownNotification}.