# EJB Programmer's Guide: Message-driven Beans

The content of this guide is the following:

1. Description of a Message-driven Bean
2. Developing a Message-driven Bean
3. Administration aspects
4. Running a Message-driven Bean
5. Transactional aspects
6. Example
7. Tuning Message-driven Bean Pool

The EJB 2.1 specification defines a new kind of EJB component for receiving asynchronous messages. This implements some type of "asynchronous EJB component method invocation" mechanism. The Message-driven Bean (also referred to as MDB in the following) is an Enterprise JavaBean, not an Entity Bean or a Session Bean, which plays the role of a JMS MessageListener.

The EJB 2.1 specification contains detailed information about MDB. The Java Message Service Specification 1.1 contains detailed information about JMS. This chapter focuses on the use of Message-driven beans within the JOnAS server.

## Description of a Message-driven Bean

A Message-driven Bean is an EJB component that can be considered as a JMS MessageListener, i.e., processing JMS messages asynchronously; it implements the onMessage(javax.jms.Message) method, defined in the javax.jms.MessageListener interface. It is associated with a JMS destination, i.e., a Queue for "point-to-point" messaging or a Topic for "publish/subscribe." The onMessage method is activated on receipt of messages sent by a client application to the corresponding JMS destination. It is possible to associate a JMS message selector to filter the messages that the Message-driven Bean should receive.

JMS messages do not carry any context, thus the onMessage method will execute without pre-existing transactional context. However, a new transaction can be initiated at this moment (refer to the "Transactional aspects" section for more details). The onMessage method can call other methods on the MDB itself or on other beans, and can involve other resources by accessing databases or by sending messages. Such resources are accessed the same way as for other beans (entity or session), i.e., through resource references declared in the deployment descriptor.

The JOnAS container maintains a pool of MDB instances, allowing large volumes of messages to be processed concurrently. An MDB is similar in some ways to a stateless session bean: its instances are relatively short-lived, it retains no state for a specific client, and several instances may be running at the same time.

## Developing a Message-driven Bean

The MDB class must implement the javax.jms.MessageListener and the javax.ejb.MessageDrivenBean interfaces. In addition to the onMessage method, the following must be implemented:

- A public constructor with no argument.
- public void ejbCreate(): with no arguments, called at the bean instantiation time. It may be used to allocate some resources, such as connection factories, for example if the bean sends messages, or datasources or if the bean accesses databases.
- public void ejbRemove(): usually used to free the resources allocated in the ejbCreate method.
- public void setMessageDrivenContext(MessageDrivenContext mdc): called by the container after the instance creation, with no transaction context. The JOnAS container provides the bean with a container context that can be used for transaction management, e.g., for calling setRollbackOnly(), getRollbackOnly(), getUserTransaction().

The following is an example of an MDB class:

    public class MdbBean  implements MessageDrivenBean, MessageListener {

        private transient MessageDrivenContext mdbContext;

        public MdbBean() {}

        public void setMessageDrivenContext(MessageDrivenContext ctx) {
            mdbContext = ctx;
        }

        public void ejbRemove() {}

        public void ejbCreate() {}

        public void onMessage(Message message) {
            try {
                TextMessage mess = (TextMessage)message;
                System.out.println( "Message received: "+mess.getText());
            }catch(JMSException ex){
                System.err.println("Exception caught: "+ex);
            }
        }
    }

The destination associated to an MDB is specified in the deployment descriptor of the bean. A destination is a JMS-administered object, accessible via JNDI. The description of an MDB in the EJB 2.0 deployment descriptor contains the following elements, which are specific to MDBs:

- the JMS acknowledgment mode: auto-acknowledge or dups-ok-acknowledge (refer to the JMS specification for the definition of these modes)
- an eventual JMS message selector: this is a JMS concept which allows the filtering of the messages sent to the destination
- a message-driven-destination, which contains the destination type (Queue or Topic) and the subscription durability (in case of Topic)

The following example illustrates such a deployment descriptor:

      <enterprise-beans>
        <message-driven>
          <description>Describe here the message driven bean Mdb</description>
          <display-name>Message Driven Bean Mdb</display-name>
          <ejb-name>Mdb</ejb-name>
          <ejb-class>samplemdb.MdbBean</ejb-class>
          <transaction-type>Container</transaction-type>
          <message-selector>Weight >= 60.00 AND LName LIKE 'Sm_th'</message-selector>
          <message-driven-destination>
            <destination-type>javax.jms.Topic</destination-type>
            <subscription-durability>NonDurable</subscription-durability>
          </message-driven-destination>
          <acknowledge-mode>Auto-acknowledge</acknowledge-mode>
        </message-driven>
      </enterprise-beans>

