Skip to main content

Developing Message-Driven Beans

An message-driven EJB is used to receive and process asynchronous messages using JMS. Message-driven EJBs are never directly invoked by other EJBs. However, they in turn can invoke methods of session and entity beans and send JMS messages to be processed by other message-driven EJBs. The topics listed below discuss development of message-driven beans.

What are Message-Driven Beans?

Message-driven beans are server-side objects used only to process JMS messages. These beans are stateless, in that each method invocation is independent from the next. Unlike session and entity beans, message-driven beans are not invoked by other beans or client applications. Instead a message-driven bean responds to a JMS message.

Because message-driven beans are not invoked by other EJBs or clients, these beans do not have interfaces. For each message-driven bean a single method, onMessage, is defined to process a JMS message. Although message-driven beans cannot be invoked by other EJBs, they can in turn invoke other EJBs. Also, message-driven beans can send JMS messages. As with the other types of EJBs, the EJB container is responsible for managing the beans environment, including making enough instances available for processing and message-acknowledgement.
Asynchronous and Concurrent Processing

A core feature of message-driven beans is the notion of asynchronous processing. A client application can send a JMS message to execute a certain business task. After the message has been sent, the client application can continue right away and does not have to wait for the JMS message to be received and processed. This is especially helpful when the business task is complex, requires the use of entity (and session) beans, and takes a long time to complete. In contrast, if the same client application were to use a session bean to execute a certain business task, it would have to wait until the session bean method completed and returned control to the client application. The message façade design pattern formalizes this use of message-driven beans as an intermediary between client applications and entity beans to achieve asynchrony. An example of this pattern is shown in the Message-Driven Bean Sample.

Another important feature of message-driven beans is that JMS messages are processed concurrently. That is, although each bean instance handles a message at a time, the EJB container takes care of creating enough bean instances to handle the message load at a given moment. In WebLogic you can set the initial number and max number of bean instances created by the container. For more information, see the @ejbgen:message-driven Annotation.

Because message-driven beans are stateless and processing of JMS messages occurs in an asynchronous message, there is no guarantee that messages are processed in the order they were sent. Therefore, sending multiple messages such that one message is dependent on the successful processing of another message might cause unexpected results. Instead, you should reconsider the granularity of your business task such that one message can initiate its execution, possibly by handling one piece of the task, and then sending a JMS message to be processed by another message-driven bean for the remainder of the business task.

Topics and Queues

A message-driven bean listens to a particular channel for JMS messages. There are two types of channels, namely topics and queues. Topics implement the publish-and-subscribe messaging model, in which a given message can be received by many different subscribers, that is, many different message-driven bean classes (not instances) listening to the same topic. In contrast, queues implement the point-to-point messaging model, in which a given message is received by exactly one message-driven bean class, even when multiple classes are listening to this queue.

The Life Cycle of a Message-Driven Bean

The EJB container is responsible for creating a pool of message-driven bean instances. When it creates an instance, it calls the setMessageDrivenContext() and the ejbCreate() methods. At this point the message-driven bean is ready to receive messages. When a bean instance is processing a JMS message, its onMessage method is being invoked. When the EJB container removes a bean instance, it first calls the ejbRemove method before the instance is ready for garbage collection. The life cycle of a message-driven bean is depicted in the following figure.

When defining a message-driven bean in WebLogic, in most cases you will implement the onMessage method to execute a particular business task, and use the ejbCreate method to implement actions that only need to be executed once, such as looking up the home interfaces of entity beans that are invoked by the message-driven bean's onMessage method. A typical example of simple message-driven bean is given below. Notice that the ejbCreate method is used to find the home interface of a Recording entity bean, while the onMessage method processes the message and invokes the Recording bean:
/**
 * @ejbgen:message-driven
 *   ejb-name = Statistics
 *   destination-jndi-name="jms.EJBTutorialSampleJmsQ"
 *   destination-type = javax.jms.Queue
 *
 * @ejbgen:ejb-local-ref
 * type="Entity"
 * name="ejb/recordLink"
 * local="bands.Recording"
 * link="Recording"
 * home="bands.RecordingHome"
 */
public class StatisticsBean extends GenericMessageDrivenBean implements MessageDrivenBean, MessageListener
{
  private RecordingHome recordingHome;

  public void ejbCreate() {
    try {
        javax.naming.Context ic = new InitialContext();
        recordingHome = (RecordingHome)ic.lookup("java:/comp/env/ejb/recordLink");
    }
    catch(NamingException ne) {
        System.out.println("Encountered the following naming exception: " + ne.getMessage());       
    }
  }
 
