Skip Headers

Oracle9iAS Containers for J2EE Enterprise JavaBeans Developer's Guide
Release 2 (9.0.3)

Part Number A97677-01
Go To Core Documentation
Core
Go To Platform Documentation
Platform
Go To Table Of Contents
Contents
Go To Index
Index

Go to previous page Go to next page

7
Message-Driven Beans

A Message-Driven Bean (MDB) is a Java Messaging Service (JMS) message listener that can reliably consume messages from a queue or a subscription of a topic. The advantage of using an MDB instead of a JMS message listener is that you can use the asynchronous nature of a JMS listener with the benefit of the EJB container performing the following:

An MDB is an easy method for creating a JMS message listener.

The following sections discuss the tasks in creating an MDB in Oracle9iAS Containers for J2EE (OC4J) and demonstrate MDB development with a basic configuration to use Oracle JMS as the resource provider.

Download the MDB example from the OC4J sample code page on the OTN web site.

MDB Overview

An MDB is a unique EJB whose function is to read or write JMS messages from a JMS Destination (topic or queue).

The OC4J MDB interacts with Oracle JMS, which must be installed and configured appropriately. Oracle JMS is installed and configured on an Oracle database. Within this database, the appropriate queue or table is created.

  1. The MDB opens a JMS connection to the database using a data source with a username and password. The data source represents the Oracle JMS resource provider and uses a JDBC driver to facilitate the JMS connection.

  2. The MDB opens a JMS session over the JMS connection.

  3. Any message for the MDB is routed to the onMessage method of the MDB from the queue or topic. Other clients may have access to the same queue or topic to put on messages for the MDB.

Figure 7-1 Demonstration of an MDB Interacting with an Oracle JMS Destination

Text description of mdba.gif follows

Text description of the illustration mdba.gif

Creating MDBs

MDBs interact with queues and topics furnished by the Oracle JMS resource provider. A full description of how to use this resource provider is discussed in the JMS chapter in the Oracle9iAS Containers for J2EE Services Guide.

The JMS chapter details the following steps that enable each resource provider:

  1. Install and configure the resource provider. For Oracle JMS, this includes the following:

    1. Create an RDBMS user through which the MDB connects to the database. Grant this user appropriate access privileges to perform Oracle JMS operations.

    2. Create the tables and queues to support the JMS Destination objects.

    3. Configure the Oracle JMS resource provider by configuring a data source with the capabilities that are appropriate for the functionality within your application.

  2. Configure the location of the resource provider in the OC4J XML files.

To create an MDB that uses the resource provider, perform the following steps:

  1. Implement the bean, which includes the following:

    1. The bean class must implement the javax.ejb.MessageDrivenBean and javax.jms.MessageListener interfaces, which includes the following:

      • the onMessage method in the MessageListener interface

      • the setMessageDrivenContext method in the MessageDrivenBean interface

    2. The bean class must implement the container callback methods that normally match methods in the EJB home interface. A remote, local, and home interface are not implemented with an MDB. However, some of the callback methods required for these interfaces are implemented in the bean implementation. These methods include the following:

      • an ejbCreate method

      • an ejbRemove method

  2. Create the MDB deployment descriptors.

    1. Define the JMS connection factory and Destination used in the EJB deployment descriptor. Define if any durable subscriptions are used.

    2. Map the JMS connection factory and Destination type to the MDB in the OC4J-specific deployment descriptor--orion-ejb-jar.xml.

    3. If the MDB is a container-managed transaction, specify the onMessage method in the <container-transaction> element.

  3. Create an EJB JAR file containing the bean and the deployment descriptors. Configure the application-specific application.xml file, create an EAR file, and install the EJB in OC4J.

The following sections demonstrate a simple MDB, using Oracle JMS as the resource provider. For directions on configuring other resource providers, see the JMS chapter in the Oracle9iAS Containers for J2EE Services Guide.

Install And Configure The Resource Provider

Before you can use the MDB within the application, you must choose and configure a resource provider for the JMS Destination objects used by the MDB. The following sections discuss how to configure Oracle JMS.

Create User and Assign Privileges

Create an RDBMS user through which the MDB connects to the database. Grant access privileges to this user to perform Oracle JMS operations. The privileges that you need depend on what functionality you are requesting. Refer to the Oracle9i Application Developer's Guide - Advanced Queuing for more information on privileges necessary for each type of function.

The following example creates MYUSER with privileges required for Oracle JMS operations:

