Event-driven archi­tec­ture is a big topic these days and with good rea­son. Aside from pro­vid­ing sig­nif­i­cant oppor­tu­ni­ties to increase scale and per­for­mance, given the right approach it can also make it eas­ier to divide sys­tems up into log­i­cal com­po­nents. The basic prin­ci­ple behind “event-driven” archi­tec­tures, as one might expect from the name, is asyn­chronic­ity: rather than view­ing your appli­ca­tion as a rigid sequence of processes, each being called in order as the result of a user action or sched­uled exe­cu­tion, com­po­nents pub­lish seman­tic events to which one or more other com­po­nents may be sub­scrib­ing and to which they may exe­cute logic (and per­haps pub­lish sub­se­quent events) in response. So, as you can prob­a­bly tell, like most buzz­words, “event-driven” really just tries to put a catchy name to an age-old con­cept; in this case “publish-subscribe” — but it does so, I would sug­gest, with the intent of help­ing devel­op­ers to get their head into the right space to think about how to actu­ally build sys­tems that are con­structed around such a mechanism.

In Java-land, we are for­tu­nate to have a sta­ble API with a num­ber of proven imple­men­ta­tions for publish-subscribe mes­sag­ing; namely: JMS, my favourite provider for which is Apache’s ActiveMQ. This is all well-and-good for han­dling our needs in this area on the server-side, but what about the user (sit­ting there with their browser) as an actor in this sys­tem? This ques­tion becomes very impor­tant when you con­sider that, described in abstract terms, the pri­mary use case, in my expe­ri­ence, for event-based mes­sag­ing sys­tems comes when you have users sub­mit­ting batches of work that demand inten­sive pro­cess­ing and require noti­fi­ca­tion of its completion.

In the past, one might have han­dled such sit­u­a­tions syn­chro­nously: the user would make the request through their browser, they would then sit and watch the “wheel of death” go around and around until, even­tu­ally, hope­fully, the request would be com­pleted. Of course, as soon as you got a few users doing this at the same time, your server became over­loaded and the whole thing ground to a halt. So you might have got as far as mak­ing this pro­cess­ing asyn­chro­nous, but then user instead would have to resort hit­ting the refresh but­ton and going “Is it done yet? Is it done yet? Is it done yet?” … or, worse, you would resort to that bête noire of office “noti­fi­ca­tion meth­ods”: email. Yuck!

Nowa­days, this kind of behav­iour is totally unnec­es­sary. Mak­ing the browser part of your event-driven sys­tem is a dod­dle and requires zero-alterations to your exist­ing JMS-based code (assum­ing that is what you are using). In this post, I would like to pro­vide a really basic exam­ple of how to send and receive JMS mes­sages using Active MQ’s Ajax sup­port. (Whilst there are bet­ter solu­tions on the hori­zon — most notably, in my view, Web­Sock­ets and STOMP — I have found them more than a lit­tle bit painful to inte­grate with exist­ing code to date, so I will focus on the more sta­ble solu­tion for the time being.)

Let’s begin by assum­ing that we have an appli­ca­tion that con­tains 1 “pro­cess­ing queue” and 1 “event topic”. With ActiveMQ, I can setup both a mes­sage bro­ker, the queue and the topic in a Spring appli­ca­tion con­text file:

    <amq:topic id="eventTopic" name="topic.events" />

    <amq:queue id="processingQueue" name="queue.process" />

    <amq:connectionFactory id="connectionFactory" brokerURL="vm://localhost" />

    <bean id="eventTopicTemplate" class="org.springframework.jms.core.JmsTemplate">
        <property name="connectionFactory" ref="connectionFactory" />
        <property name="defaultDestinationName" value="topic.events" />
    </bean>

Now, let’s say we have some kind of “ser­vice” that is respon­si­ble for doing this “inten­sive” asyn­chro­nous pro­cess­ing. The ser­vice will pick up mes­sages from the pro­cess­ing queue and, when the pro­cess­ing is com­plete, it will pub­lish a mes­sage to any inter­ested sub­scribers on the event topic say­ing that pro­cess­ing is com­plete. The API might look some­thing like this:

package com.christophertownson.foo;

public interface SomeService {

    void doProcessing(String msg);

}

For now, let’s make the most basic imple­men­ta­tion possible:

@Service("someService") public class SomeServiceImpl implements SomeService {

    private final JmsTemplate jms;

    @Autowired public SomeServiceImpl(@Qualifier("eventTopicTemplate") JmsTemplate eventTopicTemplate) {
        jms = eventTopicTemplate;
    }

    public void doProcessing(String msg) {
        jms.convertAndSend("We did something with your message: " + msg);
    }

}

All our imple­men­ta­tion does for now is send a mes­sage to the event topic to indi­cate that pro­cess­ing is “com­plete”. Any num­ber of com­po­nents may sub­scribe to this topic. In our case, the browser will ini­ti­ate pro­cess­ing requests by send­ing mes­sages to the pro­cess­ing queue and will sub­scribe to the event topic so that it can be noti­fied when the pro­cess­ing is com­plete and dis­play this to the user. But first we will wire up our ser­vice imple­men­ta­tion so that it will receive mes­sages sent to the pro­cess­ing queue:

    <jms:listener-container connection-factory="connectionFactory">
        <jms:listener destination="queue.process" ref="someService" method="doProcessing" />
    </jms:listener-container>

