# Message-driven Beans

1. What is a Message-driven Bean
2. Developing a Message-driven Bean
3. Administration aspects
4. Running a Message-driven Bean
5. Transactional aspects
6. Example

The EJB 2.0 specification defines a new kind of EJB component in order to receive asynchronous messages. This implements some kind of "asynchronous EJB component method invocation" mechanism. The Message-driven Bean (sometimes called MDB in the following) is an Enterprise JavaBean, neither an Entity Bean nor a Session Bean, which plays the role of a JMS MessageListener.

Details about MDB should be found in the EJB 2.0 specification, while details about JMS should be found in the Java Message Service Specification 1.0.2. This chapter focuses on the use of Message-driven beans within the JOnAS EJB server.

## What is a Message-driven Bean

A Message-driven Bean is an EJB component which may be considered as a JMS MessageListener, i.e. which processes JMS messages asynchronously: it implements the onMessage(javax.jms.Message) method, defined in the javax.jms.MessageListener interface. It is associated to a JMS destination, i.e. a Queue for "point to point" messaging, or a Topic for "publish/subscribe". The onMessage method will be activated on the reception 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 the Message-driven Bean should receive.

JMS messages do not carry any context, therefore the onMessage method will execute without pre-existing transactional context. However a new transaction may be initiated at this moment, see the Transactional aspects section for more details. Of course, the onMessage method may call other methods on the MDB itself or on other beans, and may 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 will maintain a pool of MDB instances, allowing to process large volumes of messages concurrently. A MDB is similar in some way to a stateless session bean: its instances are relatively short-lived, retain 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 instanciation time, it may be used to allocate some resources such as connection factories if the bean send messages or datasources 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 may be used for transaction management, e.g. for calling setRollbackOnly(), getRollbackOnly(), getUserTransaction().

The following is an example of 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 a MDB is specified in the deployment descriptor of the bean. A destination is a JMS administered object accessible via JNDI. The description of a MDB in the EJB 2.0 deployment descriptor contains the following elements which are specific to MDBs:

