Transactions are the key abstraction to provide reliability with large scale distributed systems and are a primary component of enterprise systems. This specification provides an OSGi service based design for the Java Transaction Architecture (JTA) Specification, which describes the standard transaction model for Java applications. Providing the JTA specification as a service based model enables the use of independent implementations. This JTA Transaction Services Specification provides a managed model, where an Application Container (such as the Java EE EJB container) manages the transaction and the enlistment of resources, and an unmanaged model, where each application is responsible for these tasks itself.
This specification provides a brief overview of JTA and then the use of it through 3 transaction services: User Transaction, Transaction Manager, and Transaction Synchronization.
This specification is based on [1] Java Transaction API Specification 1.1.
-
Portability - It is important that applications are easy to port from other environments that support JTA.
-
Plugability - Allow different vendors to provide implementations of this specification.
-
JTA Compatible - Support full JTA 1.1 Specification
-
JTA Provider - Implementation of this specification. It is responsible, on request from a Transaction Originator, for starting and ending transactions and coordinating the work of Resource Managers that become involved in each Transaction. This entity provides the User Transaction service, Transaction Manager service, and the Transaction Synchronization Registry service.
-
Transaction - An atomic unit of work that is associated with a thread of execution.
-
Transaction Originator - An Application or its Container, that directs the JTA Provider to begin and end Transactions.
-
User Transaction - A service used by a Transaction Originator for beginning and ending transactions.
-
Transaction Manager - A service used by a Transaction Originator for managing both transaction demarcation and enlistment of Durable Resources or Volatile Resources.
-
Transaction Synchronization Registry - A service for enlistment of Volatile Resources for getting notifications before and after ending Transactions.
-
Application Bundle - An entity that initiates work that executes under a Transaction.
-
Container - An entity that is distinct from the Application and which provides a managed environment for Applications. Unmanaged environments do not distinguish between the Application and Container entities.
-
Resource Manager - Provides the transactional resources whose work is externally coordinated by a JTA Provider. Examples of Resource Managers include databases, Java Message Service providers and enterprise information systems.
-
Durable Resource - A resource whose work is made durable when the Transaction is successfully committed. Durable Resources can be enlisted with a Transaction to ensure that work is performed within the scope of the Transaction and to participate in the outcome of a Transaction. Durable Resource enlistment is the responsibility of the Application Bundle or its Container. Durable Resources implement the
javax.transaction.xa.XAResource
interface -
Volatile Resource - Resources that are associated with a Transaction but are no longer needed after the Transaction, for example transaction-scoped caches. Volatile Resources are registered with the JTA Provider to receive notifications before and after the outcome of the Transaction. Volatile Resources implement the
javax.transaction.Synchronization
interface -
Transaction Services - The triplet of the User Transaction, Transaction Manager, and Transaction Synchronization Registry services registered by the JTA Provider.
This specification is based on the following packages:
javax.transaction
javax.transaction.xa
These packages must be exported as version 1.1.
The JTA Provider register the Transaction Services:
-
User Transaction - Offers transaction demarcation capabilities to an Application bundle.
-
Transaction Manager - Offers transaction demarcation and further transaction management capabilities to an Application Bundle or an Application Container.
-
Transaction Synchronization Registry - Offers a callback registration service for volatile transactional participants wishing to be notified of the completion of the transaction.
A JTA Provider must register these services when it is started. A
JTA Provider may put restrictions on which bundles can use these
services. For example, in a Java EE environment, the JTA Provider does
not expose the TransactionManager
interface to
applications. An OSGi environment which supports the Java EE
specifications will typically provide access to the Transaction Manager
service only to Java EE Containers.
A typical example of the use of a transaction is for transferring money from one bank account to another. Two Durable Resources are involved, one provided by the database from which the money is to be withdrawn and another provided by the database to which the money will be deposited. An Application Bundle acting as the Transaction Originator gets the User Transaction service and uses it to begin a transaction. This transaction is associated with the current thread (implicitly) by the JTA Provider. On the same thread of execution, the Application Bundle connects to the database from which the money is to be withdrawn and updates the balance in the source account by the amount to be debited.
The database is a resource manager whose connections have associated XA Resources; the first time a connection is used within the scope of a new transaction the Application Bundle, or a Container, obtains the XA Resource associated with the connection and enlists it with the JTA Provider through the Transaction Manager service. On the same thread of execution, the Application Bundle connects to the second database and updates the balance in the target account by the amount to be credited. An XA Resource for the second connection is enlisted with the Transaction Manager service as well by the Application Bundle or a Container.
Now that the money has been transferred the Transaction Originator requests a commit of the Transaction (on the same thread of execution) via the User Transaction Service, causing the JTA Provider to initiate the two-phase commit process with the two Resource Managers through the enlisted XA Resources. The transaction is then atomically committed or rolled back.
A transaction is a unit of work in which interactions with multiple participants can be coordinated by a third party such that the final outcome of these interactions has well-defined transactional semantics. A variety of well-known transaction models exist with specific characteristics; the transactions described in this specification provide Atomic Consistent Isolated and Durable (ACID) semantics as defined in [2] XA+ Specification whereby all the participants in a transaction are coordinated to an atomic outcome in which the work of all the participants is either completely committed or completely rolled back.
The [2] XA+ Specification defines a Distributed Transaction Processing (DTP) software architecture for transactional work that is distributed across multiple Resource Managers and coordinated externally by a Transaction Manager using the two-phase commit XA protocol. The DTP architecture defines the roles of the Transaction Manager and Resource Manager; this specification uses the term JTA Provider rather than Transaction Manager to distinguish it from the Transaction Manager service. Note that Distributed Transaction Processing does not imply distribution of transactions across multiple frameworks or JVMs.
The [1] Java Transaction API Specification 1.1 defines the Java interfaces required for the management of transactions on the enterprise Java platform.
A transaction may be a local transaction or a global transaction. A local transaction is a unit of work that is local to a single Resource Manager and may succeed or fail independently of the work of other Resource Managers. A global transaction, sometimes referred to as a distributed transaction, is a unit of work that may encompass multiple Resource Managers and is coordinated by a JTA Provider external to the Resource Manager(s) as described in the DTP architecture. The term transaction in this specification always refers to a global transaction.
The JTA Provider is responsible for servicing requests from a Transaction Originator to create and complete transactions, it manages the state of each transaction it creates, the association of each transaction with the thread of execution, and the coordination of any Resource Managers that become involved in the global transaction. The JTA Provider ensures that each transaction is associated with, at most, one application thread at a time and provides the means to move that association from one thread to another as needed.
The model for resource commit coordination is the two phase commit XA protocol, with Resource Managers being directed by the JTA Provider. The first time an Application accesses a Resource Manager within the scope of a new global transaction, the Application, or its Container, obtains an XA Resource from the Resource Manager and enlists this XA Resource with the JTA Provider.
At the end of a transaction, the Transaction Originator must decide whether to initiate a commit or rollback request for all the changes made within the scope of the Transaction. The Transaction Originator requests that the JTA Provider completes the transaction. The JTA Provider then negotiates with each enlisted Resource Manager to reach a coordinated outcome. A failure in the transaction at any point before the second phase of two-phase commit results in the transaction being rolled back.
XA is a presumed abort protocol and implementations of XA-compliant JTA Providers and Resource Managers can be highly optimized to perform no logging of transactional state until a commit decision is required. A Resource Manager durably records its prepare decision, and a JTA Provider durably records any commit decision it makes. Failures between a decision on the outcome of a transaction and the enactment of that outcome are handled during transaction recovery to ensure the atomic outcome of the transaction.
Durable Resources are provided by Resource Managers and must
implement the XAResource
interface described in the [1] Java Transaction API Specification 1.1. An XAResource
object is enlisted with
a transaction to ensure that the work of the Resource Manager is
associated with the correct transaction and to participate in the
two-phase commit process. The XAResource
interface is
driven by the JTA Provider during the completion of the transaction and
is used to direct the Resource Manager to commit or rollback any changes
made under the corresponding transaction.
Volatile resources are components that do not participate in the
two phase commit but are called immediately prior to and after the two
phase commit. They implement the [1] Java Transaction API Specification 1.1
Synchronization
interface. If a request is made to commit a
transaction then the volatile participants have the opportunity to
perform some before completion processing such as
flushing cached updates to persistent storage. Failures during the
before completion processing must cause the
transaction to rollback. In both the commit and rollback cases the
volatile resources are called after two phase commit to perform
after completion processing. After
completion procession cannot affect the outcome of the
transaction.
As noted above in Global and Local Transactions, a global transaction must not be associated with more than one application thread at a time but can be moved over time from one application thread to another. In some environments Applications run in containers which restrict the ability of the Application component to explicitly manage the transaction-thread association by restricting access to the Transaction Manager. For example, Java EE application servers provide web and EJB Containers for application components and, while the Containers themselves can explicitly manage transaction-thread associations, these containers do not allow the Applications to do so. Applications running in these containers are required to complete any transactions they start on that same application thread. In general, Applications that run inside a Container must follow the rules defined by that Container. For further details of the considerations specific to Java EE containers, see the section Transactions and Threads in [4] Java Platform, Enterprise Edition (Java EE) Specification, v5.
An Application is a bundle that may use transactions, either as a Transaction Originator or as a bundle that is called as part of an existing transaction. A Transaction Originator Application bundle starts a transaction and end it with a commit or rollback using the User Transaction or Transaction Manager service.
A Transaction Originator Application bundle may not make use of Resource Managers itself but may simply provide transaction demarcation and then call other bundles which do use Resource Managers. In such a case the Transaction Originator Application bundle requires only the use of the User Transaction service for transaction demarcation. The called bundles may use the Transaction Manager service if they use Resource Managers.
Application Bundles that use Resource Managers have to know the enlistment strategy for the Resource Managers they use. There are two possibilities:
-
Application Bundle Enlistment - The Application Bundle must enlist the Resource Managers itself. For each Resource Manager it uses it must enlist that Resource Manager with the Transaction Manager.
-
Container-Managed Enlistment - An Application runs in a container, such as a Java EE Container, which manages the Resource Manager enlistment on behalf of the Application.
These scenarios are explained in the following sections.
A Transaction Originator Application bundle that uses no Resource Managers itself but starts a Transaction before calling another bundle may use the User Transaction service to control the Transaction demarcation.
For example, an Application can use the User Transaction service to begin a global transaction:
UserTransaction ut = getUserTransaction();
ut.begin();
The User Transaction service associates a transaction with the current thread until that transaction is completed via:
UserTransaction ut = getUserTransaction();
ut.commit();
Or the equivalent rollback
method. The
getUserTransaction
method implementation (not shown) can
get the User Transaction service directly from the service registry or
from an injected field.
An Application Bundle is responsible for enlisting Resource
Managers itself. That is, it must enlist Resource Manager it uses with
the Transaction Manager service. The Transaction
Manager service is an implementation of the JTA
TransactionManager
interface, registered by the JTA
Provider.
For example, an Application Bundle can get an
XADataSource
object from a Data Source Factory service.
Such a Data Source object can provide an XAConnection
object that then can provide an XAResource
object.
XAResource
objects can then be enlisted with the
Transaction Manager service.
For example:
TransactionManager tm;
XADataSource left;
XADataSource right;
void acid() throws Exception {
tm.begin();
Transaction transaction = tm.getTransaction();
try {
XAConnection left = this.left.getXAConnection();
XAConnection right = this.right.getXAConnection();
transaction.enlistResource( left
.getXAResource());
transaction.enlistResource( right
.getXAResource());
doWork(left.getConnection(), right.getConnection());
tm.commit();
} catch( Throwable t ) {
tm.rollback();
throw t; } }
// ...
void setTransactionManager( TransactionManager tm ) { this.tm= tm; }
void setDataSourceFactory( DataSourceFactory dsf ) {
left = dsf.createXADataSource( getLeftProperties() );
right = dsf.createXADataSource( getRightProperties() );
}
In the previous example, the Transaction Manager service could have been injected with a component model like Declarative Services:
<reference interface="javax.transaction.TransactionManager"
bind="setTransactionManager"/>
<reference name="dsf" interface="org.osgi.service.jdbc.DataSourceFactory"
bind="setDataSourceFactory"/>
For example, it is possible to provide a Data Source service that
provides automatic enlistment of the Connection as an XA Resource when
one of its getConnection
methods is called inside a
transaction. The following code contains a Declarative Service component
that implement this design. The component references a Transaction
Manager service and a Data Source Factory service and provides a Data
Source service that proxies an XA Data Source. Applications depend on
the Data Source service, assuming that the Data Source service
automatically enlists the connections it uses inside a transaction. See
for an overview Figure 123.2 on page .
This general purpose Data Source Proxy component can be fully
configured by the Configuration Admin service to instantiate this
component for each needed database connection. The Declarative Services
service properties can be used to select a Data Source Factory for the
required database driver (using the target
), as well as
provide the configuration properties for the creation of an XA Data
Source. That is, such a component could be part of a support
library.
The code for such an Application component could start like:
public class DataSourceProxy implements DataSource{
Properties properties = new Properties();
TransactionManager tm;
XADataSource xads;
The activate
method is called when the component's
dependencies are met, that is, there is a Transaction Manager service as
well as a matching Data Source Factory service. In this method, the
properties of the component are copied to a Properties
object to be compatible with the Data Source Factory factory
methods.
void activate(ComponentContext c) {
// copy the properties set by the Config Admin into properties
...
}
The relevant methods in the Data Source Proxy component are the
getConnection
methods. The contract for this proxy
component is that it enlists the XA Data Connection's XA Resource when
it is called inside a transaction. This enlistment is done in the
private enlist
method.
public Connection getConnection() throws SQLException{
XAConnection connection = xads.getXAConnection();
return enlist(connection); }
public Connection getConnection(String username, String password)
throws SQLException {
XAConnection connection = xads.getXAConnection(username,password);
return enlist(connection); }
The enlist
method checks if there currently is a
transaction active. If not, it ignores the enlistment, the connection
will then not be connection to the transaction. If there is a current
transaction, it enlists the corresponding XA Resource.
private Connection enlist(XAConnection connection)throws SQLException {
try {
Transaction transaction = tm.getTransaction();
if (transaction != null)
transaction.enlistResource( connection.getXAResource());
} catch (Exception e) {
SQLException sqle=
new SQLException("Failed to enlist");
sqle.initCause(e);
throw sqle;
}
return connection.getConnection();
}
What remains are a number of boilerplate methods that forward to the XA Data Source or set the dependencies.
void setTransactionManager(TransactionManagertm) { this.tm = tm;}
void setDataSourceFactory(DataSourceFactory dsf) throws Exception{
xads = dsf.createXADataSource(properties);}
public PrintWriter getLogWriter()
throws SQLException { return xads.getLogWriter(); }
public int getLoginTimeout()
throws SQLException { return xads.getLoginTimeout();}
public void setLogWriter(PrintWriter out)
throws SQLException { xads.setLogWriter(out); }
public void setLoginTimeout(int seconds)
throws SQLException { xads.setLoginTimeout(seconds);}
This is a fully coded example, it only lacks the configuration definitions for the Configuration Admin service.
This example Data Source proxy component makes it possible for an Application to depend on a Data Source service. The connections the Application uses from this Data Source are automatically transactional as long as there is a current transaction when the service is called. However, this approach only works when all bundles in the OSGi framework follow the same enlistment strategy because this specification does not provide a common enlistment strategy.
The Application Container is responsible for enlisting Resource Managers used by the Application. For example, the Java EE Web and EJB Containers have a well defined model for managing resources within a transaction. If an Application runs inside a Java EE Container then it is the responsibility of the Java EE Container to handle the resource enlistment, the actual rules are beyond this specification.
A Transaction Originator Application bundle running inside a Container which manages any Resource Managers enlistment may use the User Transaction service for transaction demarcation, assuming this service is made available by the Container.
When a Java EE Container runs inside an OSGi framework then it must ensure that any services seen by its contained Applications are the same Transaction services as other bundles on that OSGi framework.
Resource Managers perform work that needs to be committed or rolled back in a transaction. To participate in a transaction, a Resource Manager must have an XA Resource enlisted with the current transaction. This specification does not define how OSGi service implementations should be enlisted. This can be done by a Java EE Container, the Applications themselves, or through some other unspecified means.
The JTA Provider is the entity that provides the transaction services:
-
User Transaction - A service that implements the JTA
UserTransaction
interface. -
Transaction Manager - A service that implements the JTA
TransactionManager
interface. -
Transaction Synchronization Registry - A service that implements the JTA
TransactionSynchronizationRegistry
interface.
There can be at most one JTA Provider in an OSGi framework and this JTA Provider must ensure that at most one transaction is associated with an application thread at any moment in time. All JTA Provider's transaction services must map to the same underlying JTA implementation. All JTA services should only be registered once.
The User Transaction service may be used by an Application bundle, acting as the Transaction Originator, to demarcate transaction boundaries when the bundle has no need to perform resource enlistment.
The Transaction Manager service offers transaction demarcation and further transaction management capabilities, such as Durable and Volatile resource enlistment, to an Application bundle or Application Container.
The Transaction Synchronization Registry service may be used by an
Application bundle or a Container. The service provides for the
registration of Volatile Resources that implement the JTA
Synchronization
interface.
For example:
private class MyVolatile implements Synchronization{...}
TransactionSynchronizationRegistry tsr = ...; // may be injected
tsr.registerInterposedSynchronization(new MyVolatile());
The life cycle of the transaction services and bundles that make up the JTA Provider must be dealt with appropriately such that implementations always ensure the atomic nature of transactions. When the JTA Provider is stopped and its services are unregistered, the JTA Provider must make sure that all active transactions are dealt with appropriately. A JTA Provider can decide to rollback all active transactions or it can decide to keep track of existing active transactions and allow them to continue to their normal conclusion but not allow any new transactions to be created. Any failures caused by executing code outside their life cycle can be dealt with as general failures. From a transactional consistency point of view, stopping the bundle(s) that implement the JTA Provider while transactional work is in-flight, is no different from a failure of the framework hosting the JTA Provider. In either case transaction recovery is initiated by the JTA Provider after it has re-started.
There are well-defined XA semantics between a JTA Provider and
Resource Managers in the event of a failure of either at any point in a
transaction. If a Resource Manager bundle is stopped while it is
involved in-flight transactions then the JTA Provider should exhibit the
same external behavior it does in the event of a communication failure
with the Resource Manager. For example a JTA Provider will respond to an
XAER_RMFAIL
response resulting from calling the
XAResource commit
method by retrying the
commit
. The mechanism used by the JTA Provider to determine
when to retry the commit
is a detail of the
implementation.
Applications can act in the role of the Transaction Originator. There is no guarantee that an Application that starts a transaction will always be available to complete the transaction since the client can fail independently of the JTA Provider. A failure of the Application Bundle to complete, in a timely fashion, a transaction it originated must finally result in the JTA Provider rolling back the transaction.
This specification does not define a specific error handling strategy. Exceptions and errors that occur during transaction processing can result in the transaction being marked rollback-only by the container or framework in which an Application runs or may be left for the Application to handle. An Application which receives an error or an exception while running under a transaction can choose to mark the transaction rollback-only.
[1]Java Transaction API Specification 1.1https://www.oracle.com/java/technologies/jta.html
[4]Java Platform, Enterprise Edition (Java EE) Specification, v5https://jcp.org/en/jsr/detail?id=244