Chapter 5. Using the Narayana transaction manager
This section provides details for using the Narayana transaction manager by implementing the javax.transaction.UserTransaction
interface, the org.springframework.transaction.PlatformTransactionManager
interface, or the javax.transaction.Transaction
interface. Which interface you choose to use depends on the needs of your application. At the end of this chapter, there is a discussion of the resolution of the problem of enlisting XA resources. The information is organized as follows:
For Java transaction API details, see the Java Transaction API (JTA) 1.2 specification and the Javadoc.
5.1. Using UserTransaction objects
Implement the javax.transaction.UserTransaction
interface for transaction demarcation. That is, for beginning, committing, or rolling back transactions. This is the JTA interface that you are most likely to use directly in your application code. However, the UserTransaction
interface is just one of the ways to demarcate transactions. For a discussion of different ways that you can demarcate transactions, see Chapter 9, Writing a Camel application that uses transactions.
5.1.1. Definition of the UserTransaction interface
The JTA UserTransaction
interface is defined as follows:
public interface javax.transaction.UserTransaction { public void begin(); public void commit(); public void rollback(); public void setRollbackOnly(); public int getStatus(); public void setTransactionTimeout(int seconds); }
5.1.2. Description of UserTransaction methods
The UserTransaction
interface defines the following methods:
- begin()
- Starts a new transaction and associates it with the current thread. If any XA resources get associated with this transaction, the transaction implicitly becomes an XA transaction.
- commit()
Completes the current transaction normally, so that all pending changes become permanent. After the commit, there is no longer a transaction associated with the current thread.
NoteIf the current transaction is marked as rollback only, however, the transaction would actually be rolled back when
commit()
is called.- rollback()
- Aborts the transaction immediately, so that all pending changes are discarded. After the rollback, there is no longer a transaction associated with the current thread.
- setRollbackOnly()
- Modifies the state of the current transaction, so that a rollback is the only possible outcome, but does not perform the rollback yet.
- getStatus()
Returns the status of the current transaction, which can be one of the following integer values, as defined in the
javax.transaction.Status
interface:-
STATUS_ACTIVE
-
STATUS_COMMITTED
-
STATUS_COMMITTING
-
STATUS_MARKED_ROLLBACK
-
STATUS_NO_TRANSACTION
-
STATUS_PREPARED
-
STATUS_PREPARING
-
STATUS_ROLLEDBACK
-
STATUS_ROLLING_BACK
-
STATUS_UNKNOWN
-
- setTransactionTimeout()
- Customizes the timeout of the current transaction, specified in units of seconds. If the transaction is not resolved within the specified timeout, the transaction manager automatically rolls it back.
5.2. Using TransactionManager objects
The most common way to use a javax.transaction.TransactionManager
object is to pass it to a framework API, for example, to the Camel JMS component. This enables the framework to look after transaction demarcation for you. Occasionally, you might want to use a TransactionManager
object directly. This is useful when you need to access advanced transaction APIs such as the suspend()
and resume()
methods.
5.2.1. Definition of the TransactionManager interface
The JTA TransactionManager interface has the following definition:
interface javax.transaction.TransactionManager { // Same as UserTransaction methods public void begin(); public void commit(); public void rollback(); public void setRollbackOnly(); public int getStatus(); public void setTransactionTimeout(int seconds); // Extra TransactionManager methods public Transaction getTransaction(); public Transaction suspend() ; public void resume(Transaction tobj); }
5.2.2. Description of TransactionManager methods
The TransactionManager
interface supports all of the methods found in the UserTransaction
interface. You can use a TransactionManager
object for transaction demarcation. In addition, a TransactionManager
object supports these methods:
- getTransaction()
-
Gets a reference to the current transaction, which is the transaction that is associated with the current thread. If there is no current transaction, this method returns
null
. - suspend()
Detaches the current transaction from the current thread and returns a reference to the transaction. After calling this method, the current thread no longer has a transaction context. Any work that you do after this point is no longer done in the context of a transaction.
NoteNot all transaction managers support suspending transactions. This feature is supported by Narayana, however.
- resume()
- Re-attaches a suspended transaction to the current thread context. After calling this method, the transaction context is restored and any work that you do after this point is done in the context of a transaction.
5.3. Using Transaction objects
You might need to use a javax.transaction.Transaction
object directly if you are suspending/resuming transactions or if you need to enlist a resource explicitly. As discussed in Section 5.4, “Resolving the XA enlistment problem”, a framework or container usually takes care of enlisting resources automatically.
5.3.1. Definition of the Transaction interface
The JTA Transaction
interface has the following definition:
interface javax.transaction.Transaction { public void commit(); public void rollback(); public void setRollbackOnly(); public int getStatus(); public boolean enlistResource(XAResource xaRes); public boolean delistResource(XAResource xaRes, int flag); public void registerSynchronization(Synchronization sync); }
5.3.2. Description of Transaction methods
The commit()
, rollback()
, setRollbackOnly()
, and getStatus()
methods have the same behavior as the corresponding methods from the UserTransaction
interface. In fact, a UserTransaction
object is a convenient wrapper that retrieves the current transaction and then invokes the corresponding methods on the Transaction
object.
Additionally, the Transaction
interface defines the following methods, which have no counterparts in the UserTransaction
interface:
- enlistResource()
Associates an XA resource with the current transaction.
NoteThis method is of key importance in the context of XA transactions. It is precisely the capability to enlist multiple XA resources with the current transaction that characterizes XA transactions. On the other hand, enlisting resources explicitly is a nuisance and you would normally expect your framework or container to do this for you. For example, see Section 5.4, “Resolving the XA enlistment problem”.
- delistResource()
Disassociates the specified resource from the transaction. The flag argument can take one of the following integer values as defined in the
javax.transaction.Transaction
interface:-
TMSUCCESS
-
TMFAIL
-
TMSUSPEND
-
- registerSynchronization()
-
Registers a
javax.transaction.Synchronization
object with the current transaction. TheSynchronization
object receives a callback just before the prepare phase of a commit and receives a callback just after the transaction completes.
5.4. Resolving the XA enlistment problem
The standard JTA approach to enlisting XA resources is to add the XA resource explicitly to the current javax.transaction.Transaction
object, which represents the current transaction. In other words, you must explicitly enlist an XA resource each time a new transaction starts.
5.4.1. How to enlist an XA resource
Enlisting an XA resource with a transaction involves invoking the enlistResource()
method on the Transaction
interface. For example, given a TransactionManager
object and an XAResource
object, you could enlist the XAResource
object as follows:
// Java import javax.transaction.Transaction; import javax.transaction.TransactionManager; import javax.transaction.xa.XAResource; ... // Given: // 'tm' of type TransactionManager // 'xaResource' of type XAResource // Start the transaction tm.begin(); Transaction transaction = tm.getTransaction(); transaction.enlistResource(xaResource); // Do some work... ... // End the transaction tm.commit();
The tricky aspect of enlisting resources is that the resource must be enlisted on each new transaction and the resource must be enlisted before you start to use the resource. If you enlist resources explicitly, you could end up with error-prone code that is littered with enlistResource()
calls. Moreover, sometimes it can be difficult to call enlistResource()
in the right place, for example, this is the case if you are using a framework that hides some of the transaction details.
5.4.2. About auto-enlistment
Instead of explicitly enlisting XA resources, it is easier and safer to use features that support auto-enlistment of XA resources. For example, in the context of using JMS and JDBC resources, the standard technique is to use wrapper classes that support auto-enlistment.
The common pattern, both for JDBC and JMS access is:
-
The application code expects
javax.sql.DataSource
for JDBC access andjavax.jms.ConnectionFactory
for JMS to get JDBC or JMS connections. - Within an application/OSGi server, database or broker specific implementations of these interfaces are registered.
- An application/OSGi server wraps the database/broker-specific factories into generic, pooling, enlisting factories.
In this way, application code still uses javax.sql.DataSource
and javax.jms.ConnectionFactory
, but internally when these are accessed, there is additional functionality, which usually concerns:
- Connection pooling - instead of creating new connections to a database/message broker every time, a pool of pre-initialized connections is used. Another aspect of pooling may be, for example, periodical validation of connections.
-
JTA enlistment - before returning an instance of
java.sql.Connection
(JDBC) orjavax.jms.Connection
(JMS), the real connection objects are registered if they are true XA resources. Registration happens within the JTA transaction if it is available.
With auto-enlistment, application code does not have to change.
For more information about pooling and enlisting wrappers for JDBC data sources and JMS connection factories, see Chapter 6, Using JDBC data sources and Chapter 7, Using JMS connection factories.