create user MYUSER identified by MYPASSWORD;

grant connect, resource to MYUSER;

grant execute on sys.dbms_aqadm  to  MYUSER;
grant execute on sys.dbms_aq     to  MYUSER;
grant execute on sys.dbms_aqin   to  MYUSER;
grant execute on sys.dbms_aqjms  to  MYUSER;

connect MYUSER/MYPASSWORD;

You may need to grant other privileges, such as two-phase commit (requires FORCE ANY TRANSACTION) or system administration privileges, based on what the user needs.

Create JMS Destination Objects

Each resource provider requires its own method for creating the JMS Destination object. Refer to the Oracle9i Application Developer's Guide - Advanced Queuing for more information on the DBMS_AQADM packages and Oracle JMS messages types. For our example, Oracle JMS requires the following methods:


Note:

The SQL for creating the tables for the Oracle JMS example is included in the MDB example available from OTN.


  1. Create the tables that handle the JMS Destination (queue or topic).

    In Oracle JMS, both topics and queues use a queue table. The Oracle JMS example within this chapter creates two tables: QTque for a queue and QTtpc for a topic.

    To create the queue table, execute the following SQL:

    DBMS_AQADM.CREATE_QUEUE_TABLE(
            Queue_table            => 'QTque',
            Queue_payload_type     => 'SYS.AQ$_JMS_BYTES_MESSAGE',
            multiple_consumers     =>  false);
    
    

    The type of message is defined as one of the following:

    • Bytes: SYS.AQ$_JMS_BYTES_MESSAGE

    • Map: SYS.AQ$_JMS_MAP_MESSAGE

    • String: SYS.AQ$_JMS_STRING_MESSAGE

    • Text: SYS.AQ$_JMS_TEXT_MESSAGE

    • Object: SYS.AQ$_JMS_OBJECT_MESSAGE

    The third parameter denotes whether there are multiple consumers or not; thus, is always false for a queue and true for a topic.

    To create the topic table, execute the following SQL:

    DBMS_AQADM.CREATE_QUEUE_TABLE(
            Queue_table            => 'QTtpc',
            Queue_payload_type     => 'SYS.AQ$_JMS_BYTES_MESSAGE',
            multiple_consumers     => TRUE);
    
    
    
  2. Create the JMS Destination. If you are creating a topic, you must add each subscriber for the topic. This example chooses to use durable subscribers, which results in additional configuration within the deployment descriptors.

    The Oracle JMS example within this chapter requires a single topic with two subscribers--topic1 with MDBSUB and MDBSUB2--and a single queue--queue1.

    The following creates a topic called topic1 within the topic table QTtpc with max retries set to 2. After creation, two durable subscribers are added to the topic. Finally, the topic is started.

    DBMS_AQADM.CREATE_QUEUE( 'topic1', 'QTtpc');
    DBMS_AQADM.ADD_SUBSCRIBER('topic1', sys.aq$_agent('MDSUB', null, null));
    DBMS_AQADM.ADD_SUBSCRIBER('topic1', sys.aq$_agent('MDSUB2', null, null));
    DBMS_AQADM.START_QUEUE('topic1');
    
    

    The following creates a queue called queue1 within the queue table QTque with max retries set to 2. After creation, the queue is started.

    DBMS_AQADM.CREATE_QUEUE( 'queue1' , 'QTque');
    DBMS_AQADM.START_QUEUE('queue1');
    

Configure the DataSource

Configure the Oracle JMS resource provider by configuring a data source. The topics and queues connect to the database and use database tables and queues to facilitate messaging.

The type of data source you use depends on the functionality you want.

Transactional Functionality

For no transactions or single-phase transactions, you can use either an emulated or non-emulated data sources. For two-phase commit transaction support, you can use only a non-emulated data source.

Example 7-1 Non-Emulated With Thin JDBC Driver

The following example contains a non-emulated data source that uses the thin JDBC driver. It can support a two-phase commit transaction; it cannot support session pooling.

The example is displayed in the format of an XML definition; see the Oracle9iAS Containers for J2EE User's Guide for directions on adding a new data source to the configuration.

<data-source
  class="com.evermind.sql.OrionCMTDataSource"
  name="myDS"
  location="jdbc/MyDS"
  connection-driver="oracle.jdbc.driver.OracleDriver"
  username="myuser"
  password="mypasswd"
  url="jdbc:oracle:thin:@myhost.foo.com:1521:mydb"
  inactivity-timeout="30"
