« Converting Mercurial To Git | Main | Introducing Shovel: An AMQP Relay »

Server Side AS3

Today I was asked to present something at Osmosoft's Show And Tell gathering, so I decided to write a small summary of the presentation. I was asked to talk about something to do with RabbitMQ, make it hip by using Github and because Osmosoft created TiddlyWiki, it would be cool if it were something funky that could be self-contained within a browser. So I came up with the idea of turning a Flash player into a server, hence freeing AS3 from it's client side only image.

Introduction

Being a developer of an open source messaging broker does not immediately qualify me to build funky and attractive applications. But I thought that if I stuck to the theme of the previous Show And Tell events, I might have a chance of not totally boring people to death.

As touched on above, Jeremy Ruston from Osmosoft created TiddlyWiki, which turns a browser into a full blown wiki. At the last installment of this event, Tony Garnock-Jones demonstrated his distributed version control system written entirely in JavaScript, which is a fantastically empowering component for browser-side applications. The theme that has been developing here is turning a standard browser into an application with server-like capabilities.

Keeping in the spirit of turning a web browser, which has traditionally been used as a client device, into a server device, I thought that I would write a server application in AS3 and serve it up in a browser.

The Demo Application

Although I had the entire canvas and possibilities of Flash within my reach, I decided to implement a simple Fibonacci number generator as a demo application. The Fibonacci generator connects to an instance of RabbitMQ using AMQP as it's communication protocol. To encode and decode the data suitable for transport over a wire and to be able to speak to non-Flash clients, I opted for the lightweight JSON serialization format. Clients can request the generation of a Fibonacci number by sending a message to a queue on RabbitMQ and waiting for a response via a reply queue.

Getting The Source

If anybody is interested in accessing the source code for this application, it is available in this Github repository.

Running The Application

First of all, you will need a running instance of RabbitMQ. Please refer to the friendly documentation at RabbitMQ about to install this.

I chose to run the server and a client from with the Flexbuilder IDE, although you can just run the compiled Flash films using the Flash Player. If you've downloaded the source, you can import the project into Flexbuilder and run the applications from there.

It's a good idea to run the server.mxml application in debug mode, because you will get the server log printed out to the console within Flexbuilder. When you run the server, a new browser window will open (screenshot not included), and you will see some log statements in the console indicating the server has successfully booted:

Thu Jul 3 22:11:15 - Starting AMQP JSON Server.....please stand by
Thu Jul 3 22:11:16 - AMQP JSON Server has booted and will now \
                     accept requests :-)

Once this is running, you can start the client by running the client.mxml application, and a new browser window will open within which you can request a Fibonacci number:

Unless the AS3 server is running a machine with a very high spec, it is probably not a good idea to enter a number that is too large. On a Core Duo 2.33 on OSX 10.5 for example, the highest input that the Flash Player could handle without timing out was 35. Your milage may vary, but the demo was designed to illustrate a concept rather than high performance computations.

If you look back at the server logs, you will see that the server has logged all of the requests from the client:

Fri Jul 4 08:53:58 - Received {"number":15} as input, returning \
                     {"question":15,"answer":987}

Discussion Of The Implementation

The implementation of the server is quite simple:

public class Fibonacci implements RequestHandler
    {

    public function process(o:*):* {
        const n:int = o.number;
        var response:* = new Object();
        response.question = n;
        response.answer = fib(n);
        return response;    
    }

    function fib(n:int):int {
        if (n < 2) return 1;
        else return fib(n-1) + fib(n-2);
    }
}

The RequestHandler interface specifies a callback mechanism by which a generalized AMQP listener can dispatch requests to some meaningful service implementation. This is all glued together by dependency injection using the Prana DI framework so that the resulting server.mxml remains equally as simple:

<mx:Script>
    <![CDATA[
        import com.squarespace.hopper.json.demo.Fibonacci;
        import com.squarespace.hopper.json.impl.AMQPServer;
        import com.squarespace.hopper.json.util.ServerContext;
        import org.pranaframework.ioc.factory.xml.XMLObjectFactory;

        private var amqpServer:AMQPServer;
        private var fib:Fibonacci;
        private var factory:XMLObjectFactory 
                    = new XMLObjectFactory();
        private var ctx:ServerContext = new ServerContext();

        private function onCreationComplete():void {
            factory.addConfig(ctx.getXML());
            factory.load();
            factory.getObject("server");                
        }
    ]]>