- the JMS acknowledgement mode : auto-acknowledge or dups-ok-acknowledge (see 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 behaviour of MDBs 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 attribute must be used, since there can be no pre-existing transaction context.

For the message selector specified in the above example, it is expected that the sent JMS messages 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. See the JMS specification for more details.

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

      <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 may 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 uses a resource environment reference to obtain the destination object. The use of resource environment references is described in the EJB/JMS User's Guide (Writing JMS operations within an enterprise bean / Accessing the destination object section).

## Administration aspects

We considere at this point that the JOnAS EJB Server will make use of an existing JMS implementation, e.g. Joram, SwiftMQ, ...

The default policy is that the MDB developer and deployer will not care about JMS administration. This means that the developer/deployer will not create or use any JMS Connection factory and will not create any JMS destination (as it is necessary for performing JMS operations within a session or entity bean, see EJB/JMS User's Guide), she/he will just define the type of the destination in the deployment descriptor and its JNDI name in the JOnAS specific deployment descriptor, as explained in the previous section. This means that the necessary administered objects will be implicitly created at EJB server starting time, and that for this purpose the EJB server will make use of 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, in case of Joram, the wrapper is org.objectweb.jonas_jms.JmsAdminForJoram (this is the default wrapper class that is specified in the jonas.service.jms.mom property of the $JONAS_ROOT/config/jonas.properties file), in case of SwiftMQ, you may get a com.swiftmq.appserver.jonas.JmsAdminForSwiftMQ class from the SwiftMQ site. For the purpose of this implicit administration phase, the deployer must specify in the jonas.properties file the administered objects (comma separated) to be created, and add the 'jms' service in the list of the JOnAS services included in the EJB server. For the provided example, the jonas.properties file should contain:

        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

In that case, the EJB server will use the JMS provider administration API to create a JMS Topic and to register it in JNDI with the name "sampleTopic".

Of course the destination objects could pre-exist. If the deployer would like to make use of well known destinations of an existing messaging system, it can simply use their JNDI name in the JOnAS specific deployment descriptor, and let the jonas.service.jms.topics and jonas.service.jms.queues properties empty. Note that if you fill one of these properties with a JNDI name, and that an object with the same name is already registered in JNDI, the EJB server will not create the corresponding JMS destination object(see also JMS Administration).

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

        jonas.service.ejb.mdbthreadpoolsize   50

## Running a Message-driven Bean

In order to deploy and run a Message-driven Bean the following steps should be followed:

- A registry should be running.

- The Message-Oriented Middleware (the JMS provider implementation) should be started. See the section Launching the Message-Oriented Middleware.

- The JMS destination administered object that will be used by the MDB should be created and registered in JNDI.

  This is done automatically by declaring it in the jonas.properties file (as specified in the previous section) or explicitly by the proprietary administration facilities of the JMS provider (JMS Administration).

- Deploy the MDB component on the EJB server. I.e. launch the EJB server.

  `EJBServer`

- Run the EJB client application.
- Stop the application: when using JMS, it is very important that the EJB server is stopped by using the JonasAdmin -s command, and not directly by killing it.

### Launching the Message-Oriented Middleware

JOnAS will use the MOM for some administration operations, and thus will need a wrapper to the proprietary administration API of the MOM. The wrapper for Joram is org.objectweb.jonas_jms.JmsAdminForJoram, it is delivered with JOnAS. The wrapper for SwiftMQ is com.swiftmq.appserver.jonas.JmsAdminForSwiftMQ, you may get it from the SwiftMQ site. You should position the jonas property jonas.service.jms.mom in the jonas.properties file to this wrapper. If the jonas property jonas.services contains the jms service, then the JOnAS JMS service will be launched, and will eventually try to launch a JMS implementation (a MOM).

For launching the MOM, three possibilities may be considered:

1. Launching the MOM automatically in the EJB server JVM

   This can only be done with the MOM default options, and by assigning to the jonas property jonas.service.jms.collocated the `true` value (its default value !) in the jonas.properties file.

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

   In that case, the MOM will be launched automatically at the EJB server launching time (command `EJBServer`).

2. Launching the MOM in a separate JVM on the same host

   The Joram MOM may be launched with its default options by the command:

   `JmsServer`

   For other MOMs, the proprietary command should be used.

   In that case, the jonas property jonas.service.jms.collocated must be set to `false` in the jonas.properties file.

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

3. Launching the MOM on another host

   You may launch the MOM on a separate host. In this case, you must specify to the EJB server that the MOM is running on another host through the jonas property jonas.service.jms.url in the jonas.properties file. In case of Joram, its value should be the Joram URL `joram://host:port` where `host` is the host name, and `port` the default Joram port number, i.e. 16010. In case of SwiftMQ, the value of the URL is something like `smqp://host:4001/timeout=10000`.

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

If you intend to change the default MOM configuration, the MOM may be directly launched by the proprietary command (in the jonas.properties file, the jonas property jonas.service.jms.collocated must be set to false, and the jonas.service.jms.url property should be set if you run the MOM on a separate host). In the case of Joram, the command is:

        java -DTransaction=NullTransaction fr.dyade.aaa.agent.AgentServer 0 ./s0

This corresponds to the default options used by the `JmsServer` command. If your messages need to be persistent, you should replace the `-DTransaction=NullTransaction` option by the `-DTransaction=ATransaction` option. See the Joram documentation for more details about this command. The directory where Joram is explicitly launched should contain the `a3servers.xml` configuration file (and the corresponding `a3config.dtd` file). A default `a3servers.xml` file and the `a3config.dtd` file (used by the `JmsServer` command) are respectively provided in $JONAS_ROOT/config and $JONAS_ROOT/xml directories; this a3servers.xml file specifies that the MOM runs on the localhost using the Joram default port number. To change the MOM configuration (distribution, multi-servers, ...), see the Joram documentation on http://www.objectweb.org/joram.

## Transactional aspects

Since according the the EJB 2.0 specification, a transactional context may not be carried by a message, a MDB will never execute within an existing transaction. However, a transaction may be started during the onMessage method execution (either because of a "required" transaction attribute (container-managed transaction) or because it is explicitely 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 dequeuing the JMS message (the receipt of which will thus be part of the started transaction), 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), sending messages (by using a "connection factory" resource).

## Example

JOnAS provides a couple of examples located in the example/src/mdb directory of your installation
samplemdb is very sample and the code of this example is used in the previous topics for illustrating how to use Message driven beans.
sampleappli is a little more complex one that shows how the sending of JMS messages and updates in a data base via JDBC may be involved in a same distributed transaction.
the following figure illustrates the architecture of this little application

In this application there are two message driven beans:

- 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 to 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")

and 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 of a Quantity (int), the method decreaseQuantity(int qty) decreases the quantity for the corresponding stockid but may 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 well known values in order to be able to check the results of updates at the end of the test. 11 messages are sent, the corresponding transactions are committed, the last message sent causes the transaction to be rolled back.

### Compiling this example

On examples/src/mdb/sampleappli, run the compile script, make if you use gnuMake or compile.sh on Unix or compile.bat on Windows.

### Running this example

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

        jonas.services		   security,jtm,dbm,jms,ejb       // The jms service must be added
        jonas.service.jms.topic        StockHandlerTopic
        jonas.service.jms.queues	   OrdersQueue
        jonas.service.jms.collocated   false

This means that the JMS Server will be launched in a separate JVM than the EJBServer, and the JMS administered objects StockHandlerTopic (Topic) and OrdersQueue (Queue)must be created and registered in JNDI.

- Make sure that a "registry" is running
- The JOnAS classes and prerequired classes for JOnAS must be accessible from the classpath:

      . $JONAS_ROOT/bin/unix/config_env

- Run the JmsServer:

      JmsServer&

- Run the EJBServer:

      EJBServer&

- Run the EJB client:

      java sampleappli.SampleAppliClient

- Stops the server

      JonasAdmin -s