  public void onMessage(Message msg) {
     try {
         // read the message
         MapMessage recordingMsg = (MapMessage)msg;
         String bandName = recordingMsg.getString("bandName");
         String recordingTitle = recordingMsg.getString("recordingTitle");
         ...
         // save the rating with the recording
         Recording album = recordingHome.findByPrimaryKey(new RecordingBeanPK(bandName, recordingTitle));
         album.setRating(rating);
     }
     catch(Exception e) {
        System.out.println("Encountered the following exception: " + e.getMessage());
     }
  }
}
You can implement the ejbRemove method if cleanup is required before the object is removed, and you can implement setMessageDrivenContext method if you need access to the javax.ejb.MessageDrivenContext provided by the EJB container. The MessageDrivenContext contains information about the container, in particular its transaction methods; for more information, see your favorite J2EE documentation. A message-driven bean defined in WebLogic by default extends weblogic.ejb.GenericMessageDrivenBean, which provides empty definitions for all these methods with the exception of the onMessage method; the definition of your bean must therefore implement the onMessage method.


Using a JMS Control

In WebLogic you can use a JMS control to send JMS messages to a topic or queue. JMS controls can be used to send messages in page flows and web services; they cannot be used to send JMS messages in EJBs. You can use JMS controls to both send and receive messages. In this case you will only be using the JMS control to send messages, as a message-driven bean will be used to process received messages. For details on how to create JMS controls for topics and queues, see JMS Control.
The advantage of using a JMS control is that the details of the JMS messaging API are by and large taken care of by the JMS control. For example, the following code samples shows the definition of a JMS control to send messages to a queue:
/**
 *   @jc:jms send-type="queue" send-jndi-name="jms.SamplesAppMDBQ"
 *            connection-factory-jndi-name="weblogic.jws.jms.QueueConnectionFactory"
 */
public interface sendToQueueControl extends JMSControl, com.bea.control.ControlExtension
{
    /**
     * this method will send a javax.jms.Message to send-jndi-name
     */
    public void sendJMSMessage(Message msg);
    static final long serialVersionUID = 1L;
}
As the definition shows, you must still define the name of the queue to which to send the JMS message as well as the name of a QueueConnectionFactory. However, to send a message using this JMS control, you simply need to invoke the method sendJMSMessage and pass a message as the parameter. The JMS control handles the creation of a factory instance, a connection, session, and so forth.

When using a JMS control from a page flow or web service, you can use a built-in control to encapsulate all the message sending related logic, instead of defining this directly in the page flow or web service. For instance, the following built-in control code snippet uses the JMS control defined above to send 20 messages at one time to a queue.
...
public class MessageSenderImpl implements MessageSender, ControlSource
{
    /**
     * @common:control
     */
    private messageDriven.sendToQueueControl queueSend;

    /**
     * @common:operation
     * @common:message-buffer enable="true"
     */
    public void add20ViaQueue(int currentNum) throws JMSException
    {
        String name;
        for(int i = 0; i < 20; i++) {
           MapMessage msg = queueSend.getSession().createMapMessage();
           msg.setStringProperty("Command", "Add");
           name = Integer.toString(currentNum + i);    
           msg.setString("tokenName", name);
           queueSend.sendJMSMessage(msg);
        }
    }
}
For more information on built-in controls, see Building Custom Java Controls. The use of JMS controls and built-in controls in a page flow is demonstrated in the Message-Driven Bean Sample.

Sending Messages from EJBs

To send JMS message from an EJB, you cannot use JMS controls and must use the 'standard' JMS messaging API. (You can also use the standard messaging API in page flows and web services if you do not want to use a JMS control.) The following code sample shows a session bean sending a message to a queue. The queue and QueueConnectionFactory are defined using @ejbgen:resource-env-ref and @ejbgen:resource-ref annotations. In the bean's ejbCreate method the queue and QueueConnectionFactory are located (this only need to be done once), while the method executeTask contains the correct procedure to create a connect, session, sender, and message before sending the JMS message.
/**

 * @ejbgen:resource-ref
 *   auth="Container"
 *   jndi-name = "weblogic.jws.jms.QueueConnectionFactory"
 *   name = "jms/QueueConnectionFactory"
 *   type="javax.jms.QueueConnectionFactory"
 *
 * @ejbgen:resource-env-ref
 *  jndi-name="jms.ASampleJmsQ"
 *  name="jms/ASampleJmsQ"
 *  type = "javax.jms.Queue"
 *  
 * ...
 */
