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="osgiJtaTransactionManager" />
<property name="connectionFactory" ref="jmsXaPoolConnectionFactory" />
<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:
- Because this is an XA example, it does not show any explicit transaction demarcation (for example,
begin
orcommit
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. - When you create the first
XAConnection
object,connection1
, it automatically creates the associatedXAResource
object for this connection,resource1
. The TM automatically enlistsresource1
into the current transaction by calling XAResource.start(). - When you create the second
XAConnection
object,connection2
, it automatically creates the associatedXAResource
object for this connection,resource2
. The TM automatically joinsresource2
to the current transaction: the TM does this by calling XAResource.start() with the TMJOIN flag. - Because you have set
rmIdFromConnectionId
totrue
in this example,resource1
andresource2
have different XA resource names, which means that the TM treats them as two different resources. - You can now do some work in the current transaction by sending messages on
connection1
and onconnection2
. All of these message sends belong to the current transaction. - When the current transaction is finished (committed or rolled back), the TM will call XAResource.end() on both
resource1
andresource2
. This behaviour is guaranteed, because the TM perceivesresource1
andresource2
to be different resources (due to different XA resource names).NoteIf you have not set thermIdFromConnectionId
option, the typical behaviour of the TM at this point would be to callXAResource.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 callsXAResource.end
onresource2
as well.