If the transaction type is "container," the transactional behavior of the MDB's methods are defined as for other enterprise beans in the deployment descriptor, as in the following example:

      <assembly-descriptor>
        <container-transaction>
          <method>
            <ejb-name>Mdb</ejb-name>
            <method-name>*</method-name>
          </method>
          <trans-attribute>Required</trans-attribute>
        </container-transaction>
      </assembly-descriptor>

For the onMessage method, only the Required or NotSupported transaction attributes must be used, since there can be no pre-existing transaction context.

For the message selector specified in the previous example, the sent JMS messages are expected to have two properties, "Weight" and "LName," for example assigned in the JMS client program sending the messages, as follows:

            message.setDoubleProperty("Weight",75.5);
            message.setStringProperty("LName","Smith");

Such a message will be received by the Message-driven bean. The message selector syntax is based on a subset of the SQL92. Only messages whose headers and properties match the selector are delivered. Refer to the JMS specification for more details.

The JNDI name of a destination associated with an MDB is defined in the JOnAS-specific deployment descriptor, within a jonas-message-driven element, as illustrated in the following:

      <jonas-message-driven>
        <ejb-name>Mdb</ejb-name>
        <jonas-message-driven-destination>
          <jndi-name>sampleTopic</jndi-name>
        </jonas-message-driven-destination>
      </jonas-message-driven>

Once the destination is established, a client application can send messages to the MDB through a destination object obtained via JNDI as follows:

`Queue q = context.lookup("sampleTopic");`

If the client sending messages to the MDB is an EJB component itself, it is preferable that it use a resource environment reference to obtain the destination object. The use of resource environment references is described in the JMS User's Guide (Writing JMS operations within an application component / Accessing the destination object section).

## Administration aspects

It is assumed at this point that the JOnAS server will make use of an existing JMS implementation, e.g., Joram, SwiftMQ.