public class ASessionBean extends GenericSessionBean implements SessionBean
{
   private QueueConnectionFactory factory;
   private Queue queue;
   
   public void ejbCreate() {
      try {
         javax.naming.Context ic = new InitialContext();
         factory = (QueueConnectionFactory) ic.lookup("java:comp/env/jms/QueueConnectionFactory"); 
         queue = (Queue) ic.lookup("java:comp/env/jms/ASampleJmsQ");
      }
      catch (NamingException ne) {
         throw new EJBException(ne);
      }
   }

   /**
    * @ejbgen:local-method
    */
   public void executeTask(String prop1, String prop2)
   {
     try {
        //interpret the properties
        ...
        //send a message
        QueueConnection connect = factory.createQueueConnection();
        QueueSession session = connect.createQueueSession(true,Session.AUTO_ACKNOWLEDGE);
        QueueSender sender = session.createSender(queue);
        MapMessage recordingMsg = session.createMapMessage();
        recordingMsg.setString("Property1", prop1);
        recordingMsg.setString("Property2", prop2);
        sender.send(recordingMsg);
        connect.close();
     }
     catch(JMSException ne) {
        throw new EJBException(ne);
     }
   }
}

JMS Message Types

The JMS messaging API defines various message types, allowing for different types of information to be included in the body of a JMS message. Frequently used message types are TextMessage to send a text string, MapMessage to create sets of name-value pairs, and ObjectMessage to send a serializable object. For more details on the various message types, see your favorite J2EE documentation or the javax.jms.Message API documentation at http://java.sun.com.

Message Selectors

A JMS message can also contain message properties. There are different types of properties which can serve a number of functions. One common use of a message property is to set a message selector. A message selector is a property that is used by a message-driven bean to determine whether it should process this message or should leave its processing up to another message-driven bean listening to the same topic or queue.

