flexlogo.pngThe complete story of combining a lot of different technologies and a lot of different blog post and articles found on the web. There are multiple good posts around this topic, but none of them gave the exact solution I needed. Therefore I write this blog post, maybe someone else can use this as well.

Picture 1.pngTo get an idea if this post is about the approach you need, let me describe the problem or challenge I am facing.

I have an application that is showing the current stock supply of an online shop. Because obtaining all stock items takes a few seconds, I want to notify users when stock changes. To be able to do this I want to communicate changes from a java server using spring technology to a flex client through BlazeDS. The amount of users is not very big, and the changes to the stock are not important when someone obtains all stock items. Therefore the messages do not need to be persisted and we can use an in memory message server implementation provided by ActiveMq. So the flow becomes:

  • Spring sends a message to a topic.
  • BlazeDS pics up the message and transforms the stock payload
  • All flex clients gets notified of the message

Go on to read about the solution.

Technology stack

The project uses a normal architecture, web layer, business layer and data layer. There is an integration component for xml export. The jms part is an integral part of the business layer, maybe I move it to the integration layer. But that does not have influence on the solution. Now we focus on the most important components. The web component and the business component.

Web/BlazeDS project

BlazeDS is used as the remoting solution for the flex clients. The flex clients send requests to the server. BlazeDS runs on the server, receives the requests and using spring factories calls the spring beans. For our solution we need to do it the other way around. From the server we want to notify all connected clients. Using BlazeDS, this is done with JMS. BlazeDS has an adapter to publish and subscribe to queues and topics.

BlazeDS only supports client pull for notifications. This is not ideal, actual push by the server would be better. You’ll need less bandwidth and less activity on the server as well as client. This is supported by the commercial version life cycle data services of adobe, but not by BlazeDS. The commercial version uses a proprietary called Real Time Messaging Protocol. You can read more on this topic in this blog post by Aaron West. This technology is not Open Source and therefor not in the BlazeDS technology stack. For applications with a limited amount of users, this client pull is not a real problem. The client pull can use two different channel types: Streaming and polling. I’ll settle with polling for now. Streaming might me the better option, but polling works for now. More info on the channels can be found in an example by Adobe

Business containing JMS provider

The spring framework comes with some extras to support JMS communication. Sending messages is made easy using the spring framework. We still need a messaging platform. I have choosen ActiveMQ. It is one of the best known Open Source JMS providers and it has very good integration with the spring framework. ActiveMQ comes with a mechanism to embed the messaging container in your application. This can be fully configured in spring.

Maven for dependencies

I am not going into a great detail here. I have written multiple posts about BlazeDS, find maven setup in one of these posts. For the messaging I had to add the following dependencies

<dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-jms</artifactId>
     <version>${spring.version}</version>
</dependency>
<dependency>
     <groupId>javax.jms</groupId>
     <artifactId>jms</artifactId>
     <version>1.1</version>
</dependency>
<dependency>
     <groupId>org.apache.activemq</groupId>
     <artifactId>activemq-core</artifactId>
     <version>5.2.0</version>
</dependency>
<dependency>
     <groupId>org.apache.xbean</groupId>
     <artifactId>xbean-spring</artifactId>
     <version>3.5</version>
</dependency>

Spring configuration of ActiveMQ

Time to start the messaging container. I could find most of the required info online in other post. Still I had to do some tweaking to get it done. The most important post I used is how-do-i-embed-a-broker-inside-a-connection. I’ll show you all the configuration in a spring config file first.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:amq="http://activemq.apache.org/schema/core"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
			http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
            http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core-5.2.0.xsd">

    <amq:broker useJmx="false" persistent="false">
        <amq:transportConnectors>
            <amq:transportConnector uri="tcp://localhost:61616"/>
        </amq:transportConnectors>
    </amq:broker>

    <amq:topic id="destination" physicalName="stockMessageTopic"/>

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

Namespace

ActiveMQ has namespace support for spring, I think they do use an extension in the XBean project. I did not really evaluate this, but you need the dependency. Beware that in the original sample the xsd does not have the right url. If you want to use xml support in Intellij, you need to change the xsd url in what I have. The namespace can be used to configure the Topic, the connection factory and of course the broker.

Topic destination

We configure a topic, again I spend some time to figure this out. The sample used a query instead of a topic. I just missed it. So beware to take the right one. The physical name is important, this is used by BlazeDS as well to connect to the topic to receive messages. Check the Tomcat configuration later on.

Connection Factory

Configuration of a connection factory that connects to a local broker (running in the same jvm). This connection factory can be used by clients, in our case the spring jms client that publishes messages.

Broker

The broker does the actual routing of messages, provides the queues and topics. In short this is the message container. Because we define it in the spring container, it runs in the same jvm. Which is perfect in our case, we do not need persistent messages. The send messages can very short, just long enough to notify all connected subscribers.

The Message provider

The message provider is a spring bean. We need to add a bit of spring configuration. The next block shows the additional beans.

    <bean id="myJmsTemplate" class="org.springframework.jms.core.JmsTemplate">
        <property name="connectionFactory">
            <bean class="org.springframework.jms.connection.SingleConnectionFactory">
                <property name="targetConnectionFactory">
                    <ref local="jmsFactory"/>
                </property>
            </bean>
        </property>
    </bean>

    <bean id="stockMessageProducer" class="nl.primatras.backoffice.service.jms.JmsStockMessageProducer">
        <property name="jmsTemplate" ref="myJmsTemplate"/>

        <property name="destination" ref="destination"/>
    </bean>

