09 - Introduction to Message Driven Beans

Introduction

Java Messaging Service (JMS) is a Java API that is used to simplify the process of integrating application components. EJB3 specifications provide a type of beans that can be used to allow JEE applications to integrate with JMS queues and topics and process provided messages asynchronously.

Message Driven Bean characteristics

  • Only bean class is required, no interfaces should be provided or implemented.
  • Message driven beans have no conversational state similar to the Stateless Session beans
  • Bean objects are not thread safe.
  • Can only process messages asynchronously.
  • Bean objects cannot be accessed directly and can only be triggered with queue/topic new messages.
  • Support all types of JMS messages.
  • The container provides a pool of bean objects to handle multiple simultaneously provided messages.
  • The container can provide transaction management techniques for the bean objects.
  • EJB 3.2 supports JMS specification version 2.

Message Driven Bean Life Cycle

The provided callback methods by the Message Driven Beans are:

  • @PostConstruct: the method is called once, all dependency injection on the bean class finishe and it indicates that the bean is ready for invocation. The method can be used to allocate the resources needed by the bean object to function correctly (i.e. Network connections, database connections...etc).
  • @PreDdestroy: the method is called before destroying the bean object and can be used to release all the reserved resources by the bean object.

The below is a state diagram that visualizes the stateless session bean lifecycle:

In the following example, we will create a Message Driven Bean that listens to a defined Queue in JBOSS AS 7, another Stateless service will act as the messages producer for the Queue.

The message driven bean:

import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;

@MessageDriven(activationConfig = {
        @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
        @ActivationConfigProperty(propertyName = "destination", propertyValue = "queue/helloWorld") })
public class HelloWorldMDB implements MessageListener {

        @Override
    public void onMessage(Message message) {
        TextMessage textMessage = (TextMessage) message;
        try {
            System.out.println("Message Received [" + textMessage.getText()
                    + "]");
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

The messages producer bean:

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;

@Startup
@Singleton
public class MDBTestClientBean {

       @Resource(mappedName = "java:jboss/exported/jms/queue/helloWorld")
       private Queue helloQueue;

       @Resource(mappedName = "java:/JmsXA")
       private ConnectionFactory cf;

       @PostConstruct
       public void sendAllMessages() {
       Connection connection = null;
       try {
             connection = cf.createConnection();
            Session session = connection.createSession(false,
                    Session.AUTO_ACKNOWLEDGE);
            MessageProducer publisher = null;
            publisher = session.createProducer(helloQueue);
            connection.start();
            TextMessage message = session
                    .createTextMessage("this is hello world message");
            publisher.send(message);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (connection != null)
                try {
                    connection.close();
                } catch (JMSException e) {
                    e.printStackTrace();
                }
        }
    }
}

Now, edit the file JBOSS AS 7 HOME / standalone / configuration / standalone-full.xml.

At the section <jms-destinations> add the following new Queue definition:

<jms-queue name="helloWorldQueue">
    <entry name="queue/helloWorld"/>
    <entry name="java:jboss/exported/jms/queue/helloWorld"/>
</jms-queue>

Now add the 2 beans to an EJB 3 application and deploy to JBOSS, after starting JBOSS, you will see like the following lines in the JBOSS Console logs:

05:35:04,204 INFO  [org.hornetq.core.server.impl.HornetQServerImpl] (MSC service thread 1-14) trying to deploy queue jms.queue.helloWorldQueue
...
05:35:04,211 INFO  [org.jboss.as.messaging] (MSC service thread 1-14) JBAS011601: Bound messaging object to jndi name java:/queue/helloWorld
05:35:04,218 INFO  [org.jboss.as.messaging] (MSC service thread 1-14) JBAS011601: Bound messaging object to jndi name java:jboss/exported/jms/queue/helloWorld
...
05:35:13,904 INFO  [stdout] (Thread-1 (HornetQ-client-global-threads-614217941)) Message Received [this is hello world message]

Now let’s explain what we have done in the previous example, we have defined a new JMS Queue on JBOSS AS 7 named “helloWorldQueue”. JBOSS will bind the queue under JNDI paths java:/queue/helloWorld and java:jboss/exported/jms/queue/helloWorld.

We have created a message driven bean that will listen to that queue called MDBTestClientBean. The bean once receives a message from the queue, casts it to Text message and prints the message content on the Console log.

Another Singleton bean called MDBTestClientBean is used as a message producer for helloWorld queue. The bean uses injection to inject the queue resource “helloWorld” and JMS connection factory “JmsXA”.  The bean is annotated with @Startup annotation and once it has method annotated with @PostConstruct annotation, the method uses the JMS API to send a text message with content “this is hello world message” to the helloWorld queue.

Like us on Facebook