</mx:Script>

This approach is essentially the same as loading a Spring configuration file. In fact, the syntax for Prana is very similar to that of Spring. When the server.mxml application is started, it initializes and wires together the following bits of plumbing:

<object id="connectionState" class="org.amqp.ConnectionState">
    <property name="username" value="guest"/>
    <property name="password" value="guest"/>
    <property name="vhostpath" value="/"/>
    <property name="serverhost" value="localhost"/>
</object>

<object id="connection" class="org.amqp.Connection">
    <constructor-arg ref="connectionState"/>
</object>

<object id="server" 
    class="com.squarespace.hopper.json.impl.AMQPServer">
    <constructor-arg ref="connection"/>
    <property name="realm" value="/data"/>
    <property name="exchange" value="x"/>
    <property name="exchangeType" value="topic"/>
    <property name="bindingKey" value="*"/>
    <property name="requestHandler">
    <object class="com.squarespace.hopper.json.demo.Fibonacci"/>
    </property>
</object>

Here you can see that a connection to an AMQP broker has been configured as well as our application specific request handler, the Fibonacci class. These objects are initialized lazily, so when factory.getObject("server") is called, the AMQP server is booted with the Fibonacci component wired in. It is then ready to commence processing client requests.

The Client View

Because the server speaks JSON over AMQP, a client written in pretty much any language could be used. In this example, I'm going to stick with AS3, because it works as a client side language just as well as it does on the server side.

The client side configuration is very similar to that of the server side, in that the configuration for the connection to the AMQP broker is contained in a Prana application context file. In turn, this makes the client.mxml application equally as simple as the server:

<mx:Script>
    <![CDATA[
    private var proxy:AMQPClientProxy;

    private function onCreationComplete():void {
        factory.addConfig(ctx.getXML());
        factory.load();
        proxy = factory.getObject("client");
    }

    public function submitRequest():void {
        var o:* = new Object();
        o.number = new Number(input.text);
        proxy.send(o,onResponse);   
    }

    public function onResponse(event:JSONEvent):void {
        var result:int = event.result.question;
        // display the result somewhere
    }
</mx:Script>

I've left out the strictly GUI related code for brevity's sake. When the application is booted, it configures a connection to the broker in a fashion analogous to the previously discussed server configuration:

<object id="client"
        class="com.squarespace.hopper.json.impl.AMQPClientProxy">
    <constructor-arg ref="connection"/>
    <property name="connection" ref="connection"/>
    <property name="realm" value="/data"/>
    <property name="exchange" value="x"/>
    <property name="exchangeType" value="topic"/>
    <property name="routingKey" value="doesn't matter"/>
</object>

Whenever a GUI event needs to trigger a server request, it uses the send function on the client proxy and registers a callback handler to process the server response.

And that's pretty much the whole application.

Outlook

So I think I've managed to achieve the set out objectives:

  • Talk about something to do with RabbitMQ;
  • Make it hip by using Github and;
  • Do something funky that could be self-contained within a browser.

This particular example demonstrates how to turn a Flash application into a server application, which keeps in the spirit of producing browser based applications that were traditionally thought to run only on big server machines. Whilst this example may be a bit contrived from a practical perspective, its development has been useful in fleshing out a high level abstraction for the low level AMQP library for AS3. This work can potentially be folded back into the as3amqp code base.

Acknowledgements

This demo made use of the JSON implementation for AS3 from the as3corelib library as well as the flexunit framework for unit testing.

Posted on Friday, July 4, 2008 at 01:22PM by Registered Commenter0x6e6562 in , | CommentsPost a Comment

Reader Comments

There are no comments for this journal entry. To create a new comment, use the form below.

PostPost a New Comment

Enter your information below to add a new comment.

My response is on my own website »
Author Email (optional):
Author URL (optional):
Post:
 
Some HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>