And that is it for the JMS-stuff on the server-side. Now let’s turn our atten­tion to the browser. I am going to assume that our user is inter­act­ing with our appli­ca­tion with some kind of “dash­board” web page. I am going to make my extremely beau­ti­ful (not!) dash­board look like this:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title>Dashboard : Browser JMS Demo</title>
        <link rel="stylesheet" href="/resources/style.css" />
        <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.3/jquery.min.js" type="text/javascript"></script>
        <script type="text/javascript" src="/resources/amq_jquery_adapter.js"></script>
        <script type="text/javascript" src="/resources/amq.js"></script>
        <script type="text/javascript" src="/resources/behaviour.js"></script>
    </head>
    <body>
        <header>
            <hgroup>
                <h1>Browser JMS Demo Dashboard</h1>
            </hgroup>
        </header>
        <section id="send">
            <hgroup>
                <h1>Make something happen</h1>
                <form id="sendMessage" action="/process" method="post">
                    <fieldset>
                        <legend>Send a message to the processing queue.</legend>
                        <label for="msg">Message</label> <input type="text" id="msg" name="msg" maxlength="255" /> <input type="submit" value="Send" />
                    </fieldset>
                </form>
            </hgroup>
        </section>
        <section id="receive">
            <hgroup>
                <h1>Messages sent to the event topic</h1>
                <ol id="messages">
                </ol>
            </hgroup>
        </section>
        <footer>
            <p>Copyright &copy; Christopher Townson 2011</p><!-- yeah, that's right maaan: hands off my design! :p -->
        </footer>
    </body>
</html>

Note the use of the ActiveMQ JavaScript files on lines 8–9: these are pro­vided as part of any ActiveMQ dis­tri­b­u­tion pack­age, if you were won­der­ing where they come from. “Adapters” are also pro­vided for Pro­to­type and Dojo, but I am using JQuery in my exam­ple so opted for the JQuery adapter.

I will assume for now that you know how to setup some kind of con­troller to deliver this HTML file and will not go into the details of that here. As usual, I am using a Spring-Velocity setup to achieve this. How­ever, in this instance, that is not impor­tant because “con­trollers” are not really being used at all in this example.

So, let’s take a look at that behaviour.js file ref­er­enced on line 10. This is where I am putting the impor­tant stuff to actu­ally send and receive JMS messages …

function init() {
    var amq = org.activemq.Amq;
    amq.init({
        uri: 'amq',
        logging: true,
        timeout: 20
    });

    amq.addListener('theBrowser', 'topic.events', function(msg) {
        $('#messages').append('<li>' + msg.textContent + '</li>')
    });

    $('#sendMessage').submit(function() {
        var msg = $('#msg').val();
        amq.sendMessage('queue.process', msg);
        $('#sendMessage').after('<p id="ack">Sent message: "' + msg + '"');
        $("#ack").fadeOut(2000, function () {
            $("#ack").remove();
        });
        return false;
    });
}

$(document).ready(init);

Let’s go through this step-by-step …

  1. On lines 2–7, we instan­ti­ate and ini­tialise ActiveMQ.
  2. On lines 9–11, we setup lis­ten­ing for mes­sages on the event topic, pass­ing a client ID, the name of the des­ti­na­tion we want to lis­ten to (i.e. the event topic in this case) and a call­back to exe­cute when a mes­sage is received … I am just going to add the mes­sage to a list on the page for now.
  3. On lines 13–21, I inter­cept mes­sage form sub­mis­sions, send­ing a JMS mes­sage rather than con­tin­u­ing with a nor­mal HTTP form sub­mis­sion. As a lit­tle piece of sugar, I have a fade ani­ma­tion alert to say that the mes­sage has been sent after you sub­mit the form. (In real life, you should make this degrade grace­fully by hav­ing a han­dler on the server-side to send the JMS mes­sage and mak­ing your JavaScript code per­form an asyn­chro­nous request to that han­dler, rather than send­ing the mes­sage directly as I am doing here.)

The final piece of the jig­saw puz­zle is the ActiveMQ AjaxServlet. This is part of the activemq-web, which you will need to declare as a depen­dency in addi­tion to the usual activemq-core. Then we just need to declare this servlet in our web.xml as follows:

    <servlet>
        <servlet-name>amq</servlet-name>
        <servlet-class>org.apache.activemq.web.AjaxServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>amq</servlet-name>
        <url-pattern>/amq/*</url-pattern>
    </servlet-mapping>

And, Lo and behold! Our dash­board page receives event noti­fi­ca­tions in the page with­out reload. Isn’t JavaScript magic?! Now, of course, there are all kinds of sub­tleties and sophis­ti­ca­tions that one can intro­duce, includ­ing mes­sage fil­ter­ing, brows­ing, con­ver­sion and so on. How­ever, first you need to con­sider care­fully what type of mes­sages you are send­ing to the browser. Ide­ally, in my view, these should nor­mally only con­sti­tute events that lead a call­back han­dler to update the data dis­played about some under­ly­ing per­sis­tent entity (because you don’t get these mes­sages again when the page is reloaded, obvi­ously). Also, there is much to con­sider about the for­mat of such mes­sages: using JSON as a JMS text mes­sage for­mat, rather than map or object mes­sages, makes a lot of sense when you look at things from this angle because it is then very easy to serialize/deserialize mes­sages in both the browser and in a MessageConverter in your Java code. Finally, as with all event-driven sys­tems, pay very close atten­tion to the point in time at which mes­sages are sent. For exam­ple, if, as I sug­gest, you are using these events to trig­ger an update to dis­played per­sis­tent state, you need to be damned sure that this state has actu­ally been updated before you pub­lish the “update your dis­play” event.

You can down­load the Maven project with the com­plete code for this demo. You can, of course, dis­trib­ute and change it in any way you like … but do share the results if you do. Enjoy!