XA Transaction Management
Since 3.4.0
The Interlok-XA component is built against the ASL version of Atomikos (3.8.0).
XA stands for “eXtended Architecture” and is an X/Open group standard for executing a “global transaction” that accesses, in our case, one or more JMS endpoints.
XA specifies how a transaction manager will roll up the transactions against the different data-stores into an “atomic” transaction and execute this with the two-phase commit (2PC) protocol for the transaction.
During runtime there are 5 possible steps that a transaction manager will add to normal message processing, in order these are;
Step | Description | Optional |
---|---|---|
Begin transaction | Enables message transaction for messages processed | No |
Enlisting a resource | Every transaction requires the participants to be known, enlisting performs that function. | No |
Delisting a resource | Once a resource is finished with in the current transaction, it needs to be de-listed. | No |
Commit | Successfully completes a transaction. | Yes, only commits on success |
Rollback | Completes a transaction rolling back all messages after a failure. | Yes, only on failure |
Installation
For each Interlok instance you wish to activate XA transactions, you will need only one additional component; “interlok-xa”.
Simply copy the java archive files from the “/optional/interlok-xa” directory into your “lib” directory at the root of your installation.
Configuring XA transaction management
There are several components ranging from the required to the optional. Each will be discussed below.
The properties file
A new “transactions.properties” file should be created and added to the classpath of your Interlok instance.
If you’re using the standard Interlok start-up mechanisms, then dropping this file into the “config” directory at the root of your Interlok installation will suffice.
The following properties are required, however the values may change depending on configuration discussed later;
Property | Description | Value |
---|---|---|
com.atomikos.icatch.automatic_resource_registration | Allows us to dynamically register JMS resources. | true |
com.atomikos.icatch.max_timeout | Specifies the maximum timeout (in milliseconds) that can be allowed for transactions. | Set to a value of 5 minutes or more. |
com.atomikos.icatch.default_jta_timeout | Specifies the maximum timeout (in milliseconds) for JTA transactions. | Set to a value of 5 minutes or more. |
com.atomikos.icatch.service | The transaction manager implementation | com.atomikos.icatch.standalone.UserTransactionServiceFactory |
Interlok component configuration
Currently the only XA transaction participants supported by Interlok are JMS endpoints, for both consumers and producers.
The most basic XA pattern would simply consume from a JMS endpoint; should your service list fail, we rollback the transaction and any messages contained in the transaction, but should your service list succeed we commit the transaction removing all consumed messages from the consume source.
The next step would be to move messages from one JMS endpoint to another; both endpoints participating in the transaction. Should one fail, we rollback.
The Interlok components required are the transaction manager, the consumer and if required a producer.
The transaction manager
Per instance of Interlok, only one transaction manager instance is permitted, therefore you configure the transaction manager in the shared components;
<shared-components>
<transaction-manager class="xa-cached-jms-transaction-manager">
<unique-id>xa-transaction-manager</unique-id>
</transaction-manager>
</shared-components>
The unique-id, much the same as shared-connections is used as the internal JNDI name which pairs up with the shared-transaction-managers, spoken about later.
There are currently 2 transaction manager implementations;
- xa-jms-transaction-manager
The base implementation, which will enlist and delist both the consumer and producer for each message. A good solution for low activity workflows, where message batching (discussed later) is not required.
When using this transaction manager, you must set the transactions.properties property com.atomikos.icatch.automatic_resource_registration to true.
- xa-cached-jms-transaction-manager
Built upon the base implementation, but will only enlist/delist resources once for each unique resource per transaction. Use this transaction manager for higher throughput workflows where message batching is required.
When using this transaction manager, you must set the transactions.properties property com.atomikos.icatch.automatic_resource_registration to true.
The consumer/produce connections
Should you have acesss to a JNDI store for your chosen broker then the connection can be configured almost exactly as any other Interlok JMS JNDI connection;
<xa-jms-connection>
<vendor-implementation class="xa-jndi-implementation">
<jndi-params>
<key-value-pair>
<key>java.naming.factory.initial</key>
<value>...</value>
</key-value-pair>
<key-value-pair>
<key>java.naming.provider.url</key>
<value>...</value>
</key-value-pair>
</jndi-params>
</vendor-implementation>
</xa-jms-connection>
Since 3.8.2
You may also configure specific vendor implementations such as for Solace, WebsphereMQ and Tibco EMS. Examples of each below;
Solace. We have both the basic and advanced implementations, the basic one show below.:
<xa-jms-connection>
<unique-id>xa-solace-connection</unique-id>
<user-name>admin</user-name>
<password>admin</password>
<vendor-implementation class="xa-basic-solace-implementation">
<broker-url>hostname:55555</broker-url>
<message-vpn>default</message-vpn>
</vendor-implementation>
</xa-jms-connection>
Websphere MQ. We have both the basic and advanced implementations, the advanced one show below.:
<xa-jms-connection>
<unique-id>xa-wmq-connection</unique-id>
<vendor-implementation class="xa-advanced-mq-series-implementation">
<connection-factory-properties>
<key-value-pair>
<key>Channel</key>
<value>ServerConnectionChan</value>
</key-value-pair>
<key-value-pair>
<key>Port</key>
<value>1414</value>
</key-value-pair>
<key-value-pair>
<key>TransportType</key>
<value>MQJMS_TP_BINDINGS_MQ</value>
</key-value-pair>
<key-value-pair>
<key>HostName</key>
<value>localhost</value>
</key-value-pair>
<key-value-pair>
<key>QueueManager</key>
<value>MyQM</value>
</key-value-pair>
</connection-factory-properties>
</vendor-implementation>
</xa-jms-connection>
Tibco EMS:
<xa-jms-connection>
<unique-id>xa-tibco-connection-1</unique-id>
<vendor-implementation class="xa-basic-tibco-ems-implementation">
<broker-url>tcp://localhost:7222</broker-url>
</vendor-implementation>
</xa-jms-connection>
Connection error handling
Inside your connection configuration you may use either the standard xa-jms-connection-error-handler which registers itself with the connection and relies on the JMS onException() method to trigger component restarts, or you can use the xa-active-jms-connection-error-handler which will peridically use the connection to attempt a message send to a temporary topic. If the send fails the connection is deemed to have been lost.
If you use the active error handler (which is the suggested error handler in almost all cases) there is some additional configuration;
<connection-error-handler class="xa-active-jms-connection-error-handler">
<additional-logging>true</additional-logging>
<xa-resource-name>testResource</xa-resource-name>
<transaction-manager class="shared-transaction-manager">
<lookup-name>xa-transaction-manager</lookup-name>
</transaction-manager>
</connection-error-handler>
Should you set the additional-logging to true, debug messages will appear in the log file upon every attempt to verify the connection.
The xa-resource-name is an Interlok wide unique name that identifies this connection error handler to the transaction manager. Any unique string is fine.
Finally, because the active error handler uses the XA connection, we need to know about the transaction manager, which can be referenced with the shared-transaction-manager.
The consumer
The XA compliant consumer is built upon the standard jms-consumer, therefore all configuration is inherited from the standard jms consumer with the addition of two items; the transaction manager and the resource name.
<consumer class="xa-jms-consumer">
<unique-id>JMS-Consumer</unique-id>
<destination class="configured-consume-destination">
<destination>jms:queue:myQueue</destination>
</destination>
<message-translator class="text-message-translator">
<move-metadata>false</move-metadata>
<move-jms-headers>false</move-jms-headers>
</message-translator>
<correlation-id-source class="null-correlation-id-source"/>
<transaction-manager class="shared-transaction-manager">
<lookup-name>xa-transaction-manager</lookup-name>
</transaction-manager>
<xa-resource-name>xa-consumer-resource</xa-resource-name>
</consumer>
The transaction manager is looked up from the shared components and the xa-resource-name is an Interlok instance wide unique chosen name for this particular consumer that enables the transaction manager to manage this instance of the consumer.
Should messages fail and be rolled back, we can configure a time period through the xa-exception-handler that Interlok will wait before attempting to continue consuming;
<consumer class="xa-jms-consumer">
...
<xa-exception-handler class="xa-wait-exception-handler">
<wait-period>
<unit>SECONDS</unit>
<interval>30</interval>
</wait-period>
</xa-exception-handler>
...
</consumer>
The default wait period is 30 seconds.
Finally, you can also choose to recreate the session and consumer on any error. Once the above error-handler has been executed and if you set this option to true, we will close the current consumer and session recreating them both.
This option can be useful on some rare issues with the JMS vendor specific client libraries. Such as overloading the message performance capability of some vendors, seems to close the client session.
The default for this option is false, but you can override like this;
<consumer class="xa-jms-consumer">
...
<recreate-consumer-on-error>true</recreate-consumer-on-error>
...
</consumer>
The producer
The XA compliant producer is also built upon the standard jms-producer, therefore inherits all of it’s properties. But as with the XA compliant consumer, we must configure the transaction manager and assign a unique resource name. We also must configure the session-factory, all shown below.
<producer class="xa-jms-producer">
<destination class="configured-produce-destination">
<destination>jms:queue:myQueue</destination>
</destination>
<message-translator class="text-message-translator">
<move-metadata>false</move-metadata>
<move-jms-headers>false</move-jms-headers>
</message-translator>
<correlation-id-source class="null-correlation-id-source"/>
<delivery-mode>PERSISTENT</delivery-mode>
<priority>4</priority>
<session-factory class="jms-default-producer-xa-session"/>
<xa-resource-name>wmq-xa-producer-resource</xa-resource-name>
<transaction-manager class="shared-transaction-manager">
<lookup-name>xa-transaction-manager</lookup-name>
</transaction-manager>
</producer>
Performance considerations
Transactions can add time to your message processing, consider the following options when you require workflows with high throughput.
Events
In your workflow set the send-events to false;
<workflow-list>
<standard-workflow>
<send-events>false</send-events>
...
</standard-workflow>
</workflow-list>
JMS headers / Interlok metadata
If you don’t need to copy consumed JMS headers to the produced messages, then consider setting this functionality to false in both your consumer and producer;
<consumer class="xa-jms-consumer">
...
<message-translator class="text-message-translator">
<move-metadata>false</move-metadata>
<move-jms-headers>false</move-jms-headers>
</message-translator>
</consumer>
<producer class="xa-jms-producer">
...
<message-translator class="text-message-translator">
<move-metadata>false</move-metadata>
<move-jms-headers>false</move-jms-headers>
</message-translator>
</producer>
Per-message-properties
If you configure the JMS delivery-mode, time-to-live and the priority in your producer and require the same values for every message produced turn off per-message-properties in your producer;
<producer class="xa-jms-producer">
...
<per-message-properties>false</per-message-properties>
</producer>
Destination caching
If every message you consume is to be sent to the same destination (queue or topic), then set the destination caching to true. This will stop Interlok re-creating the destination object for every message and instead use a cached version.
<producer class="xa-jms-producer">
...
<cache-destination>true</cache-destination>
</producer>
Message batching
Adding transactions to your message processing will add processing time, so we have introduced message batching.
This allows us to consume, process and produce multiple messages all within the same transaction.
The downside to this of course is that should you configure a message batch of 100 messages for example and the 100th message fails, then all 100 messages will be rolled back.
In your XA compliant JMS consumer, simply set the max-batch property;
<consumer class="xa-jms-consumer">
...
<max-batch>250</max-batch>
</consumer>
While batching is in effect you will want your transactions to complete (commit/rollback) in a timely manner should you not consume the maximum configured messages.
We do this by configuring the max-transaction-time on the consumer;
<consumer class="xa-jms-consumer">
...
<max-transaction-time>
<unit>MILLISECONDS</unit>
<interval>100</interval>
</max-transaction-time>
</consumer>
The way this is implemented, is that a consumer will attempt to consume a message from the queue, but will only wait for this configured time period. If there is no message, then the transaction is deemed complete.
If there is a message available to consume, then we process the message and try again for the same configured time.
Eventually, you will either consume the maximum configured messages for your batched transaction, or a message will not be available and your transaction will end.
Multi-channel/workflow
Consider scaling up by duplicating workflows and/or channels.
The downside to this is that you cannot guarantee message order. If you require message order you can use a global lock on your consumers.
Some background; When we consume messages, we first need to create a new transaction. Then once a message is consumed and processed/produced, we will commit the transaction, before starting over.
Creating a new transaction and commiting/rolling back a transaction can take time. Therefore we have introduced consumer global locking.
The idea of global locking is that only one consumer may pull messages from a JMS endpoint at a time and once it is ready to commit it will release a lock which will allow a second consumer to pull and process messages.
With consumer global locking we effectively still guarantee order, but do not have to wait for a commit and then begin transaction operation to complete before processing messages.
To configure global locking, simply have two or more channels configured identically, but setting the global-lock configuration property in each consumer to true.
<consumer class="xa-jms-consumer">
...
<global-lock>true</global-lock>
</consumer>
Performance tests
Environment
Windows 7 (64 bit) ::: Intel Core i7-5600U CPU @2.60Ghz ::: RAM: 16Gb
Solace VMR - 7.1.1.345
Interlok 3.4.0
Single threaded
A single channel/workflow with a transaction batch size of 250, consuming from a Solace queue and producing to another queue.
200,000 messages at a steady rate of 4,000 msg / sec.
Multi-channel with locking
Three identical channels, locked so that only one is active at a time, with a transaction batch size of 250, consuming from a Solace queue and producing to another queue.
200,000 messages at a steady rate of 6,000 msg / sec.
Test configurations used
Single threaded test configuration
<adapter>
<unique-id>Solace-XA-Adapter</unique-id>
<shared-components>
<transaction-manager class="xa-cached-jms-transaction-manager">
<unique-id>transaction-manager</unique-id>
</transaction-manager>
</shared-components>
<channel-list>
<channel>
<consume-connection class="xa-jms-connection">
<connection-error-handler class="xa-jms-connection-error-handler"/>
<vendor-implementation class="xa-jndi-implementation">
<jndi-params>
<key-value-pair>
<key>java.naming.factory.initial</key>
<value>com.solacesystems.jndi.SolJNDIInitialContextFactory</value>
</key-value-pair>
<key-value-pair>
<key>java.naming.provider.url</key>
<value>smf://192.168.132.128:55555</value>
</key-value-pair>
<key-value-pair>
<key>java.naming.security.principal</key>
<value>admin</value>
</key-value-pair>
<key-value-pair>
<key>java.naming.security.credentials</key>
<value>admin</value>
</key-value-pair>
</jndi-params>
<jndi-name>xa-factory</jndi-name>
</vendor-implementation>
</consume-connection>
<produce-connection class="xa-jms-connection">
<vendor-implementation class="xa-jndi-implementation">
<jndi-params>
<key-value-pair>
<key>java.naming.factory.initial</key>
<value>com.solacesystems.jndi.SolJNDIInitialContextFactory</value>
</key-value-pair>
<key-value-pair>
<key>java.naming.provider.url</key>
<value>smf://192.168.132.128:55555</value>
</key-value-pair>
<key-value-pair>
<key>java.naming.security.principal</key>
<value>admin</value>
</key-value-pair>
<key-value-pair>
<key>java.naming.security.credentials</key>
<value>admin</value>
</key-value-pair>
</jndi-params>
<jndi-name>xa-factory</jndi-name>
</vendor-implementation>
</produce-connection>
<unique-id>Channel1</unique-id>
<workflow-list>
<standard-workflow>
<consumer class="xa-jms-consumer">
<unique-id>JMS-Consumer</unique-id>
<max-batch>250</max-batch>
<destination class="configured-consume-destination">
<destination>jms:queue:Q1</destination>
</destination>
<message-translator class="text-message-translator">
<move-metadata>false</move-metadata>
<move-jms-headers>false</move-jms-headers>
</message-translator>
<correlation-id-source class="null-correlation-id-source"/>
<transaction-manager class="shared-transaction-manager">
<lookup-name>transaction-manager</lookup-name>
</transaction-manager>
<xa-resource-name>wmq-xa-consumer-resource</xa-resource-name>
</consumer>
<producer class="xa-jms-producer">
<destination class="configured-produce-destination">
<destination>jms:queue:Q2</destination>
</destination>
<message-translator class="text-message-translator">
<move-metadata>false</move-metadata>
<move-jms-headers>false</move-jms-headers>
</message-translator>
<correlation-id-source class="null-correlation-id-source"/>
<delivery-mode>PERSISTENT</delivery-mode>
<priority>4</priority>
<session-factory class="jms-default-producer-xa-session"/>
<xa-resource-name>wmq-xa-producer-resource</xa-resource-name>
<transaction-manager class="shared-transaction-manager">
<lookup-name>transaction-manager</lookup-name>
</transaction-manager>
<cache-destination>true</cache-destination>
<per-message-properties>false</per-message-properties>
</producer>
<send-events>false</send-events>
<produce-exception-handler class="null-produce-exception-handler"/>
<unique-id>Workflow1</unique-id>
</standard-workflow>
</workflow-list>
</channel>
</channel-list>
</adapter>
Multi-channel test configuration
<adapter>
<unique-id>Solace-XA-Adapter</unique-id>
<shared-components>
<transaction-manager class="xa-cached-jms-transaction-manager">
<unique-id>transaction-manager</unique-id>
</transaction-manager>
</shared-components>
<channel-list>
<channel>
<consume-connection class="xa-jms-connection">
<connection-error-handler class="xa-jms-connection-error-handler"/>
<vendor-implementation class="xa-jndi-implementation">
<jndi-params>
<key-value-pair>
<key>java.naming.factory.initial</key>
<value>com.solacesystems.jndi.SolJNDIInitialContextFactory</value>
</key-value-pair>
<key-value-pair>
<key>java.naming.provider.url</key>
<value>smf://192.168.132.128:55555</value>
</key-value-pair>
<key-value-pair>
<key>java.naming.security.principal</key>
<value>admin</value>
</key-value-pair>
<key-value-pair>
<key>java.naming.security.credentials</key>
<value>admin</value>
</key-value-pair>
</jndi-params>
<jndi-name>xa-factory</jndi-name>
</vendor-implementation>
</consume-connection>
<produce-connection class="xa-jms-connection">
<vendor-implementation class="xa-jndi-implementation">
<jndi-params>
<key-value-pair>
<key>java.naming.factory.initial</key>
<value>com.solacesystems.jndi.SolJNDIInitialContextFactory</value>
</key-value-pair>
<key-value-pair>
<key>java.naming.provider.url</key>
<value>smf://192.168.132.128:55555</value>
</key-value-pair>
<key-value-pair>
<key>java.naming.security.principal</key>
<value>admin</value>
</key-value-pair>
<key-value-pair>
<key>java.naming.security.credentials</key>
<value>admin</value>
</key-value-pair>
</jndi-params>
<jndi-name>xa-factory</jndi-name>
</vendor-implementation>
</produce-connection>
<unique-id>Channel1</unique-id>
<workflow-list>
<standard-workflow>
<consumer class="xa-jms-consumer">
<unique-id>JMS-Consumer</unique-id>
<max-batch>250</max-batch>
<destination class="configured-consume-destination">
<destination>jms:queue:Q1</destination>
</destination>
<message-translator class="text-message-translator">
<move-metadata>false</move-metadata>
<move-jms-headers>false</move-jms-headers>
</message-translator>
<correlation-id-source class="null-correlation-id-source"/>
<transaction-manager class="shared-transaction-manager">
<lookup-name>transaction-manager</lookup-name>
</transaction-manager>
<xa-resource-name>wmq-xa-consumer-resource</xa-resource-name>
<global-lock>true</global-lock>
</consumer>
<producer class="xa-jms-producer">
<destination class="configured-produce-destination">
<destination>jms:queue:Q2</destination>
</destination>
<message-translator class="text-message-translator">
<move-metadata>false</move-metadata>
<move-jms-headers>false</move-jms-headers>
</message-translator>
<correlation-id-source class="null-correlation-id-source"/>
<delivery-mode>PERSISTENT</delivery-mode>
<priority>4</priority>
<session-factory class="jms-default-producer-xa-session"/>
<xa-resource-name>wmq-xa-producer-resource</xa-resource-name>
<transaction-manager class="shared-transaction-manager">
<lookup-name>transaction-manager</lookup-name>
</transaction-manager>
<cache-destination>true</cache-destination>
<per-message-properties>false</per-message-properties>
</producer>
<send-events>false</send-events>
<produce-exception-handler class="null-produce-exception-handler"/>
<unique-id>Workflow1</unique-id>
</standard-workflow>
</workflow-list>
</channel>
<channel>
<consume-connection class="xa-jms-connection">
<connection-error-handler class="xa-jms-connection-error-handler"/>
<vendor-implementation class="xa-jndi-implementation">
<jndi-params>
<key-value-pair>
<key>java.naming.factory.initial</key>
<value>com.solacesystems.jndi.SolJNDIInitialContextFactory</value>
</key-value-pair>
<key-value-pair>
<key>java.naming.provider.url</key>
<value>smf://192.168.132.128:55555</value>
</key-value-pair>
<key-value-pair>
<key>java.naming.security.principal</key>
<value>admin</value>
</key-value-pair>
<key-value-pair>
<key>java.naming.security.credentials</key>
<value>admin</value>
</key-value-pair>
</jndi-params>
<jndi-name>xa-factory</jndi-name>
</vendor-implementation>
</consume-connection>
<produce-connection class="xa-jms-connection">
<vendor-implementation class="xa-jndi-implementation">
<jndi-params>
<key-value-pair>
<key>java.naming.factory.initial</key>
<value>com.solacesystems.jndi.SolJNDIInitialContextFactory</value>
</key-value-pair>
<key-value-pair>
<key>java.naming.provider.url</key>
<value>smf://192.168.132.128:55555</value>
</key-value-pair>
<key-value-pair>
<key>java.naming.security.principal</key>
<value>admin</value>
</key-value-pair>
<key-value-pair>
<key>java.naming.security.credentials</key>
<value>admin</value>
</key-value-pair>
</jndi-params>
<jndi-name>xa-factory</jndi-name>
</vendor-implementation>
</produce-connection>
<unique-id>Channel3</unique-id>
<workflow-list>
<standard-workflow>
<consumer class="xa-jms-consumer">
<unique-id>JMS-Consumer</unique-id>
<max-batch>250</max-batch>
<destination class="configured-consume-destination">
<destination>jms:queue:Q1</destination>
</destination>
<message-translator class="text-message-translator">
<move-metadata>false</move-metadata>
<move-jms-headers>false</move-jms-headers>
</message-translator>
<correlation-id-source class="null-correlation-id-source"/>
<transaction-manager class="shared-transaction-manager">
<lookup-name>transaction-manager</lookup-name>
</transaction-manager>
<xa-resource-name>wmq-xa-consumer-resource</xa-resource-name>
<global-lock>true</global-lock>
</consumer>
<producer class="xa-jms-producer">
<destination class="configured-produce-destination">
<destination>jms:queue:Q2</destination>
</destination>
<message-translator class="text-message-translator">
<move-metadata>false</move-metadata>
<move-jms-headers>false</move-jms-headers>
</message-translator>
<correlation-id-source class="null-correlation-id-source"/>
<delivery-mode>PERSISTENT</delivery-mode>
<priority>4</priority>
<session-factory class="jms-default-producer-xa-session"/>
<xa-resource-name>wmq-xa-producer-resource</xa-resource-name>
<transaction-manager class="shared-transaction-manager">
<lookup-name>transaction-manager</lookup-name>
</transaction-manager>
<cache-destination>true</cache-destination>
<per-message-properties>false</per-message-properties>
</producer>
<send-events>false</send-events>
<produce-exception-handler class="null-produce-exception-handler"/>
<unique-id>Workflow3</unique-id>
</standard-workflow>
</workflow-list>
</channel>
<channel>
<consume-connection class="xa-jms-connection">
<connection-error-handler class="xa-jms-connection-error-handler"/>
<vendor-implementation class="xa-jndi-implementation">
<jndi-params>
<key-value-pair>
<key>java.naming.factory.initial</key>
<value>com.solacesystems.jndi.SolJNDIInitialContextFactory</value>
</key-value-pair>
<key-value-pair>
<key>java.naming.provider.url</key>
<value>smf://192.168.132.128:55555</value>
</key-value-pair>
<key-value-pair>
<key>java.naming.security.principal</key>
<value>admin</value>
</key-value-pair>
<key-value-pair>
<key>java.naming.security.credentials</key>
<value>admin</value>
</key-value-pair>
</jndi-params>
<jndi-name>xa-factory</jndi-name>
</vendor-implementation>
</consume-connection>
<produce-connection class="xa-jms-connection">
<vendor-implementation class="xa-jndi-implementation">
<jndi-params>
<key-value-pair>
<key>java.naming.factory.initial</key>
<value>com.solacesystems.jndi.SolJNDIInitialContextFactory</value>
</key-value-pair>
<key-value-pair>
<key>java.naming.provider.url</key>
<value>smf://192.168.132.128:55555</value>
</key-value-pair>
<key-value-pair>
<key>java.naming.security.principal</key>
<value>admin</value>
</key-value-pair>
<key-value-pair>
<key>java.naming.security.credentials</key>
<value>admin</value>
</key-value-pair>
</jndi-params>
<jndi-name>xa-factory</jndi-name>
</vendor-implementation>
</produce-connection>
<unique-id>Channel2</unique-id>
<workflow-list>
<standard-workflow>
<consumer class="xa-jms-consumer">
<unique-id>JMS-Consumer2</unique-id>
<max-batch>250</max-batch>
<destination class="configured-consume-destination">
<destination>jms:queue:Q1</destination>
</destination>
<message-translator class="text-message-translator">
<move-metadata>false</move-metadata>
<move-jms-headers>false</move-jms-headers>
</message-translator>
<correlation-id-source class="null-correlation-id-source"/>
<transaction-manager class="shared-transaction-manager">
<lookup-name>transaction-manager</lookup-name>
</transaction-manager>
<xa-resource-name>wmq-xa-consumer-resource</xa-resource-name>
<global-lock>true</global-lock>
</consumer>
<producer class="xa-jms-producer">
<destination class="configured-produce-destination">
<destination>jms:queue:Q2</destination>
</destination>
<message-translator class="text-message-translator">
<move-metadata>false</move-metadata>
<move-jms-headers>false</move-jms-headers>
</message-translator>
<correlation-id-source class="null-correlation-id-source"/>
<delivery-mode>PERSISTENT</delivery-mode>
<priority>4</priority>
<session-factory class="jms-default-producer-xa-session"/>
<xa-resource-name>wmq-xa-producer-resource</xa-resource-name>
<transaction-manager class="shared-transaction-manager">
<lookup-name>transaction-manager</lookup-name>
</transaction-manager>
<cache-destination>true</cache-destination>
<per-message-properties>false</per-message-properties>
</producer>
<send-events>false</send-events>
<produce-exception-handler class="null-produce-exception-handler"/>
<unique-id>Workflow2</unique-id>
</standard-workflow>
</workflow-list>
</channel>
</channel-list>
</adapter>