Oracle® Application Server Containers for J2EE Enterprise JavaBeans Developer's Guide
10g Release 2 (10.1.2) Part No. B15505-02 |
|
Previous |
Next |
The following sections discuss the tasks in creating an MDB in Oracle Application Server Containers for J2EE (OC4J) and demonstrate MDB development with a basic configuration to use either Oracle Application Server JMS (OracleAS JMS) or Oracle JMS as the JMS provider.
Download the MDB example used in this chapter from the OC4J sample code page at on the OTN Web site.
A Message-Driven Bean (MDB) is a Java Message Service (JMS) message listener that can reliably consume messages from a queue or a topic. An MDB uses the asynchronous nature of a JMS listener with the benefit of the EJB container, which does the following:
The EJB container creates a consumer of type QueueReceiver
or TopicSubscriber
for the listener.
At deployment time, the EJB container registers the MDB with the consumer, which is either a QueueReceiver
or TopicSubscriber
, and its factory.
The EJB container specifies the message acknowledgment mode.
Within normal JMS objects, a JMS message listener exists and must explicitly specify the consumer and its factory within its code. When you use MDBs, the container specifies the consumer and its factory for you; thus, an MDB is an easy method for creating a JMS message listener. You still have to retrieve the objects and create them given the interface, but the container does most of the work for you.
The OC4J MDB interacts with a JMS provider. This chapter highlights two JMS providers, OracleAS JMS and Oracle JMS, each of which must be installed and configured appropriately.
OracleAS JMS is installed internally within the OC4J code base.
Oracle JMS (Advanced Queuing) is installed and configured within an Oracle database. Before using Oracle JMS, you must create the appropriate queue or table in the database.
Note: A full description of how to use each JMS provider is discussed in the JMS chapter in the Oracle Application Server Containers for J2EE Services Guide. In addition, for information on security, see the Oracle Application Server Containers for J2EE Security Guide. |
The following are generic steps to create and enable an MDB with a JMS provider:
Install the JMS provider.
Configure the JMS provider, the Destination
objects for the MDB, and connection details for the MDB where the provider is installed.
Configure OC4J with the JMS provider details in the OC4J XML files.
Implement the MDB and map the JMS Destination
objects used in its deployment descriptors.
This chapter describes how to implement each of these steps with both the OracleAS JMS and Oracle JMS providers. Each section uses an MDB example that is available for download from the OC4J sample code page at on the OTN Web site.
The main MDB implementation and the EJB deployment descriptor can be the same for both JMS types and is shown in the "MDB Example". The OC4J-specific deployment descriptor for this MDB and the JMS configuration is different for each JMS type, so these are described specifically in each of the provider sections.
The MDB can process incoming asynchronous requests. 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 send messages for the MDB. Most MDBs receive messages from a queue or a topic, then invoke an entity bean to process the request contained within the message.
The steps to create an MDB, which are shown in the following sections, are as follows:
Implement the bean, as shown in "MDB Example".
Create the MDB deployment descriptors.
Define the JMS connection factory and Destination
used in the EJB deployment descriptor (ejb-jar.xml
). Define if any durable subscriptions or message selectors are used. See "EJB Deployment Descriptor (ejb-jar.xml) for the MDB" for details.
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
).
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. Do not create the JMS connection and session in the ejbCreate
method. However, if you are using OracleAS JMS, then you can optimize your MDB by creating the JMS connection and session in the ejbCreate method and destroying them in the ejbRemove method.
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 MDB implementation and the ejb-jar.xml
deployment descriptor can be exactly the same for the OracleAS JMS or Oracle JMS providers—if you use resource references for the JNDI lookup of the connection factory and the Destination
object. The orion-ejb-jar.xml
deployment descriptor contains provider-specific configuration, including the mapping of the resource references. See "MDB Using OracleAS JMS" and "MDB Using Oracle JMS" for the specific configuration in the orion-ejb-jar.xml
deployment descriptor.
Note: The example used for the MDB example uses resource references, so that the MDB is generic. If you want to see how to explicitly define a JNDI string for each JMS provider, see "Client Access of MDB", as the client uses both explicit JNDI strings as well as resource references. |
The major points to do when you implement an MDB are as follows:
Note: See the EJB specification for the full details on all aspects of implementing a MDB. |
The bean class must be defined as public
(not final
or abstract
).
The bean class must implement the javax.ejb.MessageDrivenBean
and javax.jms.MessageListener
interfaces, which include the following:
The bean class must implement the container callback methods that normally match methods in the EJB home interface. Remote, local, and home interfaces 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:
Example 9-1 MDB Implementation
The following MDB example—rpTestMdb
MDB—prints out a message sent to it through a queue and responds. The queue is identified in the deployment descriptors and the JMS configuration. In the onMessage
method, the MDB creates a new message to be sent to the client. It sets the message selector property RECIPIENT
to be for the CLIENT
. Then, it sets the reply destination and sends the new message to the JMS client.
This example shows how to receive a message from a queue and send out a response. You can receive a message in several ways. This example uses the methods of the Message
object to retrieve all attributes of the message.
To send out a response to a queue, you must first set up a sender, which requires the following:
Retrieve the QueueConnectionFactory
object. This example uses a resource reference of "jms/myQueueConnectionFactory
," which is defined in the ejb-jar.xml
file and mapped to the actual JNDI name in the orion-ejb-jar.xml
file.
Create the JMS queue connection using the createQueueConnection
method of the QueueConnectionFactory
object.
Create a JMS session over the connection using the createQueueSession
method of the QueueConnection
object.
Once the session is set up, then create a sender that uses the session through the createSender
method of the QueueSession
object.
These steps are implemented as follows:
private QueueConnection m_qc = null; private QueueSession m_qs = null; private QueueSender m_snd = null; QueueConnectionFactory qcf = (QueueConnectionFactory) ctx.lookup("java:comp/env/jms/myQueueConnectionFactory"); m_qc = qcf.createQueueConnection(); m_qs = m_qc.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); m_snd = m_qs.createSender(null);
Once the sender is created, you can send any message using the send
method of the QueueSender
object. This example puts together a response from the received message and then use the sender to send out that response.
Create a message using the createMessage
method of the Message
object.
Set properties of the message using methods of the Message
object, such as setStringProperty
and setIntProperty
.
This example retrieves the destination for its response through the getJMSReplyTo
method of the Message
object. The destination was initialized in the message by the sender.
Send out the response using the sender through the send
method of the QueueSender
object. Provide the destination and the response message.
Message rmsg = m_qs.createMessage(); rmsg.setStringProperty("RECIPIENT", "CLIENT"); rmsg.setIntProperty("count", msg.getIntProperty("JMSXDeliveryCount")); rmsg.setJMSCorrelationID(msg.getJMSMessageID()); Destination d = msg.getJMSReplyTo(); m_snd.send((Queue) d, rmsg);
Example 9-2 MDB Implementation
The following is the complete example of the MDB that receives a message and sends back a response.
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() { } /* 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; } /* ejbCreate method, declared as public (but not final or * static), with a return type of void, and with no arguments. */ public void ejbCreate() { } /* ejbRemove method */ public void ejbRemove() { } /** * onMessage method * 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(); /*You create the queue connection first, then a session over the connection. Once the session is set up, then you create a sender */ // 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(); } } }
Note: The entire MDB example is available on OTN from the OC4J sample code page athttp://www.oracle.com/technology/tech/java/oc4j/demos/ on the OTN Web site.
|
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 define whether it is durable. If you have used resource references, define the resource reference for both the connection factory and the Destination
object.
The following example demonstrates the deployment information for the rpTestMdb
MDB in the <message-driven>
element, as follows:
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
.
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. See "Using a Logical Name When Client Accesses the MDB" for a full discussion on resource references for JMS object types.
<?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> <display-name>Mdb Test</display-name> <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> </ejb-jar>
If you were going to configure a durable Topic
instead, then the <message-driven-destination>
element would be configured as follows:
<message-driven-destination> <destination-type>javax.jms.Topic</destination-type> <subscription-durability>Durable</subscription-durability> </message-driven-destination>
Note: The entire MDB example is available on OTN from the OC4J sample code page athttp://www.oracle.com/technology/tech/java/oc4j/demos/ on the OTN Web site.
|
The OC4J-specific deployment descriptor (orion-ejb-jar.xml
) for this MDB and the JMS provider configuration necessary is shown in the following sections:
Instructions on how a client sends a JMS message to the MDB is discussed in "Client Access of MDB".
The MDB can process incoming asynchronous requests using OracleAS JMS. When you use OracleAS JMS, this JMS provider is already available since it is bundled with OC4J. And all configuration for the JMS provider occurs within the OC4J XML files; thus, only steps three and four (as listed in "MDB Overview") are necessary.
Note: The entire MDB example is available on OTN from the OC4J sample code page athttp://www.oracle.com/technology/tech/java/oc4j/demos/ on the OTN Web site.
|
Figure 9-1 shows how a client sends an asynchronous request directly to the OracleAS JMS queue or topic that is located internally within OC4J. The MDB receives the message directly from OracleAS JMS.
Figure 9-1 Demonstration of an MDB Interacting with an OracleAS JMS Destination
The following sections demonstrate an MDB that uses OracleAS JMS as the JMS provider.
Create the OC4J-Specific Deployment Descriptor to Use OracleAS JMS
Note: A full description of how to use each JMS provider is available in the JMS chapter in the Oracle Application Server Containers for J2EE Services Guide. |
OracleAS JMS is automatically enabled. You only configure the JMS Destination
objects used by the MDB. If your MDB accesses a database for inquiries and so on, then you can configure the DataSource
used. See "JMS Destination Object Configuration" for the JMS configuration. For information on data source configuration, see the Data Source chapter in the Oracle Application Server Containers for J2EE Services Guide.
Configure the topic or queue in the jms.xml
file to which the client sends all messages that are destined for the MDB. The name, location, and connection factory for either Destination
type must be specified.
The following jms.xml
file configuration specifies a queue—named jms/Queue/rpTestQueue
—that is used by the rpTestMdb
example. The queue connection factory is defined as jms/Queue/myQCF
. In addition, a topic is defined named jms/Topic/rpTestTopic
, with a connection factory of jms/Topic/myTCF
.
<?xml version="1.0" ?> <!DOCTYPE jms-server PUBLIC "OracleAS JMS server" "http://xmlns.oracle.com/ias/dtds/jms-server.dtd"> <jms-server port="9128"> <queue location="jms/Queue/rpTestQueue"> </queue> <queue-connection-factory location="jms/Queue/myQCF"> </queue-connection-factory> <topic location="jms/Topic/rpTestTopic"> </topic> <topic-connection-factory location="jms/Topic/myTCF"> </topic-connection-factory> <!-- path to the log-file where JMS-events/errors are stored --> <log> <file path="../log/jms.log" /> </log> </jms-server>
The OC4J-specific deployment descriptor configures the following:
Specify the Destination
and connection factory JNDI locations to the MDB through the <message-driven-deployment>
element in the orion-ejb-jar.xml
file. See "Specify the Destination and Connection Factory" for full details.
Associate any logical names defined as resource references in the ejb-jar.xml
file to the correct queue or topic, which, for OracleAS JMS, is defined in the jms.xml
file. You could have several topics and queues defined in the jms.xml
file. See "Map Any Resource References to JNDI Names" for full details on mapping the resource references in the orion-ejb-jar.xml
file.
Since this example uses resource references in the ejb-jar.xml
file, the orion-ejb-jar.xml
file maps these logical names to the actual JNDI names of the connection factory and the JMS Destination
object, which are defined in the jms.xml
file. In this example, the MDB uses a queue that is defined in the jms.xml
file as jms/Queue/rpTestQueue
. The queue connection factory is defined in the jms.xml
file as jms/Queue/myQCF
.
Map the Destination
and connection factory JNDI locations 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 rpTestMdb
example. It maps a JMS Queue
to the rpTestMdb
MDB, providing the following:
MDB name, as defined in the <ejb-name>
in the EJB deployment descriptor, is specified in the name
attribute.
JMS Destination
, as defined in the jms.xml
file, is specified in the destination-location
attribute.
JMS Destination
Connection
Factory
, as defined in the jms.xml
file, is specified in the connection-factory-location
attribute.
If this was a topic, then a durable topic name, which is user-defined, is specified in the subscription-name
attribute.
Listener threads, as defined in the listener-threads
attribute, is an optional parameter. The listener threads are spawned off when MDBs are deployed and are used to listen for incoming JMS messages on the topic or queue. These threads concurrently consume JMS messages. The default is one thread. Topics always have only one thread.
Once all of these are specified in the <message-driven-deployment>
element, the container knows how to map the MDB to the correct JMS Destination
.
<enterprise-beans>...
<message-driven-deployment name="
rpTestMdb"
connection-factory-location="jms/Queue/myQCF"
destination-location="jms/Queue/rpTestQueue" >
</message-driven-deployment>
...
</enterprise-beans>
If you wanted to specify a topic, you must also include the subscription name, as follows:
<enterprise-beans> <message-driven-deployment name="rpTestMdb"connection-factory-location="jms/Queue/myQCF"
destination-location="jms/Queue/rpTestQueue"
subscription-name="MDBSUB" > ... </enterprise-beans>
Note: You cannot use logical names in these fields. You must specify the full JNDI syntax for both the connection factory and theDestination object.
|
When you define logical names as resource references for your connection factory and Destination
object, you have to map these to the actual JNDI names.
Map the resource reference for the queue connection factory in the <resource-ref-mapping>
element. In the rpTestMdb
example, the logical name for the connection factory is jms/myQueueConnectionFactory
. This must be mapped to the JNDI string of jms/Queue/myQCF
, which is defined in the jms.xml
file.
Map the resource reference for the Destination
object in the <resource-env-ref-mapping>
element. In the rpTestMdb
example, the logical name for the queue is jms/persistentQueue
. This is mapped to the JNDI string of jms/Queue/rpTestQueue
, which is defined in the jms.xml
file.
<resource-ref-mapping name="jms/myQueueConnectionFactory" location="jms/Queue/
myQCF"/> <resource-env-ref-mapping name="jms/persistentQueue" location="jms/Queue/
rpTestQueue" />
Example 9-3 The orion-ejb-jar.xml file for the rpTestMdb Example
The following lists the complete orion-ejb-jar.xml
file for the rpTestMdb
example. It includes both the definition of the OracleAS JMS objects and the resource reference mappings.
<enterprise-beans> <message-driven-deployment name="testMdb" connection-factory-location="jms/Queue/myQCF" destination-location="jms/Queue/rpTestQueue" listener-threads="1"> <resource-ref-mapping name="jms/myQueueConnectionFactory" location="jms/Queue/myQCF"/> <resource-env-ref-mapping name="jms/persistentQueue" location="jms/Queue/rpTestQueue" /> </message-driven-deployment> </enterprise-beans> <assembly-descriptor> <default-method-access> <security-role-mapping name="<default-ejb-caller-role>" impliesAll="true" /> </default-method-access> </assembly-descriptor>
Archive your EJB into a JAR file. You deploy the MDB the same way as the session bean, which is detailed in Prepare the EJB Application for Assembly and "Deploy the Enterprise Application to OC4J".
Note: Instructions on how a client sends a JMS message to the MDB is discussed in "Client Access of MDB". |
The MDB processes incoming asynchronous requests using Oracle JMS (Advanced Queuing), as follows:
Caution: MDBs only work with certain versions of the Oracle database. See the certification matrix in the JMS chapter of the Oracle Application Server Containers for J2EE Services Guide for more information. |
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 provider and uses a JDBC driver to facilitate the JMS connection.
The MDB opens a JMS session over the JMS connection.
Any message for the MDB is routed to the onMessage
method of the MDB.
At any time, the client can send a message to the Oracle JMS topic or queue on which MDBs are listening. The Oracle JMS topic or queue is located in the database.
Note: The entire MDB example is available on OTN from the OC4J sample code page athttp://www.oracle.com/technology/tech/java/oc4j/demos/ on the OTN Web site.
|
Figure 9-2 Demonstration of an MDB Interacting with an Oracle JMS Destination
The following sections demonstrate an MDB that uses Oracle JMS as the JMS provider.
Create the OC4J-Specific Deployment Descriptor to Use Oracle JMS
Note: A full description of how to use Oracle JMS provider is discussed in the JMS chapter in the Oracle Application Server Containers for J2EE Services Guide. Also, see the Oracle9i Application Developer's Guide - Advanced Queuing. |
You or your DBA must install Oracle JMS according to theOracle9i Application Developer's Guide - Advanced Queuing and generic database manuals. Once you have installed and configured this JMS provider, you must apply additional configuration for each MDB. This includes the following:
You or your DBA should create an RDBMS user through which the MDB connects to the database. Grant this user appropriate access privileges to perform Oracle JMS operations. See "Create User and Assign Privileges".
You or your DBA should create the tables and queues to support the JMS Destination
objects. See "Create JMS Destination Objects".
Note: The following sections use SQL for creating queues, topics, their tables, and assigning privileges that is provided within the MDB demo on OTN from the OC4J sample code page athttp://www.oracle.com/technology/tech/java/oc4j/demos/ on the OTN Web site.
|
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 theOracle9i Application Developer's Guide - Advanced Queuing for more information on privileges necessary for each type of function.
The following example creates jmsuser
, which must be created within its own schema, with privileges required for Oracle JMS operations. You must be a SYS
DBA
to execute these statements.
DROP USER jmsuser CASCADE ; GRANT connect, resource,AQ_ADMINISTRATOR_ROLE TO jmsuser IDENTIFIED BY jmsuser ; GRANT execute ON sys.dbms_aqadm TO jmsuser; GRANT execute ON sys.dbms_aq TO jmsuser; GRANT execute ON sys.dbms_aqin TO jmsuser; GRANT execute ON sys.dbms_aqjms TO jmsuser; connect jmsuser/jmsuser;
You may need to grant other privileges, such as two-phase commit or system administration privileges, based on what the user needs. See the JTA chapter in the Oracle Application Server Containers for J2EE Services Guide for the two-phase commit privileges.
Each JMS provider requires its own method for creating the JMS Destination
object. Refer to theOracle9i 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 on OTN from the OC4J sample code page athttp://www.oracle.com/technology/tech/java/oc4j/demos/ on the OTN Web site.
|
Create the tables that handle the JMS Destination
(queue or topic).
In Oracle JMS, both topics and queues use a queue table. The rpTestMdb
JMS example creates a single table: rpTestQTab
for a queue.
To create the queue table, execute the following SQL:
DBMS_AQADM.CREATE_QUEUE_TABLE( Queue_table => 'rpTestQTab', Queue_payload_type => 'SYS.AQ$_JMS_MESSAGE', sort_list => 'PRIORITY,ENQ_TIME', multiple_consumers => false, compatible => '8.1.5');
The multiple_consumers
parameter denotes whether there are multiple consumers or not; thus, is always false for a queue and true for a topic.
Create the JMS Destination
. If you are creating a topic, you must add each subscriber for the topic. The rpTestMdb
JMS example requires a single queue—rpTestQueue
.
The following creates a queue called rpTestQueue
within the queue table rpTestQTab
. After creation, the queue is started.
DBMS_AQADM.CREATE_QUEUE( Queue_name => 'rpTestQueue', Queue_table => 'rpTestQTab'); DBMS_AQADM.START_QUEUE( queue_name => 'rpTestQueue');
If you wanted to add a topic, then the following example shows how you can create a topic called rpTestTopic
within the topic table rpTestTTab
. After creation, two durable subscribers are added to the topic. Finally, the topic is started and a user is granted a privilege to it.
Note: Oracle AQ uses theDBMS_AQADM.CREATE_QUEUE method to create both queues and topics.
|
DBMS_AQADM.CREATE_QUEUE_TABLE( Queue_table => 'rpTestTTab', Queue_payload_type => 'SYS.AQ$_JMS_MESSAGE', multiple_consumers => true, compatible => '8.1.5'); DBMS_AQADM.CREATE_QUEUE( 'rpTestTopic', 'rpTestTTab'); DBMS_AQADM.ADD_SUBSCRIBER('rpTestTopic', sys.aq$_agent('MDSUB', null, null)); DBMS_AQADM.ADD_SUBSCRIBER('rpTestTopic', sys.aq$_agent('MDSUB2', null, null)); DBMS_AQADM.START_QUEUE('rpTestTopic');
Note: The names defined here must be the same names used to define the queue or topic in theorion-ejb-jar.xml file.
|
To use the Oracle JMS provider, you must configure the following in the OC4J XML files:
Configure a data source for the database where the Oracle JMS provider is installed. The JMS topics and queues use database tables and queues to facilitate messaging. The type of data source you use depends on the functionality you want.
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 9-4 Emulated DataSource With Thin JDBC Driver
The following example contains an emulated data source that uses the thin JDBC driver. To support a two-phase commit transaction, use a non-emulated data source. For differences between emulated and non-emulated data sources, see the Data Source chapter in the Oracle Application Server Containers for J2EE Services Guide.
The example is displayed in the format of an XML definition; see the Oracle Application Server Containers for J2EE User's Guide for directions on adding a new data source to the configuration through the EM tool.
<data-source class="com.evermind.sql.DriverManagerDataSource" name="OracleDS" location="jdbc/emulatedOracleCoreDS" xa-location="jdbc/xa/emulatedOracleXADS" ejb-location="jdbc/emulatedDS" connection-driver="oracle.jdbc.driver.OracleDriver" username="jmsuser" password="jmsuser" url="jdbc:oracle:thin:@myhost.foo.com:1521:mydb" />
Customize this data source to match your environment. For example, substitute the host name, port, and SID of your database for mysun:1521:orcl
.
Note: Instead of providing the password in the clear, you can use password indirection. For details, see the Oracle Application Server Containers for J2EE Services Guide. |
Identify the JNDI name of the data source that is to be used as the Oracle JMS provider within the <resource-provider>
element.
If this is to be the JMS provider for all applications (global), configure the global application.xml
file.
If this is to be the JMS provider for a single application (local), configure the orion-application.xml
file of the application.
The following code sample shows how to configure the JMS provider using XML syntax for Oracle JMS.
class
attribute—The Oracle JMS provider is implemented by the oracle.jms.OjmsContext
class, which is configured in the class
attribute.
property
attribute—Identify the data source that is to be used as this JMS provider in the property
element. The topic or queue connects to this data source to access the tables and queues that facilitate the messaging.
The following example demonstrates that the data source identified by "jdbc/emulatedDS
" is to be used as the Oracle JMS provider. This JNDI name is identified in the ejb-location
element in Example 9-4. If this example used a non-emulated data source, then the name would be the same as in the location
element.
<resource-provider class="oracle.jms.OjmsContext" name="myProvider"> <description> OJMS/AQ </description> <property name="datasource" value="jdbc/emulatedDS"></property> </resource-provider>
The OC4J-specific deployment descriptor configures the following:
Specify the Destination
and connection factory JNDI locations to the MDB through the <message-driven-deployment>
element in the orion-ejb-jar.xml
file. See "Specify the Destination and Connection Factory" for full details.
Associate any logical names defined as resource references in the ejb-jar.xml
file to the correct queue or topic, which, for Oracle JMS, was defined in the database through SQL. You could have several topics and queues defined in database. See "Map Any Resource References to JNDI Names" for full details on mapping the resource references in the orion-ejb-jar.xml
file.
Since this example uses resource references in the ejb-jar.xml
file, the orion-ejb-jar.xml
file maps these logical names to the actual JNDI names of the connection factory and the JMS Destination
object, which are defined in the database. In this example, the MDB uses a queue that is defined in the database as rpTestQueue
. The queue connection factory is not defined in the database, so any name can be used. For consistency, the queue connection factory name is myQCF
.
Map the Destination
and connection factory JNDI locations 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 rpTestMdb
example. It maps a JMS Queue
to the rpTestMdb
MDB, providing the following:
MDB name, as defined in the <message-driven><ejb-name>
in the EJB deployment descriptor, is specified in the name
attribute.
JMS Destination
Connection
Factory
, as specified by the user, is specified in the connection-factory-location
attribute. The Oracle JMS syntax for the connection factory is "java:comp/resource
" + JMS provider name + "TopicConnectionFactories
" or "QueueConnectionFactories
" + a user defined name. The user-defined name can be anything and does not match any other configuration. The xxxConnectionFactories
details what type of factory is being defined. For this example, the JMS provider name is defined in the <resource-provider>
element in the application.xml
file as myProvider
.
For a queue connection factory: Since the JMS provider name is myProvider
and you decide to use a name of myQCF
, the connection factory name is "java:comp/resource/myProvider/QueueConnectionFactories/myQCF
".
For a topic connection factory: Since the JMS provider name is myProvider
and you decide to use a name of myTCF
, the connection factory name is "java:comp/resource/myProvider/TopicConnectionFactories/myTCF
".
The user defined names, as shown above by myQCF
and myTCF
, are not used for anything else in your logic. So, any name can be chosen.
JMS Destination
, as defined in the database, is specified in the destination-location
element. The Oracle JMS syntax for the Destination
is "java:comp/resource
" + JMS provider name + "Topics
" or "Queues
" + Destination
name. The Topic
or Queue
details what type of Destination
is being defined. The Destination
name is the actual queue or topic name defined in the database.
For this example, the JMS provider name is defined in the <resource-provider>
element in the application.xml
file as myProvider
. In the database, the topic name is rpTestQueue
.
For a queue: If the JMS provider name is myProvider
and the queue name is rpTestQueue
, then the JNDI name for the queue as "java:comp/resource/myProvider/Queues/rpTestQueue
."
For a topic: If the JMS provider name is myProvider
and the topic name is rpTestTopic
, then the JNDI name for the topic as "java:comp/resource/myProvider/Topics/rpTestTopic
."
If this was a topic, then a durable topic name, which is user-defined, is specified in the subscription-name
attribute.
Listener threads are an optional parameter and defined in the listener-threads
attribute. The listener threads are spawned off when MDBs are deployed and are used to listen for incoming JMS messages on the topic or queue. These threads concurrently consume JMS messages. The default is one thread. Topics always use only one thread; queues can use more than one.
Transaction timeout, as defined in the transaction-timeout
attribute, is an optional parameter. This attribute controls the transaction timeout interval (in seconds) for any container-managed transactional MDB. The default is one day or 86,400 seconds. If the transaction has not completed in this time frame, the transaction is rolled back and the message is redelivered back to the Destination
object.
The MDB transaction-timeout attribute applies only to CMT MDBs that use Oracle JMS as the JMS provider. This attribute setting has no effect on BMT MDBs or any MDBs that use OC4J JMS:
JMS behavior with Oracle Application Server—JMS attempts to redeliver the message (defaults to five attempts and is set on the DBMS_AQADM.CREATE_QUEUE
method when creating the queue in the database), after which the message is moved to the exception queue. You can browse messages in the exception queue using SQL*Plus. For more information on setting redelivery attempts and browsing the exception queue, refer to Oracle9i Application Developer's Guide - Advanced Queuing.
JMS behavior with OC4J—The transaction-timeout setting does not work for CMT MDBs that use OC4J JMS. The timeout is always one day and cannot be modified. When the timeout occurs, OC4J JMS redelivers the message indefinitely, until the delivery is successful. You cannot set a retry limit.
In addition, the global transaction-timeout attribute defined in the server.xml file does not have any effect on MDBs.
Once all of these are specified in the <message-driven-deployment>
element, the container knows how to map the MDB to the correct JMS Destination
.
<message-driven-deployment name="testMdb" connection-factory-location= "java:comp/resource/myProvider/QueueConnectionFactories/myQCF" destination-location="java:comp/resource/myProvider/Queues/rpTestQueue" listener-threads="5">
If you wanted to specify a topic, you must also include the subscription name, as follows:
<enterprise-beans> <message-driven-deployment name="rpTestMdb" connection-factory-location= "java:comp/resource/myProvider/TopicConnectionFactories/myTCF" destination-location="java:comp/resource/cartojms1/Topics/rpTestTopic" subscription-name="MDBSUB" listener-threads=1 > ... </enterprise-beans>
Note: You cannot use logical names in these fields. You must specify the full JNDI syntax for both the connection factory and theDestination object.
|
When you define logical names as resource references for your connection factory and Destination
object, you have to map these to the actual JNDI names.
Map the resource reference for the queue connection factory in the <resource-ref-mapping>
element. In the rpTestMdb
example, the logical name for the connection factory is jms/myQueueConnectionFactory
. This must be mapped to the JNDI string of java:comp/resource/myProvider/QueueConnectionFactories/myQCF.
Map the resource reference for the Destination
object in the <resource-env-ref-mapping>
element. In the rpTestMdb
example, the logical name for the queue is jms/persistentQueue
. This is mapped to the JNDI string of java:comp/resource/myProvider/Queues/rpTestQueue
.
See "Specify the Destination and Connection Factory" for how the Oracle JMS JNDI syntax was derived.
<resource-ref-mapping name="jms/myQueueConnectionFactory" location="java:comp/resource/myProvider/QueueConnectionFactories/myQCF"/> <resource-env-ref-mapping name="jms/persistentQueue" location="java:comp/resource/myProvider/Queues/rpTestQueue" />
Example 9-5 The orion-ejb-jar.xml file for the rpTestMdb Example
The following lists the complete orion-ejb-jar.xml
file for the rpTestMdb
example. It includes both the definition of the Oracle JMS objects and the resource reference mappings.
<enterprise-beans> <message-driven-deployment name="testMdb" connection-factory-location= "java:comp/resource/myProvider/QueueConnectionFactories/myQCF" destination-location="java:comp/resource/myProvider/Queues/rpTestQueue" listener-threads="5"> <resource-ref-mapping name="jms/myQueueConnectionFactory" location="java:comp/resource/myProvider/QueueConnectionFactories/myQCF"/> <resource-env-ref-mapping name="jms/persistentQueue" location="java:comp/resource/myProvider/Queues/rpTestQueue" /> </message-driven-deployment> </enterprise-beans> <assembly-descriptor> <default-method-access> <security-role-mapping name="<default-ejb-caller-role>" impliesAll="true" /> </default-method-access> </assembly-descriptor>
Archive your MDB 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.
Note: Instructions on how a client sends a JMS message to the MDB is discussed in "Client Access of MDB". |
The client sends a message to the MDB through a JMS Destination
. The client can retrieve the JMS Destination
and connection factory either through using its explicit name or by a logical name. The following sections describe both methods for retrieving the JNDI name.
Using a Logical Name When Client Accesses the MDB
Note: You may have to add the JNDI properties if the client is not co-located with the MDB. The examples provided in the following sections do not include setting the JNDI properties. See "Setting JNDI Properties" for instructions on setting these properties. |
Within your client, you can use the actual JNDI name to retrieve the JMS Destination
objects. Both OracleAS JMS and Oracle JMS have their own naming methodology, as explained in the following sections:
Accessing Oracle JMS Destination with Explicit JNDI Names
Note: Alternatively, you can specify all of the JNDI names for theDestination and JMS provider objects as resource references in your orion-ejb-jar.xml file. See "Using a Logical Name When Client Accesses the MDB" for more information.
|
The JNDI lookup for OracleAS JMS requires the OracleAS JMS Destination
and connection factory as defined by you within the jms.xml
file, prepended with "java:comp/env/
." See "JMS Destination Object Configuration" to see how the queue and topic for OracleAS JMS is configured.
Note: If you decide to use logical names instead, you would use the same JNDI syntax. Logical names are recommended, because they are portable. See "Using a Logical Name When Client Accesses the MDB" for more information. |
To lookup a queue in the JNDI lookup for the testResourceProvider
example using OracleAS JMS are as follows:
//Lookup the Queue queue = (Queue)jndiContext.lookup("java:comp/env/jms/Queue/rpTestQueue"); //Lookup the Queue Connection factory queueConnectionFactory = (QueueConnectionFactory) jndiContext.lookup("java:comp/env/jms/Queue/myQCF");
To lookup a topic, you would have slightly different strings, designating a topic rather than a queue, as follows:
//Lookup the Topic topic = (Topic)jndiContext.lookup("java:comp/env/jms/Topic/rpTestTopic"); //Lookup the Connection factory topicConnectionFactory = (TopicConnectionFactory) jndiContext.lookup("java:comp/env/jms/Topic/myTCF");
Note that the same names for the topic and the connection factory are used in the client's configuration, the jms.xml
, and the MDB deployment descriptors.
The JNDI lookup—when using Oracle JMS—requires the Oracle JMS Destination
and connection factory syntax, which is the same naming convention as described for the connection-factory-location
and destination-location
attributes in "Specify the Destination and Connection Factory".
Note: If you decide to use logical names instead, you would use the same JNDI syntax. See "Using a Logical Name When Client Accesses the MDB" for more information. |
In your JNDI lookup, the implementation would be as follows for both a queue and a topic (See Example 9-6 for the full example):
/* Retrieve an Oracle JMS Queue through JNDI */ queue = (Queue) ic.lookup("java:comp/resource/myProvider/Queues/rpTestQueue"); /*Retrieve the Oracle JMS Queue connection factory */ queueConnectionFactory = (QueueConnectionFactory) ic.lookup ("java:comp/resource/myProvider/QueueConnectionFactories/myQCF"); /* Retrieve an Oracle JMS Topic through JNDI */ topic = (Topic) ic.lookup("java:comp/resource/myProvider/Topics/rpTestTopic"); /*Retrieve the Oracle JMS Topic connection factory */ topicConnectionFactory = (TopicConnectionFactory) ic.lookup ("java:comp/resource/myProvider/TopicConnectionFactories/myTCF");
Whether or not the implementation uses logical names or the actual JNDI names, the client sends a JMS message to the MDB by doing the following:
Retrieve both the configured JMS Destination
and its connection factory using a JNDI lookup.
Create a connection from the connection factory. If you are receiving messages for a queue, then start the connection.
Create a session over the connection.
Providing the retrieved JMS Destination
, create a sender for a queue, or a publisher for a topic.
Create the message.
Send out the message using either the queue sender or the topic publisher.
Close the queue session. Close the connection for either JMS Destination
types.
Example 9-6 Servlet Client Sends Message to Queue
public final class testResourceProvider extends HttpServlet { private String resProvider = "myResProvider"; private HashMap msgMap = new HashMap(); Context ctx = new InitialContext(); public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { doPost(req, res); } public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { //Retrieve the name of the JMS provider from the request, which is // to be used in creating the JNDI string for retrieval String rp = req.getParameter ("provider"); if (rp != null) resProvider = rp; try { // 1a. Look up the Queue Connection Factory QueueConnectionFactory qcf = (QueueConnectionFactory) ctx.lookup ("java:comp/resource/" + resProvider + "/QueueConnectionFactories/myQCF"); // 1b. Lookup the Queue Queue queue = (Queue) ctx.lookup ("java:comp/resource/" + resProvider + "/Queues/rpTestQueue"); // 2 & 3. Retrieve a connection and a session on top of the connection // 2a. Create queue connection using the connection factory. QueueConnection qconn = qcf.createQueueConnection(); // 2a. We're receiving msgs, so start the connection. qconn.start(); // 3. create a session over the queue connection. QueueSession qsess = qconn.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); // 4. Since this is for a queue, create a sender on top of the session. //This is used to send out the message over the queue. QueueSender snd = sess.createSender (q); drainQueue (sess, q); TextMessage msg = null; /* Send msgs to queue. */ for (int i = 0; i < 3; i++) { // 5. Create message msg = sess.createTextMessage(); msg.setText ("TestMessage:" + i); // set property of the recipient to be the MDB //and set the reply destination. msg.setStringProperty ("RECIPIENT", "MDB"); msg.setJMSReplyTo(q); //6. send the message using the sender. snd.send (msg); // You can store the messages IDs and sent-time in a map (msgMap), // so that when messages are received, you can verify if you // *only* received those messages that you were // expecting. See receiveFromMDB() method where msgMap gets used. msgMap.put (msg.getJMSMessageID(), new Long (msg.getJMSTimestamp())); } // receive a reply from the MDB. receiveFromMDB (sess, q); //7. Close sender, session, and connection for queue snd.close(); sess.close(); qconn.close(); } catch (Exception e) { System.err.println ("** TEST FAILED **"+ e.toString()); e.printStackTrace(); } finally { } } /* * Receive any msgs sent to us via the MDB */ private void receiveFromMDB (QueueSession sess, Queue q) throws Exception { //The MDB sends out a message (as a reply) to this client. The MDB sets // the receipient as CLIENT. Thus, we will only receive msgs that have // RECIPIENT set to 'CLIENT' QueueReceiver rcv = sess.createReceiver (q, "RECIPIENT = 'CLIENT'"); int nrcvd = 0; long trtimes = 0L; long tctimes = 0L; // First msg needs to come from MDB. May take a little while //Receiving Messages for (Message msg = rcv.receive (30000); msg != null; msg = rcv.receive (30000)) { nrcvd++; String rcp = msg.getStringProperty ("RECIPIENT"); // Verify if msg in message Map // We check the msgMap to see if this is the message that we are // expecting. String corrid = msg.getJMSCorrelationID(); if (msgMap.containsKey(corrid)) { msgMap.remove(corrid); } else { System.err.println ("** received unexpected message [" + corrid + "] **"); } } rcv.close(); } /* * Drain messages from queue */ private int drainQueue (QueueSession sess, Queue q) throws Exception { QueueReceiver rcv = sess.createReceiver (q); int nrcvd = 0; /* * First drain any old msgs from queue */ for (Message msg = rcv.receive(1000); msg != null; msg = rcv.receive(1000)) nrcvd++; rcv.close(); return nrcvd; } }
If you want to use a logical name in your client application code, then define the logical name in one of the following XML files:
A standalone Java client—in the application-client.xml
file
An EJB that acts as a client—the ejb-jar.xml
file
For JSPs and servlets that act as clients—the web.xml
file
Map the logical name to the actual name of the topic or queue name in the OC4J deployment descriptors.
You can create logical names for the connection factory and Destination
objects, as follows:
The connection factory is identified in the client's XML deployment descriptor file within a <resource-ref>
element.
The logical name that you want the connection factory to be identified as is defined in the <res-ref-name>
element.
The connection factory class type is defined in the <res-type>
element as either javax.jms.QueueConnectionFactory
or javax.jms.TopicConnectionFactory
.
The authentication responsibility (Container
or Bean
) is defined in the <res-auth>
element.
The sharing scope (Shareable
or Unshareable
) is defined in the <res-sharing-scope>
element.
The JMS Destination
—the topic or queue—is identified in a <resource-env-ref>
element.
The logical name that you want the topic or queue to be identified as is defined in the <resource-env-ref-name>
element.
The Destination
class type is defined in the <resource-env-ref-type>
element as either javax.jms.Queue
or javax.jms.Topic
.
The following shows an example of how to specify logical names for a topic.
<resource-ref> <res-ref-name>myTCF</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>rpTestTopic</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 descriptors. The actual names, or JNDI names, are different in OracleAS JMS than in Oracle JMS. However, the mapping is defined in one of the following files:
For a standalone Java client—the orion-application-client.xml
For an EJB acting as a client—the orion-ejb-jar.xml
For JSPs and servlets acting as a client—the orion-web.xml
file.
The logical names in the client's deployment descriptor are mapped as follows:
The logical name for the connection factory defined in the <resource-ref>
element is mapped to its JNDI name in the <resource-ref-mapping>
element.
The logical name for the JMS Destination
defined in the <resource-env-ref>
element is mapped to its JNDI name in the <resource-env-ref-mapping>
element.
See the following sections for how the mapping occurs for both OracleAS JMS and Oracle JMS:
The JNDI name for the OracleAS JMS Destination and connection factory is defined by you within the jms.xml
file. As shown in "JMS Destination Object Configuration", the JNDI names for the topic and the topic connection factory are as follows:
The JNDI name for the topic is "jms/Topic/rpTestTopic
."
The JNDI name for the topic connection factory is "jms/Topic/myTCF
."
Prepend both of these names with "java:comp/env/
" and you have the mapping in the orion-ejb-jar.xml
file as follows:
<resource-ref-mapping name="myTCF" location="java:comp/env/jms/Topic/myTCF"> </resource-ref-mapping> <resource-env-ref-mapping name="rpTestTopic" location="java:comp/env/jms/Topic/rpTestTopic"></resource-env-ref-mapping>
The JNDI naming for Oracle JMS Destination
and connection factory objects is the same name that was specified in the orion-ejb-jar.xml
file for the MDB as described in "Specify the Destination and Connection Factory".
The following example maps the logical names for the connection factory and topic to their actual JNDI names. Specifically, the topic defined logically as "rpTestTopic
" in the ejb-jar.xml
file is mapped to its JNDI name of "java:comp/resource/cartojms1/Topics/rpTestTopic
."
<resource-ref-mapping name="myTCF" location="java:comp/resource/myProvider/TopicConnectionFactories/myTCF"> </resource-ref-mapping> <resource-env-ref-mapping name="rpTestTopic" location="java:comp/resource/myProvider/Topics/rpTestTopic"></resource-env-ref-mapping>
Once the resources have been defined, the client sends a JMS message to the MDB by doing the following:
Retrieve both the configured JMS Destination
and its connection factory using a JNDI lookup.
Create a connection from the connection factory. If you are receiving messages for a queue, start the connection.
Create a session over the connection.
Providing the retrieved JMS Destination
, create a sender for a queue, or a publisher for a topic.
Create the message.
Send out the message using either the queue sender or the topic publisher.
Close the queue session. Close the connection for either JMS Destination
types.
Example 9-7 JSP Client Sends Message to a Topic
The method of sending a message over a topic is almost the same. Instead of creating a queue, you create a topic. Instead of creating a sender, you create subscribers.
The following JSP client code sends a message over a topic to the MessageBean
MDB. The code uses logical names, which should be mapped in the OC4J deployment descriptor.
<%@ page import="javax.jms.*, javax.naming.*, java.util.*" %> <% //1a. Lookup the MessageBean topic jndiContext = new InitialContext(); topic = (Topic)jndiContext.lookup("rpTestTopic"); //1b. Lookup the MessageBean Connection factory topicConnectionFactory = (TopicConnectionFactory) jndiContext.lookup("myTCF"); //2 & 3. Retrieve a connection and a session on top of the connection topicConnection = topicConnectionFactory.createTopicConnection(); topicSession = topicConnection.createTopicSession(true, Session.AUTO_ACKNOWLEDGE); //5. Create the publisher for any messages destined for the topic topicPublisher = topicSession.createPublisher(topic); //6. Send out the message for (int ii = 0; ii < numMsgs; ii++) { message = topicSession.createBytesMessage(); String sndstr = "1::This is message " + (ii + 1) + " " + item; byte[] msgdata = sndstr.getBytes(); message.writeBytes(msgdata); topicPublisher.publish(message); System.out.println("--->Sent message: " + sndstr); } //7. Close publisher, session, and connection for topic topicPublisher.close(); topicSession.close(); topicConnection.close(); %> Message sent!
The oracle.mdb.fastUndeploy
system property enables you to shutdown OC4J cleanly when you are running MDBs in a Windows environment or when the backend database is running on a Windows environment. Normally, when you use an MDB, it is blocked in a receive state waiting for incoming messages. However, if you shutdown OC4J while the MDB is in a wait state in a Windows environment, then the OC4J instance cannot be stopped and the applications are not undeployed since the MDB is blocked. However, you can modify the behavior of the MDB in this environment by setting the oracle.mdb.fastUndeploy
system property. If you set this property to an integer, then when the MDB is not processing incoming messages and in a wait state, the OC4J container goes out to the database (requiring a database round-trip) and polls to see if the session is shut down. The integer denotes the number of seconds the system waits to poll the database. This can be expensive for performance. If you set this property to 60 (seconds), then every 60 seconds, OC4J is checking the database. If you do not set this property and you try to shutdown OC4J using CTRL-C, the OC4J process will hang for at least 2.5 hours.
An application that uses an RAC database must handle database failover scenarios. The MDB run time does not fail over to the newly available database. To enable failover, the deployment descriptors dequeue-retry-count and dequeue-retry-interval
must be specified in orion-ejb-jar.xml
file. The first parameter, dequeue-retry-count
, tells the container how many times to retry the database connection in case a failure happens; the default is 0. The second parameter, dequeue-retry-interval
, tells the container how long to wait between attempts (to accommodate for the time it takes for database failover); the default value is 60 (seconds).
Note: The RAC-enabled attribute of a data source is discussed in Data Sources chapter in the Oracle Application Server Containers for J2EE Services Guide.(RAC is real application clusters. For more information on using this flag with an infrastructure database, see the Oracle9iAS High Availability Guide. |
These parameters are attributes of the <message-driven-deployment>
element, as shown in the following example:
<message-driven-deployment name="MessageBeanTpc" connection-factory-location= "java:comp/resource/cartojms1/TopicConnectionFactories/aqTcf" destination-location= "java:comp/resource/cartojms1/Topics/topic1" subscription-name="MDBSUB" dequeue-retry-count=3 dequeue-retry-interval=90/>
A standalone OJMS client running against an RAC database must write similar code to obtain the connection again, by invoking the API DbUtil.oracleFatalError()
, to determine if the connection object is invalid. It must then reestablish the database connection if necessary. The following example outlines the logic:
getMessage(QueueSesssion session) { try { QueueReceiver rcvr; Message msgRec = null; QueueReceiver rcvr = session.createReceiver(rcvrQueue); msgRec = rcvr.receive(); } catch(Exception e ) { if (exc instanceof JMSException) { JMSException jmsexc = (JMSException) exc; sql_ex = (SQLException)(jmsexc.getLinkedException()); db_conn = (oracle.jms.AQjmsSession)session.getDBConnection(); if ((DbUtil.oracleFatalError(sql_ex, db_conn)) { // failover logic } } } }