Wednesday, February 15, 2012

An Implementation

Get caught up:
Part 1: http://randomgoo.blogspot.com/2012/02/release-event-hub-web-30-introduction.html
Part 2: http://randomgoo.blogspot.com/2012/02/events.html
Part 3: http://randomgoo.blogspot.com/2012/02/event-listeners-get-up-to-speed-first.html
Part 4: http://randomgoo.blogspot.com/2012/02/event-hub.html
Part 5: http://randomgoo.blogspot.com/2012/02/deploying-event-hub-application.html

Quick recap - Web 3.0 is two-way asynchronous programming - events on the way out and another event or a callback on the way back.

I have cobbled together an Event Hub implementation here: https://github.com/zzo/EventHub.
This implementation happens to be in Javascript and it built upon socket.io.  Which means besides being very easy to add to client-side modules, there is support for many languages on the server side including Erlang, Lua, Java, Perl, Go, Python, Ruby, Flash, and of course Javascript.  See the socket.io wiki for details.  The Event Hub itself is also available as an npm package.

The actual Event Hub code is very small.  It actually uses the connect frame work for transport and serves the static socket.io and Event Hub client libraries itself (there are currently 2, one for YUI and another for jQuery - feel free to add more!).  There is also a NodeJS client-library for NodeJS client to connect to the hub.  Check out the examples/ directory for how to use all of this stuff.

You have a couple choices when starting server-side event hub modules.  They can either all be completely separate processes, which is cleanest.  Each server-side module would sit on its own port connected to the event hub completely independent of each other.  They can all be on separate hosts, however you want to do it - they are all completely separate from each other.
You can also start them all up in one shot and multiplex their port amongst them.  While this is perhaps easier to do initially, if you need to restart any one of them you will need to restart all of them.

Let's look at some examples!

Here is a NodeJS EventHub client:

var eventHub = require('eventClient.js')
                     .getClientHub('http://localhost:5883?token=SECRET');

eventHub.on('eventHubReady', 
    function() {
        eventHub.on('HelloWorld', 
            function(data, callback) {
                // SESSION key is in data['eventHub:session']
                callback(null, { mark: 'trostler' });
             }
            , { type: 'unicast' }
        );

        eventHub.on('eventClient:done', 
            function(event) {
                console.log('DONE LISTENING FOR ' + event);
                // wait for us to finish processing any current
                //    event and then....
                process.exit(0);
            }
        );
    }
);

So we connect to the hub using the secret key ('SECRET' in this case). If we successfully connect (because the token is correct) we become a 'trusted' module and can listen for 'unicast' events.
If the connection is established we listen for a 'HelloWorld' event, telling the EventHub this is a 'unicast' event - so only send these events to us. That also means the Hub will sent us an 'eventClient:done' event if someone else comes along and starts listening for this event too - so we also listen for that event. If we get that event we shut ourselves down gracefully. And finally 'trusted' modules will also receive the session key of the client who emitted this event - and will be available to this modules in data['eventHub:session'] in the event data.
Upon receiving a 'HelloWorld' event, we do some processing and call back to the emitted using the supplied callback. We follow the convention that the first argument of the callback is an error object or string if there was an error, and whatever data in the second callback argument.

Now let's look at something that might emit this event:

<html>
<head>
</head>
<body>
 <script src="http://yui.yahooapis.com/3.4.1/build/yui/yui-min.js"></script>
 <script src="http://dashr.net:5883/socket.io/socket.io.js"></script>
 <script src="http://dashr.net:5883/yui3.js"></script>
 <button id="button">Press Me</button>
<script>
    YUI().use('node', 'EventHub', function(Y) {

        var hub = new Y.EventHub(io, 'http://dashr.net:5883');
        hub.on('eventHubReady', function() {
            var helloWorld = hub.addEvent('HelloWorld');
            Y.one('#button').on('click',
                function(event) {
                    helloWorld.fire({ button: 'clicked' },
                        function(error, data) { 
                            Y.log("callback from event listener!");
                        }
                    );
                }
            );
            hub.on('someOtherEvent', function(data, callback) {
                Y.log("Got some other event!");
                callback(null, { hello: "stranger" });
            });
        });
    });
</script>
</body>
</html>

Ok so check it out - first we load the YUI seed (we'll use the YUI EventHub client), socket.io and the YUI event hub library. Note we can load those last two scripts directly from the EventHub itself.
We then connect to the hub and when ready we use a convenience function in the client YUI library to create a 'shortcut' for the HelloWorld event we are going to fire.
So when someone clicks our button we fire the HelloWorld event and set up the callback for the response. At some point the callback comes back and Bob is your uncle.

Listening for events is just as easy, here I'm not using the convenience 'addEvent' method and just listening for 'someOtherEvent' and calling its callback function back when I get it.

The jQuery example is remarkably similar!

<html>
<head>
</head>
<body>
 <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.js"></script>
 <script src="http://dashr.net:5883/socket.io/socket.io.js"></script>
 <script src="http://dashr.net:5883/jquery.js"></script>
 <button id="button">Press Me</button>
<script>
    var hub = new $.fn.eventHub(io, 'http://dashr.net:5883');
    hub.bind('eventHubReady', function() {
        var helloWorldEvent = hub.addEvent('HelloWorld');
        $('#button').bind('click',
            function(event) {
                console.log('clicked on button - making new event for hub');
                console.log(clickEvent);
                helloWorldEvent.trigger({ button: 'clicked' },
                    function(error, data) { 
                        console.log("callback from event listener!");
                    }
                );
            }
        );
     
        // listen for some other event
        hub.bind('someOtherEvent',
            function(data, callback) {
                console.log('got someOtherEvent');
                console.log(data);
                callback(null, { mark: 'rox' });
            }
        );
    }); 
</script>
</body>
</html>

For jQuery we use the jQuery syntax of 'trigger' and 'bind' instead of 'fire' and 'on' but everything else is pretty much the same. We use the client utility method 'addEvent' for the HelloWorld event but don't for the 'someOtherEvent', totally optional to use the syntactic sugar 'addEvent' provides for you.

It's equally easy to add clients in other languages that socket.io supports - or any language with a Web Sockets library.

Finally starting the event hub is easy:

% node eventHub.js

For now you need to edit 'eventHub.js' itself if you want to change the port (5883) or the secret used to determine trusted clients.

If you installed EventHub using npm try:

% npm start EventHub

Have fun out there and stay tuned for even more!!

No comments:

Post a Comment