Skip Headers

Oracle® Containers for J2EE Enterprise JavaBeans Developer's Guide 10g Release 3 (10.1.3) B14428-01 |
| Home | Solution Area | Contents | Index

Previous | Next |

# Implementing an EJB 2.1 MDB

Table 17-1 summarizes the important parts of an EJB 2.1 MDB entity bean and the following procedure describes how to implement these parts. For a typical implementation, see "Using Java".

Table 17-1 Parts of an EJB 2.1 MDB Entity Bean

Part | Description
--- | ---
Bean implementation | This class must be declared as `public`, contain a `public`, empty, default constructor, one `public`, `void` `ejbCreate` method with no arguments, and no `finalize`() method. Implements `javax.ejb.MessageDrivenBean` to provide an empty implementation for lifecycle method `ejbRemove` and an implementation of the setMessageDrivenContext method. Implements `javax.jms.MessageListener` to provide an implementation of the `onMessage` method.

For more information, see "What is a Message-Driven Bean?".

Note: You can download EJB code examples from: `http://www.oracle.com/technology/tech/java/oc4j/demos`.

To implement an EJB 2.1 message-driven bean:

1. Implement the MDB entity bean:

   1. Implement a `public`, zero-argument constructor.

   2. Implement any methods that are private to the bean or package used for facilitating the business logic. This includes private methods that your public methods use for completing the tasks requested of them.

   3. Implement the `ejbCreate` method. The container invokes this method when it instantiates the MDB.

      The return type of the `ebjCreate` methods is void.

   4. Provide an empty implementation for each of the `javax.ejb.MessageDrivenBean` interface container callback methods.

   5. Implement a `setMessageDrivenContext` method that takes an instance of `MessageDrivenContext` (see "Implementing the setMessageDrivenContext Method").

   6. Implement the appropriate message listener interface:

      For a JMS message-driven bean, implement the `javax.jms.MessageListener` interface to provide the `onMessages` method with signature:

          public void onMessage(javax.jms.Message message)

      For a non-JMS message service provider, implement the message listener interface (or interfaces) it specifies.

      This method processes the incoming message. Most MDBs receive messages from a queue or a topic, then invoke an entity bean to process the request contained within the message.