The default policy is that the MDB developer and deployer are not concerned with JMS administration. This means that the developer/deployer will not create or use any JMS Connection factories and will not create a JMS destination (which is necessary for performing JMS operations within an EJB component, refer to the JMS User's Guide); they will simply define the type of destination in the deployment descriptor and identify its JNDI name in the JOnAS-specific deployment descriptor, as described in the previous section. This means that JOnAS will implicitly create the necessary administered objects by using the proprietary administration APIs of the JMS implementation (since the administration APIs are not standardized). To perform such administration operations, JOnAS uses wrappers to the JMS provider administration API. For Joram, the wrapper is org.objectweb.jonas_jms.JmsAdminForJoram (which is the default wrapper class defined by the jonas.service.jms.mom property in the jonas.properties file). For SwiftMQ, a com.swiftmq.appserver.jonas.JmsAdminForSwiftMQ class can be obtained from the SwiftMQ site.

For the purpose of this implicit administration phase, the deployer must add the 'jms' service in the list of the JOnAS services. For the example provided, the jonas.properties file should contain the following:

    jonas.services                 registry,security,jtm,dbm,jms,ejb // The jms service must be added
    jonas.service.ejb.descriptors  samplemdb.jar
    jonas.service.jms.topics       sampleTopic    // not mandatory

The destination objects may or may not pre-exist. The EJB server will not create the corresponding JMS destination object if it already exists. (Refer also to JMS administration). The `sampleTopic` should be explicitly declared only if the JOnAS Server is going to create it first, even if the Message-driven bean is not loaded, or if it is used by another client before the Message-driven bean is loaded. In general, it is not necessary to declare the `sampleTopic`.

JOnAS uses a pool of threads for executing Message-driven bean instances on message reception, thus allowing large volumes of messages to be processed concurrently. As previously explained, MDB instances are stateless and several instances may execute concurrently on behalf of the same MDB. The default size of the pool of thread is 10, and it may be customized via the jonas property jonas.service.ejb.mdbthreadpoolsize, which is specified in the jonas.properties file as in the following example:

        jonas.service.ejb.mdbthreadpoolsize   50

## Running a Message-driven Bean

To deploy and run a Message-driven Bean, perform the following steps:

- Verify that a registry is running.

- Start the Message-Oriented Middleware (the JMS provider implementation). Refer to the section "Launching the Message-Oriented Middleware."

- Create and register in JNDI the JMS destination object that will be used by the MDB.

  This can be done automatically by the JMS service or explicitly by the proprietary administration facilities of the JMS provider (JMS administration). The JMS service creates the destination object if this destination is declared in the jonas.properties file (as specified in the previous section).

- Deploy the MDB component in JOnAS.

  Note that, if the destination object is not already created when deploying an MDB, the container asks the JMS service to create it based on the deployment descriptor content.

- Run the EJB client application.
- Stop the application.

  When using JMS, it is very important to stop JOnAS using the jonas stop command; it should not be stopped directly by killing it.

### Launching the Message-Oriented Middleware

If the configuration property jonas.services contains the jms service, then the JOnAS JMS service will be launched and may try to launch a JMS implementation (a MOM).

For launching the MOM, three possibilities can be considered:

1. Launching the MOM in the same JVM as JOnAS

   This is the default situation obtained by assigning the `true` value to the configuration property jonas.service.jms.collocated in the jonas.properties file.

       jonas.services                security,jtm,dbm,jms,ejb // The jms service must be in the list
       jonas.service.jms.collocated  true

   In this case, the MOM is automatically launched by the JOnAS JMS service at the JOnAS launching time (command `jonas start`).

2. Launching the MOM in a separate JVM

   The Joram MOM can be launched using the command:

   `JmsServer`

   For other MOMs, the proprietary command should be used.

   The configuration property jonas.service.jms.collocated must be set to `false` in the jonas.properties file. Setting this property is sufficient if the JORAM's JVM runs on the same host as JONAS, and if the MOM is launched with its default options (unchanged a3servers.xml configuration file under JONAS_BASE/conf or JONAS_ROOT/conf if JONAS_BASE not defined).

       jonas.services               security,jtm,dbm,jms,ejb // The jms service must be in the list
       jonas.service.jms.collocated false

   To use a specific configuration for the MOM, such as changing the default host (which is localhost) or the default connection port number (which is 16010), requires defining the additional jonas.service.jms.url configuration property as presented in the following case.

3. Launching the MOM on another host

   This requires defining the jonas.service.jms.url configuration property. When using Joram, its value should be the Joram URL `joram://host:port` where `host` is the host name, and `port` is the connection port (by default, 16010). For SwiftMQ, the value of the URL is similar to the following: `smqp://host:4001/timeout=10000`.

       jonas.services                security,jtm,dbm,jms,ejb // The jms service must be in the list
       jonas.service.jms.collocated  false
       jonas.service.jms.url         joram://host2:16010

- Change Joram default configuration

  As mentioned previously, the default host or default connection port number may need to be changed. This requires modifying the a3servers.xml configuration file provided by the JOnAS delivery in JONAS_ROOT/conf directory. For this, JOnAS must be configured with the property jonas.service.jms.collocated set to `false`, and the property jonas.service.jms.url set to `joram://host:port`. Additionally, the MOM must have been previously launched with the JmsServer command. This command defines a `Transaction` property set to `fr.dyade.aaa.util.NullTransaction`. If the messages need to be persistent, replace the `-DTransaction=fr.dyade.aaa.util.NullTransaction` option with the `-DTransaction=fr.dyade.aaa.util.NTransaction` option. Refer to the Joram documentation for more details about this command. To define a more complex configuration (e.g., distribution, multi-servers), refer to the Joram documentation on http://joram.objectweb.org.

## Transactional aspects

Because a transactional context cannot be carried by a message (according to the EJB 2.0 specification), an MDB will never execute within an existing transaction. However, a transaction may be started during the onMessage method execution, either due to a "required" transaction attribute (container-managed transaction) or because it is explicitly started within the method (if the MDB is bean-managed transacted). In the second case, the message receipt will not be part of the transaction. In the first case, container-managed transaction, the container will start a new transaction before de-queueing the JMS message (the receipt of which will, thus, be part of the started transaction), then enlist the resource manager associated with the arriving message and all the resource managers accessed by the onMessage method. If the onMessage method invokes other enterprise beans, the container passes the transaction context with the invocation. Therefore, the transaction started at the onMessage method execution may involve several operations, such as accessing a database (via a call to an entity bean, or by using a "datasource" resource), or sending messages (by using a "connection factory" resource).

## Example

JOnAS provides examples that are located in the examples/src/mdb install directory.
samplemdb is a very simple example, the code of which is used in the previous topics for illustrating how to use Message-driven beans.
sampleappli is a more complex example that shows how the sending of JMS messages and updates in a database via JDBC may be involved in the same distributed transaction.
The following figure illustrates the architecture of this example application.

There are two Message-driven beans in this example:

- `StockHandlerBean` is a Message-driven bean listening to a topic and receiving Map messages. The `onMessage` method runs in the scope of a transaction started by the container. It sends a Text message on a Queue (OrdersQueue) and updates a Stock element by decreasing the stock quantity. If the stock quantity becomes negative, an exception is received and the current transaction is marked for rollback.

- `OrderBean` is another Message-driven bean listening on the OrdersQueue Queue. On receipt of a Text message on this queue, it writes the corresponding String as a new line in a file ("Order.txt").

The example also includes a CMP entity bean `Stock` that handles a stock table.

A Stock item is composed of a Stockid (String), which is the primary key, and a Quantity (int). The method decreaseQuantity(int qty) decreases the quantity for the corresponding stockid, but can throw a RemoteException "Negative stock."

The client application `SampleAppliClient` is a JMS Client that sends several messages on the topic StockHandlerTopic. It uses Map messages with three fields: "CustomerId," "ProductId," "Quantity." Before sending messages, this client calls the `EnvBean` for creating the StockTable in the database with known values in order to check the results of updates at the end of the test. Eleven messages are sent, the corresponding transactions are committed, and the last message sent causes the transaction to be rolled back.

### Compiling this example

To compile `examples/src/mdb/sampleappli`, use Ant with the `$JONAS_ROOT/examples/src/build.xml` file.

### Running this example

The default configuration of the JMS service in `jonas.properties` is the following:

     jonas.services                 jmx,security,jtm,dbm,jms,ejb  // The jms service must be added
     jonas.service.ejb.descriptors  sampleappli.jar
     jonas.service.jms.topics       StockHandlerTopic
     jonas.service.jms.queues       OrdersQueue
     jonas.service.jms.collocated   true

This indicates that the JMS Server will be launched in the same JVM as the JOnAS Server, and the JMS-administered objects `StockHandlerTopic` (Topic) and `OrdersQueue` (Queue) will be created and registered in JNDI, if not already existing.

- Run the JOnAS Server.

      jonas start

- Deploy the sampleappli container.

      jonas admin -a sampleappli.jar

- Run the EJB client.

      jclient sampleappli.SampleAppliClient

- Stop the server.

      jonas stop

## Tuning Message-driven Bean Pool

A pool is handled by JOnAS for each Message-driven bean. The pool can be configured in the JOnAS-specific deployment descriptor with the following tags:

### min-pool-size

This optional integer value represents the minimum instances that will be created in the pool when the bean is loaded. This will improve bean instance creation time, at least for the first beans. The default value is 0.

### max-cache-size

This optional integer value represents the maximum number of instances of ServerSession that may be created in memory. The purpose of this value is to keep JOnAS scalable. The policy is the following:
When the ConnectionConsumer ask for a ServerSession instance (in order to deliver a new message) JOnAS tries to give an instance from the ServerSessionPool. If the pool is empty, a new instance is created only if the number of yet created instances is smaller than the max-cache-size parameter. When the max-cache-size is reached, the ConnectionConsumer is blocked and it cannot deliver new messages until a ServerSession is eventually returned in the pool. A ServerSession is pushed into the pool at the end of the onMessage method.
The default value is no limit (this means that a new instance of ServerSession is always created when the pool is empty).

The values for `max-cache-size` should be set accordingly to `jonas.service.ejb.maxworkthreads` value. See Configuring JMS Service.

### example

      <jonas-ejb-jar>
        <jonas-message-driven>
          <ejb-name>Mdb</ejb-name>
          <jndi-name>mdbTopic</jndi-name>
          <max-cache-size>20</max-cache-size>
          <min-pool-size>10</min-pool-size>
        </jonas-message-driven>
      </jonas-ejb-jar>