Getting Started with Message-Driven Beans

This topic provides an overview of message-driven bean development. It contains the following sections:


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 bean environment, including making enough instances available for processing and message-acknowledgement.

Developing Message-Driven Beans in Workshop

In Workshop, you develop a message-driven bean by creating a class that extends weblogic.ejb.GenericMessageDrivenBean and implements javax.ejb.MessageDrivenBean and javax.jms.MessageListener. You annotate this class with an @MessageDriven annotation that specifies EJB characteristics.

You can get started easily in the IDE by using the WebLogic Message-Driven Bean template. When you use the template, the IDE generates code such as the following:

/**
 * GenericMessageDrivenBean subclass automatically generated by
 * Workshop.
 * 
 * Please complete the onMessage() method and review the MessageDriven
 * annotation to ensure it matches your intended use.
 */
@MessageDriven(ejbName = "MyMessageDrivenBean", 
    destinationJndiName = "MyMessageDrivenBeanJndiName", 
    destinationType = "javax.jms.Queue")
public class MyMessageDrivenBean 
    extends GenericMessageDrivenBean 
    implements MessageDrivenBean, MessageListener {
    
    private static final long serialVersionUID = 1L;

    /*
     * (non-Javadoc)
     * 
     * @see javax.jms.MessageListener#onMessage(javax.jms.Message)
     */
    public void onMessage(Message msg) {
        // IMPORTANT: Add your code here
    }
}

Note: To use the WebLogic Message Driven Bean template, in the Project Explorer right-click the package that will contain the bean, select New > Other, expand EJB, then click WebLogic Message Driven Bean.

Through the @MessageDriven annotation, you specify the type and JNDI name for the JMS destination with which the message-driven bean interacts. For more on destinations, see Topics and Queues below.

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.

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 @MessageDriven 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, or destination, 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.

You specify the destination type (topic or queue) and JndiName with attributes of the @MessageDriven annotation.

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:

@EjbLocalRefs( { 
    @EjbLocalRef(link = "Recording") })
@MessageDriven(ejbName = "Statistics", 
        destinationJndiName = "jms.EJBTutorialSampleJmsQ", 
        destinationType = "javax.jms.Queue")
public class StatisticsBean 
    extends GenericMessageDrivenBean 
    implements MessageDrivenBean, MessageListener {

    private static final long serialVersionUID = 1L;
    private RecordingHome recordingHome;

    public void ejbCreate() {
        try {
            javax.naming.Context ic = new InitialContext();
            recordingHome = (RecordingHome) ic
                    .lookup("java:/comp/env/ejb/Recording");
        } 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");

            // Placeholder logic for the rating
            Random randomGenerator = new Random();
            String rating = new Integer(randomGenerator.nextInt(5)).toString();

            // 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.


Still need help? Post a question on the Workshop newsgroup.