/>

Configure the Resource Provider

Identify the JNDI name of the data source that is to be used as the resource provider within the <resource-provider> element.

The following code sample shows how to configure the resource provider using XML syntax for Oracle JMS.

The following example demonstrates that the data source identified by "jdbc/CartEmulatedDS" is to be used as the Oracle JMS resource provider. This JNDI name is identified in the ejb-location element in Example 7-1.

<resource-provider class="oracle.jms.OjmsContext" name="cartojms1">
  <description> OJMS/AQ </description>
  <property name="datasource" value="jdbc/myDS"></property>
</resource-provider>

Bean Class Implementation

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

The following example is a MessageBean MDB. Its functionality is to print out a message sent to it through a durable topic. The topic is identified in the deployment descriptors.

As an MDB, it is responsible for the following:

Configure Deployment Descriptors

The deployment descriptors define MDB configuration in the <message-driven> element.

EJB Deployment Descriptor for the MDB

Within the EJB deployment descriptor (ejb-jar.xml), 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 must also define whether it is durable.

The following example demonstrates the deployment information for the MessageBean MDB in the <message-driven> element, as follows:

ejb-jar.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 
2.0//EN" "http://java.sun.com/dtd/ejb-jar_2_0.dtd">

<ejb-jar>
     <description>A demo cart bean package.</description>
     <display-name>A simple cart jar</display-name>

     <enterprise-beans>
     ...
       <message-driven>
         <description></description>
         <display-name>MessageBeanTpc</display-name>
         <ejb-name>MessageBeanTpc</ejb-name>
         <ejb-class>cart.ejb.MessageBean</ejb-class>
         <transaction-type>Container</transaction-type>
         <message-driven-destination>
            <destination-type>javax.jms.Topic</destination-type>
            <subscription-durability>Durable</subscription-durability>
         </message-driven-destination>
       </message-driven>
     ...
       <assembly-descriptor>
          <container-transaction>
              <method>
                 <ejb-name>MessageBeanTpc</ejb-name>
                 <method-name>onMessage</method-name>
              </method>
              <trans-attribute>Required</trans-attribute>
           </container-transaction>
       ...
       </assembly-descriptor>
     </enterprise-beans>

OC4J-Specific Deployment Descriptor

Once you have configured the MDB and the JMS Destination type, inform the container which JMS Destination to associate with the MDB. To identify the Destination that is to be associated with the MDB, map the Destination location and connection factory to the MDB through the <message-driven-deployment> element in the orion-ejb-jar.xml file.

The following is the orion-ejb-jar.xml deployment descriptor for the MessageBean example. It maps an Oracle JMS Topic to the MessageBean MDB, providing the following:

After you specify all of these in the <message-driven-deployment> element, the container knows how to map the MDB to the correct JMS Destination.

<enterprise-beans>
  <message-driven-deployment 
      connection-factory-location=
          "java:comp/resource/cartojms1/TopicConnectionFactories/aqTcf" 
      name="MessageBeanTpc" 
      destination-location="java:comp/resource/cartojms1/Topics/topic1" 
      subscription-name="MDBSUB"
      listener-threads=50 transaction-timeout=172800>
  ...
</enterprise-beans>

Deploy the Entity Bean

Archive your EJB into a JAR file. You deploy the MDB in the same way as the session bean, which "Prepare the EJB Application for Assembly" and "Deploy the Enterprise Application to OC4J" describe.

Accessing MDBs

The client sends a message to the MDB through a JMS Destination. The MDB is associated with the JMS Destination by the container.

To send a JMS message to an MDB, perform the following:

  1. Retrieve both the configured JMS Destination and its connection factory using a JNDI lookup.

  2. Create a connection from the connection factory. For a queue, start the connection.

  3. Create a session over the connection.

  4. Providing the retrieved JMS Destination, create a sender for a queue, or a publisher for a topic.

  5. Create the message.

  6. Send out the message using either the queue sender or the topic publisher.

  7. Close the queue session. Close the connection for either JMS Destination types.

The following code sends a message over a topic to the MessageBean MDB.

Context ic = new InitialContext();
/*1. Retrieve an Oracle JMS Topic and connection factory through JNDI */
topic = (Topic) ic.lookup("java:comp/resource/ojms/Topics/topic1");
/*Retrieve the Oracle JMS Topic connection factory */
topicConnectionFactory = (TopicConnectionFactory) ic.lookup
                ("java:comp/resource/ojms/TopicConnectionFactories/aqTcf");

