Chapter 5. Using the Narayana transaction manager
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: Section 5.1, "Using UserTransaction objects" Section 5.2, "Using TransactionManager objects" Section 5.3, "Using Transaction objects" Section 5.4, "Resolving the XA enlistment problem" 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. Note If 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. Note Not 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. Note This 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. The Synchronization 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 and javax.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) or javax.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 .
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); } 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); } 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); } // 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();