Configuring Transactions (JTA) using JBoss / Wildfly

This tutorial discusses about configuring and monitoring transactions using the Java Transaction API(JTA) on Wildfly application server.

Transaction overview

Let’s start from some definitions: You can define a transaction as a group of operations that must be performed as a unit and can involve persisting data objects, sending a message, and so on.
When the operations in a transaction involve multiple databases or other resources that reside on separate computers or processes, that becomes a distributed transaction. Such enterprise-wide transactions require special coordination between the resources involved and can be extremely difficult to program reliably.

This is where Java Transaction API (JTA) comes in, providing the interface that resources can implement and to which they can bind, in order to participate in a distributed transaction.

For example, the EJB container is a transaction manager that supports JTA. Therefore, it can participate in distributed transactions involving other EJB containers, as well as third-party JTA resources, such as many database management systems. Within JBoss EAP / Wildfly you can configure transactions in their own subsystem. The transactions subsystem consists mainly of four elements:

  • Core environment: includes the TransactionManager interface, which allows the application server to control the transaction boundaries on behalf of the resource being managed.
  • Recovery-environment: ensures that results of a transaction are applied consistently to all resources affected by the transaction, even if any of the application processes or the machine hosting them crashes or loses network connectivity.
  • Coordinator-environment: A transaction coordinator, in turn, manages communication with transactional objects and resources that participate in transactions.
  • Object-store: JBoss transaction service uses an ObjectStore to persistently record the outcomes of transactions, for failure recovery. As a matter of fact, the RecoveryManager scans the ObjectStore and other locations of information, looking for transactions and resources that require, or may require, recovery..

Transaction configuration

Here’s a sample custom transaction configuration:

<subsystem xmlns="urn:jboss:domain:transactions:1.3">
            <core-environment>
                <process-id>
                    <uuid/>
                </process-id>
            </core-environment>
            <recovery-environment socket-binding="txn-recovery-environment" status-socket-binding="txn-status-manager"/>
            <coordinator-environment default-timeout="300"/>
</subsystem>
  • default-timeout, specifies the default transaction timeout (in seconds) for your JTA Transactions. The default value for it is 300 seconds.
  • enable-statistics determines whether or not the transaction service should gather statistical information. The default is to not gather this information.

You can also enable transaction statistics by setting the wildfly.transactions.statistics-enabled property at start-up. Example:

set "JAVA_OPTS=%JAVA_OPTS% -Dwildfly.transactions.statistics-enabled=true"

Finally, since WildFly 23 it is possible to specify the maximum-timeout attribute which comes into play if you set a default-timeout of “0“. In this case, to avoid an unlimited transaction timeout, WildFly will use the maximum-timeout attribute as  default value for transactions.

Managing Transactions in applications

A common strategy to start a JTA Transaction is to use an EJB or a @Transactional CDI Component (see this article to learn mode: Using Transactions in CDI Beans). Then, if the duration of these transactions exceeds the specified timeout setting, the transaction service will roll-back the transactions automatically.
You can modify the default transaction timeout either at application server level by issuing from the CLI:

/subsystem=transactions/:write-attribute(name=default-timeout,value=300)

Or you can specify this annotation at class/method level of your transactional components (i.e. EJBs)
The TransactionTimeout annotation is used to specify the transaction timeout for a given method.

For example:

@TransactionTimeout(value = 10, unit = TimeUnit.SECONDS)

As an alternative you can specify the transaction timeout in the Deployment Descriptor of your jboss-ejb3.xml as follows:

<jboss:ejb-jar xmlns:jboss="http://www.jboss.com/xml/ns/javaee"
               xmlns="http://java.sun.com/xml/ns/javaee"
               xmlns:tx="urn:trans-timeout"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee 
http://www.jboss.org/j2ee/schema/jboss-ejb3-2_0.xsd
http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd
urn:trans-timeout 
http://www.jboss.org/j2ee/schema/trans-timeout-1_0.xsd"
               version="3.1"
               impl-version="2.0">
    <assembly-descriptor>
        <container-transaction>
            <method>
                <ejb-name>MyBeanWithTimeoutValue</ejb-name>
                <method-name>*</method-name>
                <method-intf>Local</method-intf>
            </method>
            <tx:trans-timeout>
                <tx:timeout>10</tx:timeout>
                <tx:unit>Seconds</tx:unit>
            </tx:trans-timeout>
        </container-transaction>
    </assembly-descriptor>
</jboss:ejb-jar>

You can set the maximum timeout from the CLI as follows:

/subsystem=transactions:write-attribute(name=maximum-timeout,value=400)

Next, you need to check that JTA statistics are enabled. The default is false, in order to enable it you can issue:

/subsystem=transactions/:write-attribute(name=enable-statistics,value=true)

Finally, you can monitor your transaction statistics either from the CLI by issuing:

/subsystem=transactions/:read-resource(recursive-depth=0,include-runtime=true)

As an alternative, you can use the Admin Console, by selecting the Runtime upper tab and the Subsystems->Transactions view

jta transactions jboss wildfly tutorial jta

Managing Transaction from the CLI

Here is a set of handy CLI commands to manage JTA transactions.

Firstly, to view all Prepared Transactions:

/subsystem=transactions/log-store=log-store:read-children-names(child-type=transactions)

Then, t view all attributes of a single Transaction:

/subsystem=transactions/log-store=log-store/transactions=0:ffff7f000001-b66efc2:4f9e6f8f:9:read-resource 

To delete a Transaction:

/profile=default/subsystem=transactions/log-store=log-store/transactions=0:ffff7f000001:-b66efc2:4f9e6f8f:9:delete

Finally, to recover a Transaction:

/profile=default/subsystem=transactions/log-store=log-store/transactions=0:ffff7f000001:b66efc2:4f9e6f8f:9/participants=2:recover

Node identifier property is set to the default value. Please make sure it is unique.

When using XA Transaction, the Transaction Manager requires that you set an unique node-identifier in order to be able to recover an XA transaction properly.

If you don’t set it, the following WARN will be displayed:

WFLYTX0013: Node identifier property is set to the default value. Please make sure it is unique.

If you aren’t using XA transactions, then the above WARN is harmless.

You can set the node identifier as follows. Firstly, define a System Property:

/system-property=jboss.tx.node.id:add(value=server1)

Then, use this system property to set the transactions subsystem’s node-identifier attribute:

/subsystem=transactions:write-attribute(name=node-identifier,value="${jboss.tx.node.id}")

That will result in the following configuration:

<subsystem xmlns="urn:jboss:domain:transactions:6.0">
    <core-environment node-identifier="${jboss.tx.node.id}">

Using a System Property to decouple the assignment of the node-identifier is especially useful in domain mode, where you can have multiple servers on the same configuration:

/host=host1/server-config=serverA/system-property=jboss.tx.node.id:add(boot-time=true,value=host1serverA)
/host=host1/server-config=serverB/system-property=jboss.tx.node.id:add(boot-time=true,value=host1serverB)

Now it’s sufficient to set the single property for the profile we’re working with:

/profile=full-ha/subsystem=transactions:write-attribute(name=node-identifier,value="${jboss.tx.node.id}")