Flex
Using XMLSocket for Server Push in Flex
How to program a server push in ActionScript and Java
Aug. 9, 2007 06:15 PM
Adobe's LifeCycle Data Services ES offers developers powerful capabilities. Some of this software’s capabilities, specifically server push, can be developed with relative ease using a utility class that has been around in the Flash APIs for a great while, and without having to pay licensing fees for LifeCycle Data Services ES. The utility class spotlighted in this article is flash.net.XMLSocket, and it provides all the client-side plumbing required for implementing server push. In the context of this article, server push is defined as the ability for a client to register with a server its interest in remote data events such that when an event occurs, a notification (and perhaps related data) is pushed from the server out to the client without the client implementing a polling mechanism.
The naming of the XMLSocket class is easily misconstrued because there is no physical enforcement within the class implementation that ensures data are in an XML format. However, in the scope of this article, server push is implemented by sending XML over the wire. Firstly, the Flex APIs offer a rich set of XML parsing capabilities with E4X. Second, tools and techniques to manage XML document creation are very mature at this point, thus allowing the developer to design, document and communicate the wire level format in a straightforward manner. Lastly, debugging is much easier with a tunnel when the stream under monitor is textual, i.e. XML, and not binary.
Defining the XML Protocol
Defining the protocol that the client and server will follow is the logical place to start the design. This can be a time consuming effort; the best heuristic is to keep it simple. In most cases, defining a simplified protocol that relies on key-value pairs offers maximum flexibility. Defining a more advanced protocol with namespaces and all-things-XML certainly may increase the capabilities, but not without a price. Consider the level of effort required to develop custom parsers, the actual performance impact at runtime for parsing deeply nested XML documents that require validation, etc.
The following simplistic protocol is going to be used to demonstrate server push in this article.
<register event=”<event name>”/>
<response success=”<true | false>” error=”<error text>”/>
<event name=”<event name>”>
<property name=”<property name” value=”property value”/>
…
</event>
<quit/>
<shutdown/>
The client informs the server of its event interests through the register element. The server will acknowledge that event registration with a response element. If the event is well known, the response will indicate success, otherwise it will indicate failure with an appropriate error string. When the server detects an event of that type, it will push the related data out to the client using the event element. Finally, the client has the power to either quit, leaving the server running for other clients, or shutdown the server in its entirety. Once the protocol has been fully defined, focus turns towards client and server implementations. Which one is implemented first is a matter of personal preference.
The high level steps to implementing the client-side aspects of server push are as follows:
1. Instantiate a new XMLSocket instance
2. Register event listeners to respond when data events are received
3. Establish a connection with the server.
4. Register one or more events with the server
5. Implement the listener function to act when data events are received
Implementing the server-side logic requires substantially more work then that required to implement the client. There are many more complex issues at stake. At a minimum, the server needs to be multi-threaded. How will the server be managed, perhaps JMX? What are the logging facilities going to look like? What about authentication and authorization issues? These issues, and more, are likely to crop up during the design of the server. For the purpose of this educational article, complex issues like multi-threaded design, authentication, etc. will be sidestepped. (I do, however, cover some of these issues in greater detail over at my blog ) Only a brief review of the Java code associated with this article will be done since this is, after all, a Flex oriented article. Also, Java is but one implementation option for the server—it could be written in Ruby, Python, .NET, etc.
All of the code can be downloaded from http://www.beadlefox.com/xmlsocket.zip
Server Architecture Review
The Java server is implemented in the SimpleServer class. This class relies on the ServerSocket class to listen for and accept client requests. When the server is instantiated, it spawns a separate thread that generates random events using an instance of RandomEventGenerator. For purposes of this demo, the “events” are simply named A, B, and C and the generator uses Math.random() as its generation algorithm. The generator maintains what equates a list of listeners for each event. When the event is generated, the listeners list is iterated and each listener is notified. Notification is encapsulated behind the RandomEventListener interface that defines an onEvent() method. In a more realistic example, the listener would subscribe to JMS queues or topics, and the onEvent() method would be replaced with the JMS onMessage() method. However, for this short article incorporating a JMS server like ActiveMQ would be overkill and arguably clutter the example code more then is required, especially since non-Java servers are not going to use the JMS APIs.
When a client establishes a connection with the server, a new dedicated thread implemented by the ClientThread class is spawned. This thread will remain active until the client either quits or some client issues a shutdown command that terminates the server. The thread responds to the XML sent to it from the client appropriately. In particular, if the thread detects a register event it contacts the RandomEventGenerator and registers itself as a listener for the event named by the client. At this point, the thread acknowledges the event registration back to the client per the protocol design, and then waits. When an onEvent() is received by the thread, it sends the event notification over to the client via server push. This is the true power of this architecture since the client doesn’t have to implement a loop or perform any type of polling operation. The only thing the client has to do is register an event listener.
When sending data from the server back to the client, it is imperative that the response be properly delineated. The Flash Player seems to require a very specific termination sequence of a carriage return followed by a null byte (“\r\0”) before the XMLSocket dispatches the DataEvent for processing. If this sequence isn’t included, the client side event is never fired!
To launch the server, invoke the following command with an appropriate “/” or “\” if the OS is Windows or *nix inside of the ./src/SimpleServer directory:
/src/SimpleServer $java -cp ./bin com.beadlefox.xmlsocket.SimpleServer
Client Architecture Review
The Flex client is implemented in the XmlSocketClient class. The UI is meant to be educational, so it is oversimplified with 5 buttons used to connect, register and shutdown the remote server. The server must be up and running at this point in order for the Flex UI to connect successfully. This application implements the 5 steps outlined earlier. The first 3 steps are implemented in the application’s connectToServer() function.
//
// The XMLSocket instance
//
private var socket:XMLSocket;
private function connectToServer():void
{
//
// Step #1: Create the XMLSocket Instance
//
socket = new flash.net.XMLSocket();
//
// Step #2: Register for event notifications
//
socket.addEventListener(Event.CONNECT, connectHandler);
socket.addEventListener(DataEvent.DATA, dataHandler);
//
// Step #3: Create the physical connection with the server
//
socket.connect("localhost", 5974);
}
The 4th step occurs when the register() function sends the XML request that indicates what event(s) the client wants to be notified of via server push. Notice the string includes a \n which is required by the server since readLine() is used in the Java code. Once the XML string has been assembled, passing it to the XMLSocket send() method automatically transmits the message over the wire.
//
// Step #4:
// Registers with the server that this client
// is interested in the named event
//
private function register(eventName:String):void
{
var request:String = "<register event=\"" + eventName + "\"/>\n";
socket.send(request);
}
Finally, when the server sends data over to the client, the listener function implemented as the 5th and final step processes and responds appropriately. In this case, it merely pushes the data object into the data provider for display on the screen.
//
// Step #5:
// Process the data sent to the client from the server
//
public function dataHandler(event:DataEvent):void
{
//
// Data has been received from the server
//
var xml:XML = new XML(event.data);
//
// What is the name of the root element?
//
var element:String = xml.name().toString();
if(element == "response")
{
//
// Swallow this event - for demo just
// assume success
//
} else if(element == "event")
{
//
// Push the event to our data provider
//
var obj:Object = new Object();
obj.id = nextId++;
obj.eventName = xml.@name;
obj.timestamp = xml.property.@value;
events.addItem(obj);
}
}
The DataEvent’s data property contains the XML sent over from the server. Using E4X, the listener inspects the root element to determine how it should respond to the data it just received. In a more powerful architecture, an Abstract Factory could be used to dynamically instantiate a concrete class based on the root element. This concrete class would then know how to fully parse and respond to the event, creating the opportunity to respond to a limitless number of XML document structures.
The rest of the MXML and ActionScript in the sample is straightforward—buttons with click methods, a data grid to display the received data, etc.
Conclusion
This sample demonstrates that with very little work on the Flex side it is possible to harness the power of server push without incurring the licensing costs of LifeCycle Data Services ES. The benefit of server push is that no client polling is required; this also reduces the load of the server because it doesn’t have to constantly respond to clients checking for new data. Beyond the protocol design, which is implementation language agnostic, the most complicated effort lies in designing the multithreaded server. The decision to implement a server or license Adobe’s solution is likely ultimately decided based upon the situation. If only server push is required, a home grown solution with XMLSocket may be the most cost effective approach, and the implementation may only require a couple of man hours of effort.
After Thoughts: Server Push and Tomcat
The Tomcat server is widely deployed in production environments today. Its architecture is completely pluggable, including its Connectors that know how to process and dispatch wire-level traffic. I’m looking to find some interested people that want to help me launch a new open source project with an Apache style license to implement a new Tomcat Connector capable of providing initially server push capabilities to a Flex/Flash client. For those looking for more information on what I’m calling the XSock Protocol, including why I think there is a need for this Tomcat ProtocolAdapter, please visit http://www.xsock.org. This is truly the beginning of the project, so it’s a great opportunity to get in early and be a major contributor!
About Jason WeissJason Weiss has over 12 years of real-world design and development experience. He's a published author and frequently lectures at industry trade shows.