Saturday, February 26, 2011

Spring JMS & Groovy: Sending and Receiving Messages

I recently wrote some code using Spring JMS to send a JMS text message to a queue and to receive a response. The examples in the Spring documentation use Java of course to send and receive the messages. Here I wanted to demonstrate how the code can be simplified using Groovy .

In order to send and receive the messages, a JmsTemplate object is required. In a typical case, the template would be configured thru Spring and injected. Here, I'm going to omit the creation process here for brevity's sake, especially given any example would be specific to a particular JMS provider, but the documentation does a great job explaining how to accomplish the creation of a template using Spring.

When sending a text message, we'll need a list of parameters:

  1. The text the message should contain.
  2. The name of the queue to place the request on.
  3. The name of the queue to have the response sent to.
  4. A correlation ID so that we can receive the response created for the message being sent.

Since its simple to get out of the way here, a correlation ID can be created by creating a unique String using:

  def createCorrelationId() { UUID.randomUUID().toString() }

We'll also need a method to create a MessageCreator to give to the JmsTemplate when sending the mesage. A Java example of doing so my look like this using an inner class:

    void sendMessage(JmsTemplate jmsTemplate, String text, String requestQueue, String responseQueue, String correlationId) throws JMSException { 
jmsTemplate.send(requestQueue, new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
TextMessage message = session.createTextMessage(text);
return message;
}
});
}

This example can be made easier on the eyes by using as in Groovy since MessageCreator is an interface (we also get the benefit of not needing the throws declarations):

    def sendMessage(jmsTemplate, text, requestQueue, responseQueue, correlationId) { 
jmsTemplate.send(requestQueue, { session ->
session.createTextMessage(text)
} as MessageCreator)
}

Or we can store the MessageCreator in a variable:

    def sendMessage(jmsTemplate, text, requestQueue, responseQueue, correlationId) { 
def messageCreator = { session ->
session.createTextMessage(text)
} as MessageCreator

jmsTemplate.send(requestQueue, messageCreator)
}

In order to set the response queue and correlationID though, we'll need access to the text message created by the Session provided by the JmsTemplate:

    def correlationId = createCorrelationId() 

def sendMessage(jmsTemplate, text, requestQueue, responseQueue, correlationId) {
def messageCreator = { session ->
session.createTextMessage(text)
message.with {
setText request
setJMSCorrelationID correlationId
setJMSReplyTo session.createQueue(responseQueue)
}

} as MessageCreator

jmsTemplate.send(requestQueue, messageCreator)
}

Since the MessageCreator is defined by a closure, we can extract a method out that the closure will have access to when its executed to create the text message:

    def correlationId = createCorrelationId() 

def createMessage(session, text, responseQueue, correlationId) {
def message = session.createTextMessage(text)
message.with {
setText text
setJMSCorrelationID correlationId
setJMSReplyTo session.createQueue(responseQueue)
}
message
}

def sendMessage(jmsTemplate, text, requestQueue, responseQueue, correlationId) {
def messageCreator = { session ->
createMessage(session, request, responseQueue, correlationId)
} as MessageCreator

jmsTemplate.send(requestQueue, messageCreator)
}

Now that we have a method for sending the request message, we need a way to grab the response. A Java example:

    void receiveResponse(JmsTemplate jmsTemplate, String responseQueue, String correlationId) throws JMSException {
String selector = "JMSCorrelationID='" + correlationId + "'";
Message message = (TextMessage) jmsTemplate.receiveSelected(parameters.getRetrieveFromQueue(), selector);
String response;
if (message != null) {
response = message.getText();
}
return response;
}

A Groovier way:

  def receiveResponse(jmsTemplate, responseQueue, correlationId) { 
def selector = "JMSCorrelationID='$correlationId'"
def message = (TextMessage)jmsTemplate.receiveSelected(responseQueue, selector)
message?.getText()
}

In both reception examples, the fact we may not get a response or text in the response. This is something the caller can deal with or we can modify the examples to throw an exception:

   def receiveResponse(jmsTemplate, responseQueue, correlationId) {
def selector = "JMSCorrelationID='$correlationId'"
def message = jmsTemplate.receiveSelected(responseQueue, selector) as TextMessage
def response = message?.getText()

if (!response) {
throw new Exception("Got nothing!") // in practice would likely throw a more apt or custom exception
}

response
}

To put it all together, the code examples above can be aggregated to a class:

class JmsMessageSender {

def createCorrelationId() { UUID.randomUUID().toString() }

def createMessage(session, text, responseQueue, correlationId) {
def message = session.createTextMessage(text)
message.with {
setText text
setJMSCorrelationID correlationId
setJMSReplyTo session.createQueue(responseQueue)
}
message
}

def sendMessage(jmsTemplate, text, requestQueue, responseQueue, correlationId) {
def messageCreator = { session ->
createMessage(session, request, responseQueue, correlationId)
} as MessageCreator

jmsTemplate.send(requestQueue, messageCreator)
}

def createSelectorFrom(correlationId) {
"JMSCorrelationID='$correlationId'"
}

void receiveResponse(jmsTemplate, responseQueue, correlationId) {
def selector = createSelectorFrom(correlationId)
def message = jmsTemplate.receiveSelected(responseQueue, selector) as TextMessage
def response = message?.getText()

if (!response) throw new Exception("Got nothing!")

response
}
}

A client code example using the above class through three calls:

    JmsTemplate jmsTemplate // injected
def correlationId = messageSender.createCorrelationId()
messageSender.sendMessage(jmsTemplate, text, requestQueue, responseQueue, correlationId)
def response = messageSender.receiveResponse(jmsTemplate, responseQueue, correlationId)

Thanks for reading!