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.

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. 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_M, and subsequently deletes all records corresponding to SimpleToken_M from the underlying database.

@EjbLocalRefs( { 
    @EjbLocalRef(link = "SimpleToken_M") })
@MessageDriven(defaultTransaction = MessageDriven.DefaultTransaction.NOT_SUPPORTED, 
        messageSelector = "Command = 'Delete'", 
        ejbName = "DeleteViaQMD", 
        destinationJndiName = "jms/SamplesAppMDBQ", 
        destinationType = "javax.jms.Queue")
public class DeleteViaQMDBean 
    extends GenericMessageDrivenBean 
    implements MessageDrivenBean, MessageListener {

    private static final long serialVersionUID = 1L;

    private SimpleTokenHome_M tokenHome;

    public void ejbCreate() {
        try {
            javax.naming.Context ic = new InitialContext();
            tokenHome = (SimpleTokenHome_M) ic
                    .lookup("java:/comp/env/ejb/SimpleToken_M");
        } 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_M) 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; 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 @MessageDriven annotation and use the Properties view to set the transactionType attribute to CONTAINER and the defaultTransaction attribute 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.

Related Topics

@MessageDriven Annotation


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