2. Configure message service provider information (see "Using Deployment XML":

   1. Define the message connection factory and `Destination` used in the EJB deployment descriptor (`ejb-jar.xml`). Define if any durable subscriptions or message selectors are used.

      For more information, see:

      - "Configuring an EJB 2.1 MDB to Use a Non-J2CA Message Service Provider"

      - "Configuring an EJB 2.1 MDB to Use a J2CA Message Service Provider"

   2. If using resource references, define these in the `ejb-jar.xml` file and map them to their actual JNDI names in the OC4J-specific deployment descriptor (`orion-ejb-jar.xml`).

   3. If the MDB uses container-managed transaction demarcation, specify the `onMessage` method in the `<container-transaction>` element in the `ejb-jar.xml` file.

      All of the steps for an MDB should be in the `onMessage` method. Since the MDB is stateless, the `onMessage` method should perform all duties.

      In general, do not create the message service connection and session in the `ejbCreate` method.

      Note: If you are using OracleAS JMS (see "Oracle Application Server JMS (OracleAS JMS) Provider: File-Based"), then you can optimize your MDB by creating the JMS connection and session in the ejbCreate method and destroying them in the ejbRemove method.

## Using Java

Example 17-1 shows a typical implementation of an EJB 2.1 MDB.

Example 17-1 EJB 2.1 MDB Implementation

    import java.util.*;
    import javax.ejb.*;
    import javax.jms.*;
    import javax.naming.*;

    public class rpTestMdb implements MessageDrivenBean, MessageListener
    {
        private QueueConnection         m_qc    = null;
        private QueueSession            m_qs    = null;
        private QueueSender             m_snd   = null;
        private MessageDrivenContext    m_ctx   = null;

        /* Constructor, which is public and takes no arguments.*/
        public rpTestMdb()
        {
        }

        /* ----------------------------------------
        * Begin private methods. The methods below
        * are used internally.
        * -------------------------------------- */

    ...

        /* ------------------------------------------------------
        * Begin EJB-required methods. The methods below are called
        * by the container, and never called by client code.
        * ------------------------------------------------------- */

       /* ejbCreate method, declared as public (but not final or
        * static), with a return type of void, and with no arguments.
        */
        public void ejbCreate()
        {
        }

        /* setMessageDrivenContext method */
        public void setMessageDrivenContext(MessageDrivenContext ctx)
        {
          /* As with all EJBs, you must set the context in order to be
             able to use it at another time within the MDB methods. */
          m_ctx = ctx;
        }

    // Lifecycle Methods

        /* ejbRemove method */
        public void ejbRemove()
        {
        }

        /* ------------------------------------------------------
        * Begin JMS MessageListener-required methods. The methods
        * below are called by the container, and never called by
        * client code.
        * ------------------------------------------------------- */

       /**
        * Receives the incoming Message and displays the text.
        */
        public void onMessage(Message msg)
        {
           /* An MDB does not carry state for an individual client. */
           try
            {
                Context ctx = new InitialContext();
                // 1. Retrieve the QueueConnectionFactory using a
                // resource reference defined in the ejb-jar.xml file.
                QueueConnectionFactory qcf = (QueueConnectionFactory)
                    ctx.lookup("java:comp/env/jms/myQueueConnectionFactory");
                ctx.close();

                // 2. Create the queue connection
                m_qc = qcf.createQueueConnection();
                // 3. Create the session over the queue connection.
                m_qs = m_qc.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
                     // 4. Create the sender to send messages over the session.
                m_snd = m_qs.createSender(null);

                /* When the onMessage method is called, a message has been sent.
                   You can retrieve attributes of the message using the Message object.
                */
                String txt = ("mdb rcv: " + msg.getJMSMessageID());
                System.out.println(txt + " redel="
                    + msg.getJMSRedelivered() + " cnt="
                    + msg.getIntProperty("JMSXDeliveryCount"));

                /* Create a new message using the createMessage method.
                   To send it back to the originator of the other message,
                   set the String property of "RECIPIENT" to "CLIENT."
                   The client only looks for messages with string property CLIENT.
                   Copy the original message ID into new msg's Correlation ID for
                   tracking purposes using the setJMSCorrelationID method. Finally,
                   set the destination for the message using the getJMSReplyTo method
                   on the previously received message. Send the message using the
                   send method on the queue sender.
                */
                // 5. Create a message using the createMessage method
                Message rmsg = m_qs.createMessage();
                // 6. Set properties of the message.
                rmsg.setStringProperty("RECIPIENT", "CLIENT");
                rmsg.setIntProperty("count", msg.getIntProperty("JMSXDeliveryCount"));
                rmsg.setJMSCorrelationID(msg.getJMSMessageID());
                // 7. Retrieve the reply destination.
                Destination d = msg.getJMSReplyTo();
                //  8. Send the message using the send method of the sender.
                m_snd.send((Queue) d, rmsg);
                System.out.println(txt + " snd: " + rmsg.getJMSMessageID());
                /* close the connection*/
                m_qc.close();
            }
            catch (Throwable ex)
            {
                ex.printStackTrace();
            }
        }
    }

## Using Deployment XML

Using the `ejb-jar.xml` file, define the MDB name, class, JNDI reference, and JMS `Destination` type (queue or topic) in the `message-driven` element. If a topic is specified, you define whether it is durable. If you have used resource references, define the resource reference for both the connection factory and the `Destination` object.

Example 17-2 shows the `ejb-jar.xml` file `message-driven` element corresponding to the MDB shown in Example 17-1.

Note the following:

- MDB name specified in the `<ejb-name>` element.

- MDB class defined in the `<ejb-class>` element, which ties the `<message-driven>` element to the specific MDB implementation.

- JMS `Destination` type is a `Queue` that is specified in the `<message-driven-destination><destination-type>` element.

- Message selector specifies that this MDB only receives messages where the `RECIPIENT` is `MDB`.

  Note: You could also specify a topic in this type definition. If you did specify a `Topic` in the type, then you could also define the durability of the topic, which is specified in the `<message-driven-destination> <subscription-durability>` element as "`Durable`" or "`nonDurable`."

- The type of transaction to use is defined in the `<transaction-type>` element. The value can be `Container` or `Bean`. If `Container` is specified, define the `onMessage` method within the `<container-transaction>` element with the type of CMT support.

- The resource reference for the connection factory is defined in the `<resource-ref>` element; the resource reference for the `Destination` object is defined in the `<resource-env-ref>` element.

Example 17-2 ejb-jar.xml For an EJB 2.1 MDB

    ...
        <enterprise-beans>
            <message-driven>
                <display-name>testMdb</display-name>
                <ejb-name>testMdb</ejb-name>
                <ejb-class>rpTestMdb</ejb-class>
                <transaction-type>Container</transaction-type>
                <message-selector>RECIPIENT='MDB'</message-selector>
                <message-driven-destination>
                    <destination-type>javax.jms.Queue</destination-type>
                </message-driven-destination>
                <resource-ref>
                    <description>description</description>
                    <res-ref-name>jms/myQueueConnectionFactory</res-ref-name>
                    <res-type>javax.jms.QueueConnectionFactory</res-type>
                    <res-auth>Application</res-auth>
                    <res-sharing-scope>Shareable</res-sharing-scope>
                </resource-ref>
                <resource-env-ref>
                   <resource-env-ref-name>jms/persistentQueue             </resource-env-ref-name>
                 <resource-env-ref-type>javax.jms.Queue</resource-env-ref-type>
                </resource-env-ref>
            </message-driven>
        </enterprise-beans>

        <assembly-descriptor>
            <container-transaction>
                <method>
                    <ejb-name>testMdb</ejb-name>
                    <method-name>onMessage</method-name>
                    <method-params>
                        <method-param>javax.jms.Message</method-param>
                    </method-params>
                </method>
                <trans-attribute>Required</trans-attribute>
            </container-transaction>
        </assembly-descriptor>
    ...

If you were going to configure a durable `Topic` instead, then the `<message-driven-destination>` element would be configured Example 17-3.

Example 17-3 ejb-jar.xml For an EJB 2.1 MDB for a Durable Topic

    <message-driven-destination>
      <destination-type>javax.jms.Topic</destination-type>
      <subscription-durability>Durable</subscription-durability>
    </message-driven-destination>

For more information, see "Configuring an EJB 2.1 MDB to Use a Non-J2CA Message Service Provider".

## Implementing the setMessageDrivenContext Method

An MDB instance uses this method to retain a reference to its context. Message-driven beans have contexts that the container maintains and makes available to the beans. The bean may use the methods in the message-driven context to retrieve information about the bean, such as security, and transactional role. Refer to the Enterprise JavaBeans specification from Sun Microsystems for the full range of information that you can retrieve about the bean from the context.

The container invokes the `setMessageDrivenContext` method, after it first instantiates the bean, to enable the bean to retrieve the context. The container will never call this method from within a transaction context. If the bean does not save the context at this point, the bean will never gain access to the context.

Example 17-4 shows an MDB saving the message-driven context in the `ctx` variable.

Example 17-4 Implementing the setMessageDrivenContext Methods

    import javax.ejb.*;

    public class myBean implements MessageDrivenBean, MessageListener {
       MessageDrivenContext m_ctx;

        /* setMessageDrivenContext method */
        public void setMessageDrivenContext(MessageDrivenContext ctx)
        {
          /* As with all EJBs, you must set the context in order to be
             able to use it at another time within the MDB methods. */
          m_ctx = ctx;
        }

       // other methods in the bean
    }