Red Hat Training

A Red Hat training course is available for Red Hat Fuse

7.4. XA Client with Two Connections to a Broker

Overview

A special case arises where an XA client opens two separate connections to the same remote broker instance. You might want to open two connections, for example, in order to send messages to the broker with different properties and qualities of service.
Each XA connection is implicitly associated with its own dedicated XA resource object. When two XA resource objects are equivalent (as determined by calling XAResource.isSameRM), however, many Transaction Managers treat these XA resource objects in a special way: when the current transaction finishes (committed or rolled back), the Transaction Manager calls XAResource.end only on the first enlisted XAResource instance. This creates a problem for Apache ActiveMQ, which expects XAResource.end to be called on every enlisted XAResource instance. To avoid this problem, Apache ActiveMQ provides an option which forces the Transaction Manager to call XAResource.end on every XA resource instance.

jms.rmIdFromConnectionId option

To cope with the scenario where an XA client opens two connections to the same remote broker, it is normally necessary to set the jms.rmIdFromConnectionId option to true. The effect of setting this option to true is that XA resource names are then based on the connection ID, instead of being based on the broker ID. This ensures that all connections have distinct XA resource names, even if they are connected to the same broker instance (note that every connection is associated with its own XA resource object). A side effect of setting this option is that the Transaction Manager is guaranteed to call XAResource.end on each of the XA resource objects.
Note
When you set the jms.rmIdFromConnectionId option to true, the transaction manager adopts the 2-phase commit protocol (2-PC). Hence, there is a significant overhead associated with sending messages on one connection and receiving messages on another, when transactions are enabled.

Setting rmIdFromConnectionId option on an endpoint URI

You can enable the rmIdFromConnectionId option by setting jms.rmIdFromConnectionId to true on an Apache ActiveMQ endpoint URI. For example, to enable this option on an OpenWire URI:
tcp://brokerhost:61616?jms.rmIdFromConnectionId=true

Setting rmIdFromConnectionId option directly on ActiveMQXAConnectionFactory

You can enable the rmIdFromConnectionId option directly on the ActiveMQXAConnectionFactory class, by invoking the setRmIdFromConnectionId method. For example, you can set the rmIdFromConnectionId option in Java, as follows:
// Java
ActiveMQXAConnectionFactory cf = new ActiveMQXAConnectionFactory( ... );
cf.setRmIdFromConnectionId(true);
And you can set the rmIdFromConnectionId option in XML, as follows:
<!--
    ActiveMQ XA Resource Manager
-->
<bean id="resourceManager"
      class="org.apache.activemq.pool.ActiveMQResourceManager"
      init-method="recoverResource">
    <property name="transactionManager" ref="recoverableTxManager">
    <property name="connectionFactory" ref="jmsXaConnectionFactory">
    <property name="resourceName" value="activemq.default">
    <property name="rmIdFromConnectionId" value="true">
</bean>

Example using rmIdFromConnectionId

The following example shows you how to use the rmIdFromConnectionId option in the context of an XA aware JMS client written in Java:
// Java
import org.apache.activemq.ActiveMQXAConnectionFactory

import javax.jms.XAConnection;
import javax.jms.XASession;
import javax.jms.XATopicConnection;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
...
ActiveMQXAConnectionFactory cf = new ActiveMQXAConnectionFactory("tcp://brokerhost:61616?jms.rmIdFromConnectionId=true");
... // Configure other connection factory options (not shown)

XAConnection connection1 = (XAConnection)cf.createConnection();
XASession session1 = connection1.createXASession();
XAResource resource1 = session1.getXAResource();

XAConnection connection2 = (XAConnection)cf.createConnection();
XASession session2 = connection2.createXASession();
XAResource resource2 = session2.getXAResource();
...
// Send messages using 'connection1' AND connection2' in this thread
...
// Commit transaction => transaction manager sends xa.end() to BOTH XAResource objects
In this case, the XA transaction proceeds as follows:
  1. Because this is an XA example, it does not show any explicit transaction demarcation (for example, begin or commit invocations). In this case, the XA Transaction Manager (TM) is responsible for transaction demarcation. For example, if you were deploying this code into a container that supports transactions, the container would normally be responsible for transaction demarcation.
  2. When you create the first XAConnection object, connection1, it automatically creates the associated XAResource object for this connection, resource1. The TM automatically enlists resource1 into the current transaction by calling XAResource.start().
  3. When you create the second XAConnection object, connection2, it automatically creates the associated XAResource object for this connection, resource2. The TM automatically joins resource2 to the current transaction: the TM does this by calling XAResource.start() with the TMJOIN flag.
  4. Because you have set rmIdFromConnectionId to true in this example, resource1 and resource2 have different XA resource names, which means that the TM treats them as two different resources.
  5. You can now do some work in the current transaction by sending messages on connection1 and on connection2. All of these message sends belong to the current transaction.
  6. When the current transaction is finished (committed or rolled back), the TM will call XAResource.end() on both resource1 and resource2. This behaviour is guaranteed, because the TM perceives resource1 and resource2 to be different resources (due to different XA resource names).
    Note
    If you have not set the rmIdFromConnectionId option, the typical behaviour of the TM at this point would be to call XAResource.end only on the first resource, resource1. This creates problems in the context of Apache ActiveMQ, because the second connection, connection2, can send messages asynchronously and these asynchronous messages will not be synchronized with the transaction unless the TM calls XAResource.end on resource2 as well.