/*2. Create a Topic connection */
topicConnection = topicConnectionFactory.createTopicConnection();

/*3. Create a Topic session over the connection */
topicSession = topicConnection.createTopicSession(true,
                                                  Session.AUTO_ACKNOWLEDGE);

/*4. Create a publisher to send a message to the MDB -- The createPublisher
   method is invoked off of the session object, but requires the retrieved
   topic as its input. */
topicPublisher = topicSession.createPublisher(topic);

/*5. Create the message to send to the MDB */  
message = topicSession.createTextMessage();

for (int i = 0; i < NUM_MSGS; i++) {
    message.setText("This is message " + (i + 1));
    System.out.println("Sending message: " +
    message.getText());
/*6. Send the message using the topic publisher */
    topicPublisher.publish(message);
}

/*7. After message is sent, close the connection */
topicConnection.close();

Using Logical Names in the JMS JNDI Lookup

If you have another EJB acting as the MDB client, you can retrieve the connection factory and the JMS Destination using logical references that have been configured in the client-side deployment descriptor.

The following defines logical names in the client-side deployment descriptor. If the client is a true Java client, this would be in its application-client.xml file. If the client is another EJB, these additions would be added in the ejb-jar.xml file.

The following shows an example of how to define a queue and a topic.

<resource-ref>
  <res-ref-name>jms/Queue/senderQueueConnectionFactory</res-ref-name>
  <res-type>javax.jms.QueueConnectionFactory</res-type>
  <res-auth>Container</res-auth>
  <res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>
<resource-env-ref>
  <resource-env-ref-name>jms/Queue/senderQueue</resource-env-ref-name>
  <resource-env-ref-type>javax.jms.Queue</resource-env-ref-type>
</resource-env-ref>
<resource-ref>
  <res-ref-name>jms/Topic/senderTopicConnectionFactory</res-ref-name>
  <res-type>javax.jms.TopicConnectionFactory</res-type>
  <res-auth>Container</res-auth>
  <res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>
<resource-env-ref>
  <resource-env-ref-name>jms/Topic/senderTopic</resource-env-ref-name>  
  <resource-env-ref-type>javax.jms.Topic</resource-env-ref-type>
</resource-env-ref>

Then, you map the logical names to actual names in the OC4J deployment descriptor. If the client is a true Java client, these additions would be within the orion-application-client.xml. If the client is another EJB, these additions are added to the orion-ejb-jar.xml.

The logical names in the client's deployment descriptor are mapped as follows:

The following example maps the logical names for the connection factories, topic and queue defined above in the client deployment descriptor to their actual JNDI names. Specifically, the topic defined logically as "jms/Topic/senderTopic" in the ejb-jar.xml file is mapped to its JNDI name of "java:comp/resource/cartojms1/Topics/topic1."

<session-deployment name="MyCart" max-instances="10" location="MyCart">

<resource-ref-mapping 
name="jms/Topic/senderTopicConnectionFactory"
location="java:comp/resource/cartojms1/TopicConnectionFactories/aqTcf"> </resource-ref-mapping> <resource-env-ref-mapping
name="jms/Topic/senderTopic"
location="java:comp/resource/cartojms1/Topics/topic1">
</resource-env-ref-mapping> <resource-ref-mapping
name="jms/Queue/senderQueueConnectionFactory"
location="java:comp/resource/cartojms1/QueueConnectionFactories/aqQcf"> </resource-ref-mapping> <resource-env-ref-mapping
name="jms/Queue/senderQueue"
location="java:comp/resource/cartojms1/Queues/queue1">
</resource-env-ref-mapping> </session-deployment>

Once the mapping is complete, you can modify your JNDI lookup to use the logical name, as follows:

Context ic = new InitialContext();
/*Retrieve an Oracle JMS Topic and connection factory through JNDI */
topic = (Topic) ic.lookup("jms/Topic/senderTopic");
/*Retrieve the Oracle JMS Topic connection factory */
topicConnectionFactory = (TopicConnectionFactory) ic.lookup
                ("jms/Topic/senderTopicConnectionFactory");



Go to previous page Go to next page
Oracle
Copyright © 2002 Oracle Corporation.

All Rights Reserved.
Go To Core Documentation
Core
Go To Platform Documentation
Platform
Go To Table Of Contents
Contents
Go To Index
Index