A transaction in JMS cannot include both producing and consuming messages as one logical unit of work, so both processes need to be treated within their own transactional contexts. The reason for this transactional separation is that a JMS client's (JMS Producer) transaction context does not flow with a JMS message, which implies that transactions initiated from a JMS client cannot be propagated to the message consumer. If a transaction is started from a JMS Producer, the JMS message is not immediately propagated to a JMS destination (a queue or topic). Instead, the message is buffered in memory or stored in the JMS server's persistent storage device (a JDBC database or file). Only if the transaction commits will messages in the buffer be sent to the target JMS destination as part of transaction commit. However, if the JMS producer's transaction is rolled back, the JMS message is flushed from the buffer, and the message is not sent to the target JMS destination. From a JMS Consumer's perspective, such as an MDB, if a transaction commits, the processed JMS message is removed from the JMS destination. However, if the transaction rolls back, the JMS message stays in the JMS destination and is redelivered as follows :
Specifying an MDB's Transaction Demarcation TypeWhen designing an MDB to use transactions, you must decide whether the bean will demarcate transactions programmatically via the onMessage() method (Bean-Managed Transaction, or BMT, demarcation) or whether the EJB container will perform transaction demarcation (Container-Managed Transaction, or CMT, demarcation). You specify the transaction demarcation type of an MDB via the <transaction-type> element in the ejb-jar.xml deployment descriptor, for example:
Note The <transaction-type> element specifies how the EJB container must manage transactions for the onMessage() method on the arrival of a JMS message. The onMessage() method is called in the scope of a transaction determined by the <transaction-type> element. Container-Managed TransactionsWith CMT demarcation, the EJB container demarcates transactions based on instructions in the ejb-jar.xml deployment descriptor. These instructions, called transaction attributes (as shown in Listing 22.6), tell the container whether it should run an MDB's onMessage() method in a new transaction started by the container or run the method without any transactions. Listing 22.6 A Segment of ejb-jar.xml Showing Transaction Attributes for the onMessage() Method Using CMT <ejb-jar> ... <assembly-descriptor> ... <container-transaction> <description>Transactions required for this method.</description> <method> <ejb-name>myMDB</ejb-name> <method-name>onMessage</method-name> </method> <trans-attribute> NotSupported or Required </trans-attribute> </container-transaction> ... </assembly-descriptor> ... </ejb-jar> As shown in Listing 22.6, these are the only transaction attributes that are applicable to MDBs with CMT demarcations:
Avoiding Poison Messages with Container-Managed MDBsIf the onMessage() method does not successfully complete or the container rolls back the transaction, the container does not acknowledge a message receipt; the message remains in its associated JMS destination and is delivered again to the MDB. When a message continuously gets redelivered to a container-managed MDB only to be rolled back, the message is referred to as a poison message . Poison messages can have a negative impact on WebLogic JMS Server's performance. They also consume MDB resources from the free pool, so they should removed from the JMS destination when they're detected . WebLogic JMS Server has built-in support for detecting message redelivery attempts and for redirecting a message to an error destination when redelivery attempts for the message reach a specific limit. You can configure WebLogic JMS Server to manage poison messages via the Administration Console, as shown in Figure 22.12. Figure 22.12. Manage poison messages via the Administration Console.
Bean-Managed TransactionsWhen an MDB uses BMT demarcation, you must use the EJBContext.getUserTransaction() callback to obtain a reference to the javax.transaction.UserTransaction object. After you have an instance of javax.transaction.UserTransaction , you can mark the beginning of a transaction with the begin() method, and end the transaction with the commit() or rollback() method. If the transaction commits, a message acknowledgement is sent to WebLogic JMS Server based on the value the <acknowledge-mode> element specifies in the ejb-jar.xml deployment descriptor: Auto-acknowledge or Dups-ok-acknowledge . The skeleton code for an onMessage() method using BMT is shown in Listing 22.7. Listing 22.7 Skeleton Code for the onMessage() Method Showing How to Use BMTpublic void onMessage(Message msg) { try { // get the UserTranaction from the EJB context javax.transaction.UserTransaction mdbtx = mdb_context.getUserTransaction(); // start the transaction mdbtx.begin(); do some processing... if (OK Condition) { // commit the transaction mdbtx.commit(); } else { // roll back transaction mdbx.rollback(); } } } |