Happy Messaging with ActiveMQ and SI (Part 1)In one of my current projects we needed to set up a communication channel between two distinct Grails applications. One of them is the master application which runs an embedded ActiveMQ message broker . The second one - the slave application - provides service APIs to the master application. Since Grails heavily relies on Spring, we decided to use Spring Integration as messaging framework . Spring Integration is a messaging framework which supports various Enterprise Application Integration Patterns , without being bound to any specific messaging protocol. Since our project team chose to use ActiveMQ we go with JMS as underlying messaging protocol in our project.
Setting up an embedded ActiveMQ message brokerActiveMQ is a fully JMS 1.1 compliant messaging provider which is available under the Apache Software License. It has quite a bag of features, the most important ones for us where persistent messages support. Besides running ActiveMQ as a distinct server, one can choose to run ActiveMQ as an embedded server inside the application. Configuring an embedded ActiveMQ broker using Grails' Beans DSL is pretty straight-forward (once you get used to the Beans DSL of course): The code above configures an embedded broker called
myEmbeddedBrokerwhich only persists messages in-memory (
persist: false), exposes itself as JMX bean (
useJmx: true) and configures a transport connector using Openwire over TCP. In order to let the master application (which holds the configuration above) connect to its embedded message broker, we need to set up a connection factory: After all, we will define two message queues one for outgoing API requests to the slave application and one for incoming responses:
Spring Integration comes into playSo far we have set up an embedded message broker which could be used for plain JMS API message exchange. In our project we decided to go with Spring Integration because it already implements several EAI patterns (e.g. router, gateway, etc.) and abstract from the underlying messaging protocol. A reference manual on Spring Integration can be found at , but let me give you a short introduction. Spring Integration (SI) is a messaging framework which implements patterns found in the book Enterprise Application Integration Patterns . That is, SI is all about messages and message exchange. To exchange a message from point A to point B there needs to be a channel between A and B. Besides messages, channels are the second most important domain entity in SI. Channels are injected into your application components just like any other Spring bean. The basic
MessageChannelinterface is pretty rudimentary: The use-case in our project was to automatically create a message and send it to some preconfigured channel whenever the programmers chooses to call a service API method: A call to
executeRemotelyshould automatically create a message object from the input parameters and send it to some sort of API request channel. Luckily, SI provides the concept of gateways which solve that particular problem. At runtime, a gateway is a proxy object for a particular interface which, on a method call, creates a message object and sends it via some preconfigured channel. Like channels, gateways are Spring beans and can therefore be configured via the Beans DSL: As you can see from the configuration snippet above, the gateway has a request/reply channel configured since gateways are synchronous (in SI 2.0 there is asynchronous gateway support) and bidirectional. The
SomeApiinterface uses SI annotations for further message configuration: From the gateway's view the interface above means: whenever
executeRemotelyis called, put param1 into a message header with name
HEADER_NAMEand put the second parameter into the message's payload. Maybe you noticed the
API_METHOD_NAMEparameter in the gateway configuration above - that was a message header too. We needed to manually inject a unique method identification token (in our case the method name only was enough) in order to call the correct method on the slave application side.
Configuring JMS messagingSo far we've set up an environment with an embedded ActiveMQ message broker and two ActiveMQ message queues. Now we need to configure the link between the SI channels configured in the last section and those JMS queues. Since gateways are bidirectional, SI needs to store some reply channel information whenever instantiating an API request. This is automatically done via the gateway implementation. If we would run inside a SI environment only we wouldn't need to care about this fact. In our case, we chose to use gatways to communicate between a master and a slave application which are in production deployed on separate server instances. In SI, a
JMSOutboundGatewaycan be used for those JMS request/reply scenarios. It is the clue between SI channels and out ActiveMQ JMS queues: In the slave application, there needs to be an inverse configuration using a JMS inbound gateway: The configuration snippet inside the slave application simply routes incoming messages to the
incomingRequestschannel. Notice that no reply channel has been specified in order to keep the reply channel which has been added by the master application in the message. In the next part of this article series we'll have a closer look at the slave application and how it is configured to invoke methods an Grails service beans.