Monday, March 11, 2013

Message Driven POJO using Spring and ActiveMQ

As we saw in our previous JMS with spring post, we used synchronous reception of JMS message from Queue.

When we call the receive() method on a JMS message consumer to receive a message, the calling thread is blocked until a message is available. During the duration, the thread can do nothing but wait.

The previous posts on JMS are as follow


In this post, we are going to look at Spring's feature for asynchronous reception of JMS Messages.

Starting with EJB 2.0, a new component called a message-driven bean (MDB) was introduced for asynchronous reception of JMS messages. An EJB container can listen for JMS messages at a message destination and trigger MDBs to react to these messages so that your application no longer has to wait for messages. Refer EJB3.0 specification


Message Driven Beans(MDB) can listen for JMS messages and  they must be deployed in an EJB container to run.  But Spring facilitates us to add the same capability to POJOs (declared in its IoC container) so that they can listen for JMS messages without an EJB container. We  called these bean as message-driven POJOs (MDPs).

A Message Driven POJO (MDP) is a simple java object which implements the javax.jms.MessageListener interface. MessageListener interface has one method onMessage() which need to be implemented by MDP. When a JMS message arrives, the onMessage() method will be called with the Message as the method argument.

This is our old Message Consumer class which has been modified to make it MDB.
MessageConsumerBean.java
package com.sarf.jms;

import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.MessageListener;

public class MessageConsumerBean implements MessageListener{
  public void onMessage(Message message) {
  MapMessage mapMessage = (MapMessage) message;
  try {
      String strEmail = mapMessage.getString("mailId");
      System.out.println("Mail #"+strEmail+" received.");
      } catch (JMSException e) {
  e.printStackTrace();
      }
  }
}



Step 1 : Now we have to configure our MDP in our bean configuration file as
Step 2 : Declaring MDP bean alone is not enough to listen for JMS messages. We need a message listener container to monitor JMS messages at a destination and trigger our message listener on message arrival.
Spring provides several types of message listener containers and we are using SimpleMessageListenerContainer  for our example.

So final version of our bean configuration file will look like this.
appContext.xml
This is our producer class MessageProducerBean.java
package com.sarf.jms;

import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.Session;
import org.springframework.jms.core.MessageCreator;
import org.springframework.jms.core.support.JmsGatewaySupport;
import com.sarf.data.MessageObject;

public class MessageProducerBean extends JmsGatewaySupport{
 public void sendMessage(final MessageObject messageObj) {
  getJmsTemplate().send(new MessageCreator() {
   public Message createMessage(Session session) throws JMSException {
   MapMessage message = session.createMapMessage();
   message.setString("mailId", messageObj.getMailId());
   message.setString("message", messageObj.getMessage());
   return message;
  }});
 }
}

Our producer Test class will look like this which will start the container as well as it will send a message to destination.
ProducerTest.java
package com.sarf.main;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.sarf.data.MessageObject;
import com.sarf.jms.MessageProducerBean;

public class ProducerTest {   
 public static void main(String[] args) {
   ApplicationContext context =
    new ClassPathXmlApplicationContext("appContext.xml");
   MessageProducerBean mp = 
    (MessageProducerBean) context.getBean("producer");
   mp.sendMessage(new MessageObject("34", "Test Message"));
   } 
}
 Click here to get source code