ONJava.com    
 Published on ONJava.com (http://www.onjava.com/)
 See this if you're having trouble printing code examples


J2EE Transaction Frameworks: Distributed Transaction Primer

by Dibyendu Baksi
05/23/2001

In the first part of this series, we covered the basics of transaction processing in a distributed component based system. In this part, the two main types of transactions possible in a J2EE configuration are described. Issues of isolation and serialization of resource managers are discussed. Explicit transaction management using JTA API for the three typical J2EE configurations is described in detail.

Transactions in J2EE configurations

In Part 1 of this series we ended the discussion identifying the three common J2EE configurations found in applications. For the benefit of the reader, the figure showing those configurations is repeated below.


Diagrame of recommended J2EE configurations.
Figure 1. Recommended J2EE configurations

This part will explain how the different types of transaction types map to the cases mentioned above.

J2EE transaction types

The typical configuration of a J2EE application is an application component deployed in a container provided by an application server. The application component needs transactional access to multiple resource managers and uses a transaction manager that's responsibile for managing transactions across multiple resource managers.

Diagram of participating components in a J2EE transaction.
Figure 2. Participating components in a J2EE transaction

A resource manager as shown in the figure above can support two types of transactions:

A transaction manager as shown above (1) coordinates transactions across multiple resource managers and (2) provides additional low level services to propagate transactional context across multiple resource managers. The J2EE connector architecture defines a transaction management contract between an application server and a resource adapter with its underlying resource manager. The transaction management contract has two parts, depending on the type of transaction:

An application server vendor uses this to provide the infrastructure and runtime environment for transaction management. Application components in turn rely on this transaction infrastructure to support their component level transaction model.

Note: It is important to emphasize that most enterprise information systems support some form of transaction management. For example, a typical JDBC database allows multiple SQL updates to be grouped in an atomic transaction. Components should always access any resource (database or an enterprise information system) under the scope of a transaction since this provides some guarantee on the integrity and consistency of underlying data being accessed. Such systems can be accessed under either a JTA transaction or a resource manager (RM) local transaction.

In the following figure, a client invokes a method on the remote interface of an EJB A. EJB A accesses resources managed by an EIS A and calls EJB Y to access another EIS B.

Diagram of transactions across multiple Resource Managers.
Figure 3. Transactions across multiple Resource Managers.

The application server uses a transaction manager to enable an application component to perform transactional access across multiple EIS resource managers. The transaction manager manages transactions across multiple resource managers and supports propagation of the transaction context across distributed systems. The transaction manager supports a JTA XAResource transaction management contract with a resource manager. The EIS system A supports JTA transactions by implementing a XAResource interface through its resource manager. The EIS system B also implements the XAResource interface. This interface enables the two resource managers to participate in transactions that are coordinated by an external transaction manager. The XAResource interface is used by the transaction manager to manage transactions across the two underlying resource managers. As the EJBs A and B access the EIS systems, the application server enlists the connections to both the systems from their respective resource managers. Finally when the transaction commits, the transaction manager performs a 2PC protocol across the two resource managers, ensuring that all read and write access to resources managed by both the EIS systems is either entirely committed or entirely rolled back.

Currently, the J2EE platform is only required to support access to a single JDBC database within a transaction (multiple connections to the same database are allowed). It is not required to support access to multiple JDBC databases within a single transaction. It is also not required to support access to other types of enterprise information systems such as enterprise resource planning systems. However, some products might choose to provide these extra transactional capabilities. For example, the J2EE SDK supports access to multiple JDBC databases in one transaction using the two-phase commit protocol. It is important for developers to understand and distinguish which transaction capabilities are required and which are optional in a J2EE product. For true portability of an application, only features required by the J2EE specification should be used. For example, if a J2EE application needs to access multiple databases under a single transaction, it will not run properly on a J2EE product that does not support two-phase commit.

Digram of J2EE transaction types and components.
Figure 4. J2EE transaction types and its components.

The figure shown above illustrates the different types of transaction management and their associated components. It is meant to clarify the relationships between JTA transaction, the EJBs and their bean managed or container managed demarcation. Note that it is always possible to do explicit local transactions from fat clients, servlets or EJBs provided the resource manager offers some transactional API.

Serialization and isolation levels

An isolation level defines how concurrent transactions accessing a RDBMS or an EIS are isolated from one another for read purposes. The following the isolation levels are generally supported in any RDBMS or EIS. These levels are defined in the ANSI SQL92 standard in terms of three phenomena that must be prevented between concurrent transactions.

Following are the descriptions of the isolation levels.

Isolation level summary
Isolation Level Dirty Read Non Repeatable Read Phantom Read
TRANSACTION_READ_UNCOMMITTED YES YES YES
TRANSACTION_READ_COMMITTED NO YES YES
TRANSACTION_REPEATABLE_READ NO NO YES
TRANSACTION_SERIALIZABLE NO NO NO

The table shown above summarizes the discussion. A lower level of isolation allows greater concurrency at the expense of more complicated logic to deal with potential data inconsistencies. A useful guideline is to use the highest isolation level provided by the RDBMS or the EIS that gives acceptable performance. Although the TX_SERIALIZABLE attribute guarantees the highest level of data integrity, it is offset by a performance trade-off because even simple reads must wait in line. All RDBMS or EIS used by a J2EE application should use the same isolation level for consistency reasons since the current J2EE specification does not define a standard way to set isolation levels when an EIS is accessed via JTA transactions. If a J2EE product does not provide a way to configure the isolation level, the RDBMS or EIS will use a default isolation level, which for most of the relational databases is TRANSACTION_READ_COMMITTED. It is strongly recommended not to change the isolation level within a transaction, especially if some work has already been done. Some enterprise information systems may even force a commit if it is attempted to change the isolation level in the middle of a transaction. They're mapped in JDBC to the static variables defined in the java.sql.Connection interface.

JTA/XA Transactions

A transaction managed and coordinated by the J2EE platform is a JTA or XA transaction. A J2EE product is required to support JTA transactions according to the transaction requirements defined in the J2EE specification. There are two ways to begin a JTA transaction. A component can begin a JTA transaction explicitly using the JTA javax.transaction.UserTransaction interface or it can also be started implicitly or automatically by the EJB container if an EJB bean uses container managed transaction specification. The main benefit of using JTA transactions is the ability to seamlessly combine multiple application components and RDBMS/EIS accesses into one single transaction with a little coding effort. For example, if a component X begins a JTA transaction and invokes a method of component Y, the transaction will be propagated transparently from component X to Y by the platform. Enterprise beans using container-managed transaction demarcation will not need to begin or commit transactions programmatically as the demarcation is handled automatically by the EJB container itself. It is always recommended to access an RDBMS or EIS within the scope of a JTA transaction. JTA allows applications to access transaction management independent of any specific implementation by specifying standard Java interfaces between a transaction manager, the transactional application, the J2EE server, and the resource managers.

OMG Object Transaction Service
JTS specifies the implementation of a transaction manager that supports JTA and implements the Java mapping of the OMG Object Transaction Service (OTS) 1.1 specification. JTS propagates transactions using IIOP. A JTS transaction manager provides the services and management functions required to support transaction demarcation, transactional resource management, synchronization, and transaction context propagation. An application component developer uses the JTA UserTransaction interface to demarcate JTA transaction boundaries in components. The JTS TransactionManager and XAResource interfaces are low level APIs between a J2EE server and enterprise information system resource managers and are not intended to be used by applications. A J2EE platform might choose to use a JTS implementation to support the transaction semantics defined in J2EE specification. An example is the J2EE SDK. The JTS implementation is transparent to J2EE components. Components should never interact directly with JTS. Instead, they should use the JTA UserTransaction interface for transaction demarcation.

Transactions in clients: Case I

This is the case where the J2EE configuration is {Stand-alone Client <-> EJB Container <-> RDBMS/EIS Resources}. Transaction support in applets and application clients is not required by the J2EE platform. However, a J2EE product might choose to implement and provide this capability for added value. So the ability of applets and fat standalone application clients to directly access a UserTransaction object depends entirely on the capabilities provided by the container. To ensure portability, applets and application clients should delegate transactional work to the lower tier of enterprise beans.

Transactions in web components: Case II

This is the case where the J2EE configuration is {Browser <-> Web Container <-> RDBMS/EIS Resources}. It is possible for a servlet or a JSP page to use JNDI lookup to get hold of a UserTransaction object and use the underlying interface to programmatically specify transactions. In a two-tier application configuration where a web component needs to access enterprise information systems under the scope of a JTA transaction, this is quite common. The code snippet below illustrates the use of the JTA interface to specify transactions within a web component:

Context myCntxt = new InitialContext();
UserTransaction ut =
(UserTransaction) myCntxt.lookup("java:comp/UserTransaction");
ut.begin();
// perform transactional work here
ut.commit();

It is important to keep in mind that a web component like a servlet may only start a transaction in its service method. Moreover, a transaction started by a servlet or a JSP page must be completed before the service method returns. Transactions cannot span across web requests. The following guidelines are recommended for handling interactions in web components between JTA transactions, threads, and JDBC connections.

Transactions in application servers: Case III

This is the case where the J2EE configuration is {Browser <-> Web Container <-> EJB Container <-> RDBMS/EIS Resources}. The following scenarios are illustrated to motivate discussions on use of transactions in application servers with EJB container and a built in transaction monitor.

Scenario A : Messages sent/received over JMS and multiple databases access:

It's possible for an application using EJB servers to send messages to or receive messages from one or more JMS destinations or update data in one or more databases in a single transaction. In the following figure, a client invokes a method on the remote interface of enterprise Bean A. which in turn sends a message to a JMS queue and updates data in a database A. After that, EJB A calls a method of another enterprise Bean B that updates data in database B. The application server with its EJB container and built in transaction manager ensures that the operations on A, B, and C are either all committed or rolled back.

Diagram of message sent to JMS queue and updates to multiple databases.
Figure 5. Message sent to JMS queue and updates to multiple databases

The application programmer does not have to do anything to ensure transactional semantics. The EJBs A and B perform the sending of the message and database updates using the standard JMS and JDBC APIs. Behind the scenes, the application server enlists the session on the connection to the JMS provider and the database connections as part of the transaction. When the transaction commits, the application server and the messaging and database systems perform a two-phase commit protocol to ensure atomic updates across all the three resources.

Scenario B: Multiple database access via multiple application servers:

The J2EE architecture allows updates of data at multiple sites to be performed in a single transaction. In the following figure, a client invokes a method on enterprise Bean A. which in turn sends a message to a message queue, updates data in database A. After that, EJB A calls a method in another EJB B deployed in another application server which updates data in database B. The EJB architecture makes it possible to perform the updates to databases A and B in a single transaction.

Diagram of updates to multiple databases in same transaction.
Figure 6. Updates to multiple databases in same transaction

When EJB A invokes EJB B, the two application servers cooperate to propagate the transaction context from A to B. This transaction context propagation is transparent to the application. At commit time, the two application servers use distributed two phase commit protocol (if the capability exists) to ensure the atomicity of the database updates and the transaction.

The two types of transaction demarcation in enterprise beans, namely, bean managed and container managed, are discussed in detail next.

Bean managed transaction

As mentioned before, in a bean-managed transaction, an enterprise bean uses the javax.transaction.UserTransaction interface to explicitly specify transaction boundaries in the application code. Only session beans and message driven beans can choose to use bean-managed demarcation. An entity bean must always use container managed transaction demarcation. The following code illustrates the use of JTA interface to specify transactions in an enterprise bean with bean-managed transaction demarcation.

UserTransaction ut = ejbContext.getUserTransaction();
ut.begin();
// Transactional work is done here
ut.commit();

The following example illustrates a business method of a typical session bean that performs a bean managed transaction involving both a database connection and a JMS connection.

public class MySessionEJB implements SessionBean {
EJBContext ejbContext;
public void someMethod(...) {
javax.transaction.UserTransaction ut;
javax.sql.DataSource ds;
java.sql.Connection dcon;
java.sql.Statement stmt;
javax.jms.QueueConnectionFactory qcf;
javax.jms.QueueConnection qcon;
javax.jms.Queue q;
javax.jms.QueueSession qsession;
javax.jms.QueueSender qsender;
javax.jms.Message message;
InitialContext initCtx = new InitialContext();
// obtain db conn object and set it up for transactions
ds = (javax.sql.DataSource)
initCtx.lookup("java:comp/env/jdbc/Database");
dcon = ds.getConnection();
stmt = dcon.createStatement();
// obtain jms conn object and set up session for transactions
qcf = (javax.jms.QueueConnectionFactory)
initCtx.lookup("java:comp/env/jms/qConnFactory");
qcon = qcf.createQueueConnection();
qsession = qcon.createQueueSession(true,0);
q = (javax.jms.Queue)
initCtx.lookup("java:comp/env/jms/jmsQueue");
qsender = qsession.createSender(q);
message = qsession.createTextMessage();
message.setText("some message");
//
// Now do a transaction that involves the two connections.
//
ut = ejbContext.getUserTransaction();
// start the transaction
ut.begin();
// Do database updates and send message. The Container
// automatically enlists dcon and qsession with the
// transaction.
stmt.executeQuery(...);
stmt.executeUpdate(...);
stmt.executeUpdate(...);
qsender.send(message);
// commit the transaction
ut.commit();

// release connections
stmt.close();
qsender.close();
qsession.close();
dcon.close();
qcon.close();
}
...
}

The following example illustrates a stateful session bean that retains transaction context across three client calls, invoked in the order {method1, method2, and method3}.

public class MySessionEJB implements SessionBean {
EJBContext ejbContext;
javax.sql.DataSource ds1;
javax.sql.DataSource ds2;
java.sql.Connection con1;
java.sql.Connection con2;
public void method1(...) {
java.sql.Statement stmt;
InitialContext initCtx = new InitialContext();
// obtain user transaction interface
ut = ejbContext.getUserTransaction();
// start a transaction
ut.begin();
// make some updates on con1
ds1 = (javax.sql.DataSource)
initCtx.lookup("java:comp/env/jdbc/Database1");
con1 = ds1.getConnection();
stmt = con1.createStatement();
stmt.executeUpdate(...);
stmt.executeUpdate(...);
//
// The Container retains the transaction associated with the
// instance to the next client call (which is method2(...)).
}
public void method2(...) {
java.sql.Statement stmt;
InitialContext initCtx = new InitialContext();
// make some updates on con2
ds2 = (javax.sql.DataSource)
initCtx.lookup("java:comp/env/jdbc/Database2");
con2 = ds2.getConnection();
stmt = con2.createStatement();
stmt.executeUpdate(...);
stmt.executeUpdate(...);
// The Container retains the transaction associated with the
// instance to the next client call (which is method3(...)).
}
public void method3(...) {
java.sql.Statement stmt;
// obtain user transaction interface
ut = ejbContext.getUserTransaction();
// make some more updates on con1 and con2
stmt = con1.createStatement();
stmt.executeUpdate(...);
stmt = con2.createStatement();
stmt.executeUpdate(...);
// commit the transaction
ut.commit();
// release connections
stmt.close();
con1.close();
con2.close();
}
...
}

t is possible for an enterprise bean to open and close a database connection in each business method rather than hold the connection open until the end of transaction. If the client in the following example executes the sequence of methods {method1, method2, method2, method3}, all the database updates done by the multiple invocations of method2 are performed in the scope of the same transaction. This is the transaction started in method1 and committed in method3.

public class MySessionEJB implements SessionBean {
EJBContext ejbContext;
InitialContext initCtx;
public void method1(...) {
       java.sql.Statement stmt;
    		// obtain user transaction interface
 		ut = ejbContext.getUserTransaction();
 		// start a transaction
 		ut.begin();
 	}

public void method2(...) {
 		javax.sql.DataSource ds;
 		java.sql.Connection con;
  		java.sql.Statement stmt;
 		// open connection
 		ds = (javax.sql.DataSource)
 		initCtx.lookup("java:comp/env/jdbc/Database");
 		con = ds.getConnection();
 		// make some updates on con
 		stmt = con.createStatement();
 		stmt.executeUpdate(...);
 		stmt.executeUpdate(...);
 		// close the connection
 		stmt.close();
 		con.close();
}
public void method3(...) {
 		// obtain user transaction interface
 		ut = ejbContext.getUserTransaction();
 		// commit the transaction
 	ut.commit();
}
...
}

An enterprise bean with bean-managed transaction specification need not and should not use the getRollbackOnly() and setRollbackOnly()methods of the EJBContext interface since, if necessary, it can obtain the status of a transaction by using the getStatus() method of the javax.transaction.UserTransaction interface. It can also roll back a transaction using the rollback()method of the same interface if required.

Bean Managed transaction for Message Driven Beans

There is never a client transaction context available when a message driven bean is invoked because a distributed transaction context does not flow with a JMS message. When a message driven bean using bean managed transaction uses the javax.transaction. UserTransaction interface to specify transactions, the message receipt that causes the onMessage() method of the bean to be invoked is not part of the transaction. Container managed transaction with the Required transaction attribute must be used if receipt of a message is to be part of a transaction. A message driven bean's newInstance, setMessageDrivenContext, ejbCreate, and ejbRemove methods are called with an unspecified transaction context.

Dibyendu Baksi is a J2EE transactions systems and frameworks designer and developer for Sun Microsystems, Inc.


Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.