The following example shows how to set a String property that will act as a message selector for a JMS MapMessage:
Message msg = session.createMapMessage();
msg.setStringProperty("MyMessage", "Build 126"");
A message-driven bean that is defined to specifically process this type of message will include a message selector property:
 * @ejbgen:message-driven
 *   message-selector="MyMessage = 'Build126'"   
 *   ejb-name = MDBean
 * ...
 */
public class MDBean extends GenericMessageDrivenBean implements MessageDrivenBean, MessageListener
{
   ...
In other words, this bean will respond specifically to this type of message and will leave the processing of other messages up to other beans listening to this topic or queue. By using a message selector you can send different types of messages to the same channel instead of having to create a separate queue/topic for every type of message. For more details on the various message properties, see your favorite J2EE documentation or the javax.jms.Message API documentation at http://java.sun.com.





Processing JMS Messages

When a JMS message is sent to a topic or queue, a message-driven bean instance will interpret and process the message, as specified in its onMessage method. When a JMS message is sent to a topic, a publish-and-subscribe system, an instance of every message-driven bean class listening to this topic will in principle receive and process this message. However, if the message contains a message selector, only the message-driven bean(s) matching the message selector will process the message. When a JMS message is sent to a queue, a point-to-point system, only one message-driven bean will process the message, even when multiple bean classes are listening to the queue. Again, the use of a message selector might limit the bean processing the message. For more on message selectors, .
How the JMS message is processed fully depends on the business task that is being modeled. It might range from simply logging the message to executing a range of different tasks which include invoking methods on session and entity beans, or sending JMS messages to be processed by other message-driven beans. The following code sample shows one use of a message-driven bean. This bean responds only to JMS messages delivered via the jms/SamplesAppMDBQ queue and contain the message selector Command= 'Delete'. When processing a JMS message, an instance of this bean invokes the query method findAll of the entity bean SimpleToken, and subsequently deletes all SimpleToken records from the underlying database.
/**

 * @ejbgen:message-driven
 *   message-selector="Command = 'Delete'"
 *   ejb-name = DeleteViaQMD
 *   destination-jndi-name = jms/SamplesAppMDBQ
 *   destination-type = javax.jms.Queue
 *
 *  @ejbgen:ejb-local-ref link="SimpleToken"
 */
public class DeleteViaQMDBean
  extends GenericMessageDrivenBean
  implements MessageDrivenBean, MessageListener
{
  private SimpleTokenHome tokenHome;
 
  public void ejbCreate() {
     try {
        javax.naming.Context ic = new InitialContext();
        tokenHome = (SimpleTokenHome)ic.lookup("java:/comp/env/ejb/SimpleToken");
     }
     catch(NamingException ne) {
        System.out.println("Encountered the following naming exception: " + ne.getMessage());       
     }
  }

  public void onMessage(Message msg) {
     try {
        Iterator allIter = tokenHome.findAll().iterator();
        while(allIter.hasNext()) {
            ((SimpleToken) allIter.next()).remove();
        }
     }
     catch(Exception e) {
        System.out.println("Encountered the following exception: " + e.getMessage());
     }
  }

Acknowledgement and Transactions

When a message-driven bean instance receives a message, and it is not part of a container-managed transaction, by default it immediately sends an acknowledgement to the JMS provider notifying that the message has been received. This will prevent the JMS provider from attempting to redeliver it. However, the acknowledgement only indicates that a message has been successfully received, but it does not guarantee that the message is successfully processed. For instance, a system exception might occur when attempting to locate an entity bean or update its records, causing the processing to fail.

If you want to ensure that a message is redelivered when processing fails, you can make the message-driven bean be part of a transaction. The easiest approach is to use container-managed transaction, where the EJB container is responsible for transaction management. To enable container-managed transaction for a message-driven bean, make sure that your cursor is placed inside the ejbgen:message-driven tag and use the Property Editor to set the transaction-type to Container and the default-transaction to Required. When JMS message processing executes successfully, any changes made during this business task, such as update to entity bean records, are committed and acknowledgement is sent to the JMS provider. However, when JMS message processing fails due to a system exception, any changes are rolled back and receipt is not acknowledged. In other words, after processing fails, the JMS provider will attempt to resend the JMS message until processing is successfully or until the maximum number of redelivery attempts specified for this topic or queue has been reached.

Note. When a message-driven bean is part of a transaction, it executes as part of its own transaction. In other words, if the transaction fails, changes that were made as part of the onMessage method are rolled back, but the occurrence of an exception has no direct effect on the EJB or client application sending the JMS message, as the sender and the message-driven bean are decoupled.

To learn more about container-managed transactions, see EJBs and Transactions. To learn more about bean-managed transaction (that is, explicit transaction management), please refer to your favorite J2EE documentation.

Comments

  1. While people may have different views still good things should always be appreciated. Yours is a nice blog. Liked it!!!

    ReplyDelete
  2. I appreciate the labor you have put in developing this blog. Nice and informative.

    ReplyDelete

Post a Comment

Popular posts from this blog

Asynchronous Vs. Synchronous Communications

Synchronous (One thread):   1 thread -> |<---A---->||<----B---------->||<------C----->| Synchronous (multi-threaded):   thread A -> |<---A---->| \ thread B ------------> ->|<----B---------->| \ thread C ----------------------------------> ->|<------C----->|

WebSphere MQ Interview Questions

What is MQ and what does it do? Ans. MQ stands for MESSAGE QUEUEING. WebSphere MQ allows application programs to use message queuing to participate in message-driven processing. Application programs can communicate across different platforms by using the appropriate message queuing software products. What is Message driven process? Ans . When messages arrive on a queue, they can automatically start an application using triggering. If necessary, the applications can be stopped when the message (or messages) have been processed. What are advantages of the MQ? Ans. 1. Integration. 2. Asynchrony 3. Assured Delivery 4. Scalability. How does it support the Integration? Ans. Because the MQ is independent of the Operating System you use i.e. it may be Windows, Solaris,AIX.It is independent of the protocol (i.e. TCP/IP, LU6.2, SNA, NetBIOS, UDP).It is not required that both the sender and receiver should be running on the same platform What is Asynchrony? Ans. With messag

Advantages & Disadvantages of Synchronous / Asynchronous Communications?

  Asynchronous Communication Advantages: Requests need not be targeted to specific server. Service need not be available when request is made. No blocking, so resources could be freed.  Could use connectionless protocol Disadvantages: Response times are unpredictable. Error handling usually more complex.  Usually requires connection-oriented protocol.  Harder to design apps Synchronous Communication Advantages: Easy to program Outcome is known immediately  Error recovery easier (usually)  Better real-time response (usually) Disadvantages: Service must be up and ready. Requestor blocks, held resources are “tied up”.  Usually requires connection-oriented protocol