Spring jms makes use of a JmsTemplate, you can read more about this in the spring reference manual. The template is used to send a message to a queue or topic. Therefore the jmsTemplate needs to have a connection factory. The other component is the message producer. This class does the actual message creation. To be able to do just that, it needs the mentioned jmsTemplate and a destination. The the code looks like this:

    public void sendStockUpdate(final StockVO stock) {
        logger.debug("About th send a message ...");
        jmsTemplate.send(destination, new MessageCreator() {
            public Message createMessage(Session session) throws JMSException {
                return session.createObjectMessage(stock);
            }
        });
    }

Notice that we use the method createObjectMessage, we will come back to this when configuring BlazeDS. You can also use TextMessage, but we use the javax.jms.ObjectMessage and let BlazeDS do it’s parsing trick on the client.

Tomcat configuration

BlazeDS uses JNDI to find the connection factory and the name of the queue or topic to connect to. These need to be available. WIth tomcat that is not to hard. You can configure this using the context.xml file that can be found in the conf folder. The contents of this file are copied almost one-on-one from this excellent blog post.

<Context path="/BlazeTest">
<Resource name="jms/flex/TopicConnectionFactory"
        type="org.apache.activemq.ActiveMQConnectionFactory"
        description="JMS Connection Factory"
        factory="org.apache.activemq.jndi.JNDIReferenceFactory"
        brokerURL="tcp://localhost:61616"
        brokerName="myBroker"/>
<Resource name="jms/stockMessageTopic"
        type="org.apache.activemq.command.ActiveMQTopic"
        description="a stock topic"
        factory="org.apache.activemq.jndi.JNDIReferenceFactory"
        physicalName="stockMessageTopic"/>
</Context>

The names of the resources are used in the flex configuration, important are the brokerURL and the physical name of the topic. These are the same as the ones configured in the spring activeMQ xml.

BlazeDS/Flex Configuration

Configuration of the destinations of messaging is done in the messaging-config.xml file. The file looks like this.

<?xml version="1.0" encoding="UTF-8"?>
<service id="message-service" class="flex.messaging.services.MessageService">
    <adapters>
        <adapter-definition id="actionscript" class="flex.messaging.services.messaging.adapters.ActionScriptAdapter"
                            default="true"/>
        <adapter-definition id="jms" class="flex.messaging.services.messaging.adapters.JMSAdapter" default="false"/>
    </adapters>

    <default-channels>
        <channel ref="my-polling-amf"/>
    </default-channels>

    <destination id="stock-topic-jms">
        <properties>
            <jms>
                <destination-type>Topic</destination-type>
                <message-type>javax.jms.ObjectMessage</message-type>
                <connection-factory>java:comp/env/jms/flex/TopicConnectionFactory</connection-factory>
                <destination-jndi-name>java:comp/env/jms/stockMessageTopic</destination-jndi-name>
                <delivery-mode>NON_PERSISTENT</delivery-mode>
                <message-priority>DEFAULT_PRIORITY</message-priority>
                <acknowledge-mode>AUTO_ACKNOWLEDGE</acknowledge-mode>
                <initial-context-environment>
                    <property>
                        <name>Context.INITIAL_CONTEXT_FACTORY</name>
                        <value>org.apache.activemq.jndi.ActiveMQInitialContextFactory</value>
                    </property>
                    <property>
                        <name>Context.PROVIDER_URL</name>
                        <value>tcp://localhost:61616</value>
                    </property>
                </initial-context-environment>
            </jms>
        </properties>
        <adapter ref="jms"/>
    </destination>
</service>

There are a few things to notice here. You can see the message type, the connection factory and destination using JNDI. The initial context environment is coming from ActiveMQ and the provider url should be familiar by now.

The client

The final step is the flex client. Flex has very good support for consumers and producers. On the client I am mainly using Mate, which is a flex event driven framework. It has support for messaging as well through the MessageHandlers tag. It turns however that flex 3.2 has a problem together with Mate. Check this blog post for the way it should work and go through the comments to get an idea why it does not work. Because of this problem I had to take a slight detour. The most important part is very easy though. The following code block shows the most important part of configuring a consumer.

<mx:Consumer id="consumer" destination="stock-topic-jms" message="messageHandler(event)"
             acknowledge="acknowledgeHandler(event);" fault=" faultHandler(event)"/>

Then finally you need to login, this can best be done in the creationComplete handler.

private function logon():void {
    consumer.subscribe();
}

That is it, now you just have to implement the three methods that are attached to the consumer. I’ll show the messageHandler implementation as the last piece of the puzzle.

private function messageHandler(event:MessageEvent):void {
    var stock:Stock = event.message.body as Stock;
    var updateEvent:StockEvent = new StockEvent(StockEvent.UPDATE_STOCKITEM_EVENT);
    updateEvent.stock = stock;
    dispatchEvent(updateEvent);
}

Notice that by using the ObjectMessage type, the body of a message is a stock object.

The future

This is all looking very good. I am very pleased about the result. I hope the spring blazeds integration project will make life easier. I know that m2 release contains some messaging support. Did not have time to have a look at it yet. Hope to add it to m books-overview example in a while.

Conclusions

I do want to stress that most of the configuration and code come from other blog posts. I have named them all in the references section. The biggest problem is to combine the server side spring/activemq part with the client/blazeds part. I think this blog post closes that hole.

Stay tuned for more flex stories.

References

Doing flex with JMS: combining BlazeDS, spring-jms and ActiveMQ
Tagged on: