Unit testing your Spring JMS implementation

JMS is a J2EE integration pattern that’s commonly used in Enterprise applicatons, which enables enterprises to send and receive business data through a distributed cross-platform communication protocol.  Spring JMS is a framework that simplifies the use of the JMS API. Without going into more detail, however, I’ll present one possible solution on how you can unit test your Spring JMS implementations.

The simplicity of the Spring framework amazes me each time I use it. In my current project, we’r building a client-server application based on IBM Websphere. The server is an existing component implemented heavily on EJB2 and Message-Driven Beans (MDB’s), whilst the client is a new component implemented on Spring MVC and Spring JMS. So just to explain you how easy Spring JMS actually is: Implementing a new message-driven bean with EJB took me about two days (including debugging & deployment nightmares), implementing a message-driven bean in Spring took me about one hour!!

I wanted my unit test to resemble the real client-server communication as much as possible and to follow the messaging pattern as much as possible. The one requirement to achieve this, is to implement a test that tests your JMS implementation without having to restrict or rewrite its functionality. In my case, that ment implementing a unit test that’s able to use my messaging handler to send to a request queue and to receive a reply from a response queue.

For the unit testing, I use an embedded instance of ActiveMQ which is integrated into Spring JMS. To be able to send and receive from two different embedded queues, I need a simple MDP (message-driven pojo) that listens to the request queue and sends a reply to the response queue. The first thing you need to do is to add a dependency on ActiveMQ in your POM file:

<dependency>
 <groupId>org.apache.activemq</groupId>
 <artifactId>com.springsource.org.apache.activemq</artifactId>
 <version>5.1.0</version>
 <scope>test</scope>
</dependency>
Then you need to configure your application configuration file to prepare an instance of ActiveMQ for your test:

 

<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
  <property name="connectionFactory" ref="connectionFactory" />
  <property name="receiveTimeout" value="7000" />
  <property name="defaultDestination" ref="request" />
 </bean>

 <bean id="request" class="demo.jms.Request">
  <constructor-arg value="queue.request"/>
 </bean>

 <bean id="response" class="demo.jms.Response">
  <constructor-arg value="queue.response"/>
 </bean>

 <bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
  <property name="brokerURL" value="vm://embedded?broker.persistent=false" />
 </bean>

 With the above code, you have a JmsTemplate, two different queues and a queue connections factory. You can now inject the JmsTemplate into a class of your choice, however in my sample I’ll inject it into a custom made message handler class that: adds a correlation id, sends to a default destination and receives from a custom destination.  The code you need to insert into your configuration file will be something like this:
 

<bean id="messageHandler" class="demo.jms.MessageHandler">
 <property name="jmsTemplate" ref="jmsTemplate" />
 <property name="responseQueue" ref="response" />
</bean>

The last bean definition we need is for a message-driven pojo:

<jms:listener-container concurrency="5">
 <jms:listener ref="messageHandlerTestMDP " destination="queue.request" method="onMessage"/>
</jms:listener-container>

<bean id="messageHandlerTestMDP" class="demo.jms.MessageHandlerTestMDP">
 <property name="jmsTemplate" ref="jmsTemplate" />
 <property name="destination" ref="response" />
</bean>

Notice the use of the “jms” namespace in the above definition. The only thing left now is to implement the message handler class and the message-driven pojo. This is the code for the latter:

import demo.jms.RequestObject;
import demo.jms.ResponseObject;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessagePostProcessor;

public class MessageHandlerTestMDP
{
    private JmsTemplate jmsTemplate;

    private Destination destination;

    public void onMessage(RequestObject request)
    {
        System.out.println("Received a " + request.getClass().getSimpleName());

        ResponseObject response = new ResponseObject();
        jmsTemplate.convertAndSend(destination, response, new MessagePostProcessor(){
            public Message postProcessMessage(Message message) throws JMSException {
                message.setJMSCorrelationID("abc12345");
                return message;
            }
        });

        System.out.println("Reply sent to "  + destination.toString());
    }

    public void setJmsTemplate( JmsTemplate jmsTemplate )
    {
        this.jmsTemplate = jmsTemplate;
    }

    public void setDestination( Destination destination )
    {
        this.destination = destination;
    }
}

And this is the code for the message handler:

import demo.jms.RequestObject;
import demo.jms.ResponseObject;
import java.io.IOException;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import org.springframework.jms.JmsException;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessagePostProcessor;

public class MessageHandler
{
    private Destination responseQueue;

    private JmsTemplate jmsTemplate;

    public void setJmsTemplate( JmsTemplate jmsTemplate )
    {
        this.jmsTemplate = jmsTemplate;
    }

    public void setResponseQueue(Destination responseQueue)
    {
        this.responseQueue = responseQueue;
    }

    public boolean sendMessage(final RequestObject pojo, final String messageId) throws JmsException
    {
        try
        {
            this.jmsTemplate.convertAndSend( pojo, new MessagePostProcessor(){
                public Message postProcessMessage(Message message) throws JMSException {
                    message.setJMSCorrelationID(messageId);
                    message.setJMSReplyTo(responseQueue);
                    return message;
                }
            });
        }
        catch (JmsException jmsException)
        {
            // handle exception here
                  return false;
        }
        return true;
    }

    public ResponseObject receiveMessage(String jmsCorrelationId) throws JmsException
    {
        ResponseObject responseObject = null;
        try
        {
            responseObject = (ResponseObject) this.jmsTemplate.
                receiveSelectedAndConvert(responseQueue, "JMSCorrelationID = '" + jmsCorrelationId + "'");
        }
        catch (JmsException jmsException)
        {
            // handle exception here
        }
        return responseObject;
    }
}

In the above code samples, pay attention to the use of the JMSCorrelationID setting. In a production environment our system will most likely service multiple users. To be able to to receive the correct reply from the server, the client sets a JMSCorrelationID on the message before sending it. This makes it able to filter messages and only receive a message containing the expected id. In my samples I have hardcoded an id to keep thing simple, but in your code you should generate unique ones (a combination of UUID+IP+Date for instance).

And finally, the unit test itself:

@ContextConfiguration(locations = {"/demo/jms/app-config.xml"} )
@RunWith(SpringJUnit4ClassRunner.class)
public class MessageHandlerTest extends TestCase
{
    @Autowired
    private MessageHandlerImpl messageHandler;

    @Test
    public void testSendMessage() throws Throwable
    {
        correlationId = messageHandler.sendMessage( new RequestObject() , "abc12345");
        System.out.println(" Sent test message with id: " + correlationId);
    }

    @Test
    public void testReceiveMessage() throws Throwable
    {
        ResponseObject responseObject = null;

        responseObject = messageHandler.receiveMessage( correlationId );
        assertNotNull( "Received object was null.", responseObject );

        System.out.println("Received test message: " + responseObject.getClass().getSimpleName());
    }
}

Running the unit test, will then print out something like this:

Sent test message with id: abc12345
Received a RequestObject
Reply sent to queue://queue.response
Received test message: ResponseObject

In this article, you have learnt
– how to use and unit test an embedded instance of Apache ActiveMQ
– how send and receive JMS messages with JMSCorrelationID’s
– how to write a simple message-driven pojo with Spring

This entry was posted in Java and tagged , , . Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>