6. Design of the Framework

THIS STILL REFLECTS VERSION 1 (REWORKING...).

The Pushlet framework allows clients to subscribe to subjects within a server from which they subsequently receive events. The framework's basic design pattern is Publish-Subscribe also known as Observer and has both server and client components:

6.1. Server-side class design

Below is the UML class diagram of the server-side Java classes [reflects v0.0.4; rework required].

Figure 1. Pushlet Framework Class Diagram.

The key classes are the Pushlet servlet, the Publisher class, Subscriber interface, and the Event class (see class diagram). By invoking the Pushlet servlet through an HTTP request, clients subscribe to receive Events. In the request is indicated:

An example request for receiving AEX stock rates formatted as JavaScript through an HTTP response stream would be:

http://www.fluidiom.com:8080/servlet/pushlet?subject="/stocks/aex"

Subject identifiers are organized as an hierarchical "topic-tree". For example, "/stocks" identifies all Events related to stock rates, while "/stocks/aex" identifies stock rates for the Amsterdam EXchange. Likewise the subject "/" indicates all events.

Currently the only receiver protocol is a client HTTP response stream. In a future extension also the receiver protocol and address can be indicated, e.g. TCP, UDP, RMI, HTTP POSTing, or even SMTP (email).

An Event is merely a set of name/value String pairs (implemented with java.util.Properties). The Publisher has an interface through which classes that generate Events can publish them. The Publisher keeps a list of Subscribers and sends each Event to those Subscribers whose subject matches to the Event's subject. Events may be originating within the server through EventGenerators who may listen to external events such as a stock feed. In addition clients may publish Events through HTTP with the Postlet class. The responsibilities of other classes in class diagram, PushletSubscriber and its contained classes can best be explained through scenario's.

6.1.1. Scenario: Event Subscription

Figure 2. Subscribe Sequence Diagram.

Above is the UML sequence diagram for a browser client subscribing for events from the Publisher. The Pushlet is invoked with the servlet method doGet(). Because multiple clients may invoke the same Pushlet object, it should itself not be a Subscriber. Instead it delegates all subscription (and subsequent Event handling) by creating a new PushletSubscriber object for each doGet() and letting it run until finished with eventLoop(). The PushletSubscriber is a Subscriber object towards the Publisher where it registers with the join() method. To deal with different client formats and protocols it creates a specialized ClientAdapter object, in this case a BrowserPushletAdapter. For browsers supporting Multipart MIME such as Netscape 4+, this would be a MultipartBrowserClientAdapter. The final call in this scenario is a "wait for event loop". Note that deQueue() is a method that suspends the execution of the current thread until an Event becomes available (indicated with half-arrow). This is explained through the next scenario.

6.1.2. Scenario: Sending and Dispatching Events

Figure 3. Publish Sequence Diagram.

Above is the UML sequence diagram for sending an Event. It shows how an Event is generated and dispatched to the browser client. In this scenario an EventGenerator creates an Event and calls Publisher.publish() to have it dispatched. The Publisher walks through its list of Subscribers and asks each if the Event matches its subscription criteria (currently only the subject). If it matches it calls send() on the Subscriber.

Each PushletSubscriber object has a GuardedQueue object in which it queues incoming Events when send() is called. So why isn't it just directly pushing the Event to the BrowserPushletAdapter ? First of all we want to suspend execution of the BrowserPushletAdapter-thread until an Event becomes available, i.e. we don't want to do a "busy-wait" (a.k.a. as polling). The second reason is that a Publisher may notify multiple clients. Having a synchronous send() call a slow client on the other end of the line may block all other clients that are to be notified next. This is actually a design pitfall which I see also in RMI or CORBA callback examples where a list of clients is called back synchronously. Client #13 on a slow connection and 386 processor may spoil it for the rest.

The GuardedQueue is a utility object that allows Objects to be en/dequeued using the readers-writers pattern with guarded suspension using java.lang.Object.wait() and notifyAll(). The thread of a client of GuardedQueue calling deQueue() will be suspended (using wait()) until there is an Object queued. Likewise a client enQueueing an Object will be suspended as long as the queue is full. When clients are fast enough the GuardedQueue is never filling up. After the BrowserPushletSubscriber has dequeued an Event object it will call push() on the BrowserPushletAdapter who will format the Event to a JavaScript element and send it to the browser. For example for a Philips stock rate of 123.45 the JavaScript element looks as follows.

<SCRIPT language=JavaScript >parent.push('subject', '/stocks/aex', 'philips', '123.45') </SCRIPT>

6.2. Client-side framework

By now we have arrived on the client browser side. The Pushlet itself was assigned to a hidden HTML FRAME. The parent of that FRAME is called and has to implement the push() method. Since this is a common task for all browser clients, two reusable files are provided for the client: pushlet.html (see /src/nl/justobjects/pushlet/pushlet.html) and pushlet.js (see /src/nl/justobjects/pushlet/pushlet.js).

pushlet.html is meant to be included in a FRAME within the application-specific client HTML document. It can be parameterized with the subject identifier and a background color (such that it remains invisible). The most important thing it does is implementing the JavaScript push() method as follows:

function push() { // Create a PushletEvent object from the arguments passed in // push.arguments is event data coming from the Server pushletEvent = new PushletEvent(push.arguments) // Show blinking light as data is coming in updateStatusFrame(); // Is parent ready to receive events ? if (!parent.onPush) { return; } // Forward the event to the parent frame who should do application // specific handling of the event parent.onPush(pushletEvent); }

The function push() first creates a JavaScript object from the parameters passed in. Yes you can do object-based programming in JavaScript. Reminiscent of 'varargs' in C/C++, JavaScript functions may have variable number of arguments. A PushletEvent object is created with whatever arguments were passed to push() from the server. PushletEvent is implemented in pushlet.js shown next.

/* Object to represent nl.justobjects.pushlet.Event in JavaScript. Arguments are an array where args[i] is name and args[i+1] is value */ function PushletEvent(args) { // Member variable setup; the Map stores the N/V pairs this.map = new Map(); // Member function setup this.getSubject = PushletEventGetSubject this.put = PushletEventPut this.get = PushletEventGet this.toString = PushletEventToString this.toTable = PushletEventToTable // Put the arguments' name/value pairs in the Map for (var i=0; i < args.length; i++) { this.put(args[i], args[++i] ); } } // Get the subject attribute function PushletEventGetSubject() { return this.map.get('subject') } // Get event attribute function PushletEventGet(name) { return this.map.get(name) } // Put event attribute function PushletEventPut(name, value) { return this.map.put(name, value) } function PushletEventToString() { return this.map.toString(); } // Convert content to HTML TABLE function PushletEventToTable() { return this.map.toTable(); }

pushlet.js in turn uses a Map JavaScript object, a java.util.Hashtable-like object I've added.

Next push() calls updateStatusFrame() to show a blinking light to indicate we are still receiving events and if a parent.onPush() function exists, it calls it with the PushletEvent. parent.onPush() is the application-specific event handling function that in this case may update the 'philips' stock-related Layer in a DHTML page.

This ends the description of the basic framework design.