[ Team LiB ] Previous Section Next Section

How to Create Message-Driven Beans

Message-Driven beans are very similar to Stateless Session beans in terms of life cycle. There are a few major differences between Message-Driven beans and other types of beans, such as Session beans and Entity beans:

  • Message-Driven beans are server-side only components, so unlike other beans, they do not have home or component interfaces.

  • Message-Driven beans have one and only one business method, onMessage(), that is invoked by the container when a message is received. Other beans may have multiple business methods and their calls are serialized.

  • Each deployed Message-Driven bean is associated with the JMS destination specified in the deployment descriptor. Other beans can receive calls from any clients.

  • Due to their asynchronous nature, Message-Driven beans do not directly return values nor throw exceptions because the callers simply issue JMS messages and leave. The real caller is the container, which knows nothing about the business and hence is totally ignorant about the result. MDBs can signal the status of the operation by other means, such as sending a response as a distinct JMS message on another destination.

Message-Driven Bean Class

Message-Driven beans are implemented with just one class. The Message-Driven bean class is required to implement two interfaces: javax.ejb.MessageDrivenBean and javax.jms.MessageListener.

The javax.ejb.MessageDrivenBean interface is defined as follows:


package javax.ejb;
public interface MessageDrivenBean extends EnterpriseBean
{
 public void setMessageDrivenContect(MessageDrivenContext context)
       throws EJBException;
 public void ejbRemove() throws EJBException;
}

Although it isn't in the interface, you must also implement the creation method:


public void ejbCreate() throws CreationException;

Message-Driven Bean Life Cycle

Message-Driven bean instances can be in one of the two states: does-not-exist or method-ready. A Message-Driven bean instance's life cycle is much like that of a Stateless Session bean:

  • The container creates a new instance by calling its class's newInstance(), and then calling its setMessageDrivenContext(context) and ejbCreate() methods, in this order.

  • Once created, the bean is in the method-ready pool, ready to process messages sent to its associated JMS destination.

  • When the container decides to remove an instance, it calls that instance's ejbRemove() method and removes it from the pool. The instance will ultimately be garbage collected.

setMessageContect(context) is called by the container to provide the Message-Driven context, which can be saved for later use. The Message-Driven context simply extends javax.ejb.EJBContext with no additional methods. The javax.ejb.EJBContext interface is defined as follows:


package javax.ejb;
public interface EJBContext
{
 // transaction methods
 public javax.transaction.UserTransaction getUserTransaction() throws IllegalStateException;
 public Boolean getRollbackOnly() throws IllegalStateException;
 public void setRollbackOnly() throws IllegalStateException;

 // EJB home methods
 public EJBHome getEJBHome();
 public gEJBLocalHome getEJBLocalHome();

 // security methods
 public java.security.Principal getCallerPrincipal();
 public Boolean isCallerInRole(String roleName);
}

The home and security-related methods are not applicable and will throw runtime exceptions if invoked. Message-Driven beans do not have home interfaces, and the JMS messages are received asynchronously, so no sender information is available.

The transaction-related methods are the only ones provided for Message-Driven beans. Message-Driven beans can operate either in container-managed or bean-managed transactions.

Although getCallerPrincipal() and isCallerInRole() methods cannot be called, you can specify a security identity using <run-as>. This would be the identity used when invoking other EJBs, making RMI calls, and any services that accept security identities.

Handling the JMS Message

JMS messages are handled in the only MessageListener method:


public void onMessage(javax.jms.Message msg);

The parameter can be one of the predefined message classes, such as TextMessage, MapMessage, ObjectMessage, and so on, or a custom message class. Note that this method does not throw any exceptions; therefore, you must catch, handle, and log all exceptions.

Setting the Context

Any business logic can be coded in the onMessage() method. However, Message-Driven beans are often used as a communication vehicle to dispatch business requests to other beans or services. In all Message-Driven bean methods, you can access JNDI resources either by direct queries or by using java:comp/env lookups. It's common to store a local reference to the JNDI Environment Naming Context (ENC) in setMessageDrivenContext():


MessageDrivenContext context;
Context jndiContext = null;

public void setMessageDrivenContext(MessageDrivenContext ctxt) {
 context = ctxt;
 jndiContext = new InitialContext();
}

Deploying Message-Driven Beans

Message-Driven beans are deployed in the same way as Session beans and Entity beans: by using the deployment descriptor files ejb-jar.xml and weblogic-ejb-jar.xml. In ejb-jar.xml, the <message-driven> tag under <enterprise-beans> describes the Message-Driven bean. Much as for other types of beans, there can be tags within <message-driven>, such as <ejb-name>, <ejb-class>, and so on. But the tags related to the home and component interfaces are absent. Beyond those, there are these tags specifically for Message-Driven beans: <message-driven-destination>, <message-selector> and <acknowledge-mode>. The following is an example:


<ejb-jar>

 <enterprise-beans>

 <message-driven>
   <ejb-name>SnoopQueueMDB</ejb-name>
   <ejb-class>SnoopMDB</ejb-class>
   <transaction-type>Container</transaction-type>
   <message-driven-destination>
    <destination-type>javax.jms.Queue</destination-type>
   </message-driven-destination>
  </message-driven>

 </enterprise-beans>

</ejb-jar>

The <message-driven-destination> must contain a <destination-type> tag. The <message-selector> tag can take a value in JMS message selector syntax, so that this bean will respond to messages that satisfy the condition. This is discouraged for performance reasons because every message will have to check against the expression, which can be expensive. The <acknowledge-mode> tells the container how to acknowledge a JMS message to the sender. The default value is Auto-acknowledge; the other valid value is Dups-ok-acknowledge.

The weblogic-ejb-jar.xml describes the deployed beans in more detail. Its <destination-jndi-name> tag under <message-driven-descriptor> specifies the JNDI name for the JMS target this bean is listening on. The JMS target must exist and match the destination type (queue or topic) specified in ejb-jar.xml; if it does not, the deployment fails.


<weblogic-ejb-jar>

  <weblogic-enterprise-bean>
   <ejb-name>SnoopQueueMDB</ejb-name>
   <message-driven-descriptor>
    <pool>
     <max-beans-in-free-pool>10</max-beans-in-free-pool>
     <initial-beans-in-free-pool>2</initial-beans-in-free-pool>
    </pool>
    <destination-jndi-name>weblogic.examples.jms.exampleQueue</destination-jndi-name>
   </message-driven-descriptor>
  </weblogic-enterprise-bean>

</Weblogic-ejb-jar>

The <jndi-name> tag is useless because the home interface cannot be retrieved. Configuring the pooling is similar to how it is done for Stateless Session beans.

In WebLogic 8.1, the <dispatch_policy> tag can be used to assign MDBs to a configured execute queue. Previously, that tag applied only to Session and Entity beans.

    [ Team LiB ] Previous Section Next Section