Java EE and .NET Interoperability: Integration Strategies, Patterns, and Best Practices

Java EE Transaction Support

The Java EE container implements support for transactions and facilitates the ACID properties required by the application logic. The container provides an implementation for the two-phase commit protocol between a transaction manager and underlying resources such as the database or messaging provider. The Java EE container is also responsible for the transaction context propagation and provides support for a distributed two-phase commit. With a distributed two-phase commit, a Java EE application can modify data across multiple application servers as if it is a single transaction. The EJB architecture only requires support for flat transactions; however, some of the vendors, such as Arjuna Technologies and Atomikos, provide support for nested transactions. (If nested transactions are not supported and a program creates a nested transaction using the javax.transaction.UserTransaction interface, the container will throw a javax.transaction.NotSupportedException.)

The Java Transaction API (JTA) and the Java Transaction Service (JTS) act as fundamental components for Java EE transaction support and transactional interoperability. The JTA API specifies an interface, UserTransaction, used by applications to delineate transaction boundaries. The Java EE transaction model is quite flexible and allows an EJB to participate in an already created transaction or never participate in a transaction depending on the application logic. The UserTransaction is utilized to start and commit and roll back transactions. JTA also defines interfaces used by an application server to communicate with a transaction manager and for a transaction manager to communicate with a resource manager. JTS represents a Java binding of the CORBA Object Transport Service (OTS) specification v1.1. Thus JTS provides transactional interoperability across servers that support the IIOP protocol. Although most of the commercial application servers, such as Sun JES Application Server, BEA's WebLogic, and IBM's WebSphere, implement the JTS API, the EJB specification only mandates JTA API support.

Note

Despite the theory, JTS/OTS cross-vendor interoperability has always been a problem in practice; this is another reason why many people are looking at Web services technology as the integrating factor.

The Java EE specification defines three types of transactional resourcesJDBC connections, JMS sessions, and resources accessed with the Connector Architecture such as Enterprise Resource Planning (ERP) systems. The Java EE container coordinates resource enlistment, the transaction commit, and delistment from the transaction by invoking the corresponding JTA XAResource API. The XAResource interface represents the industry standard XA interface based on the X/Open CAE Specification, [XASPEC]. For more details on this check out the JTA API page [SunJTA].

At the presentation tier, a transaction can be initiated by components such as Java Server Pages or Servlets that can invoke business tier components, such as Enterprise Java Beans, to participate in a transaction. In a database integration scenario, EJBs or even plain old Java objects (POJOs) connect to transactional resource managers via the JDBC connection API.

At the Business tier, EJBs or POJOs can span a new transaction connecting directly to a resource manager. The code excerpt that follows demonstrates how to demarcate a transaction in the Presentation tier using JTA. In this example the ProcessRequestServlet uses the javax.transaction.UserTransaction interface to demarcate transaction boundaries. The UserTransaction is obtained via the JNDI interface. Refer to Listing 12-1.

Listing 12-1. ProcessRequestService Snippet

public ProcessRequestServlet { ... public createPO() { Context initCtx = new InitialContext(); UserTransaction txn = (UserTransaction)initCtx.lookup("java:comp/ UserTransaction"); txn.begin(); // execute required operations txn.commit(); } ... }

Programmatic Transaction Support

Presentation tier components such as Servlets and JSPs can directly connect to the underlying resources, or they may invoke Business tier components. The transaction context is automatically propagated from the Servlet/JSP to the Business tier by the Java EE container, which encapsulates transactional services. At the Business tier the EJB transaction support is quite flexible and allows for two ways to demarcate transactionsdeclaratively Container-Managed Transaction (CMT) and programmatically Bean Managed Transaction model (BMT). For POJOs, transaction demarcation usually has to happen programmatically (although so-called pico-container frameworks like Spring (www.springframework.org) can be used to introduce CMT for POJO-based applications). For EJBs, the javax.ejb.SessionContext (EJBContext) is used to retrieve a UserTransaction. The code fragment here shows how to demarcate transactions with the BMT model. The code encapsulated between the txn.begin() and txn.commit() methods is executed within the scope of a transaction. Refer to Listing 12-2.

Listing 12-2. POHandlerEJB Implementation

public POHandlerEJB implements SessionBean { SessionContext ctx; public createPO() { try { UserTransaction txn = ctx.getUserTransaction(); txn.begin(); Connection = DataSource.getConnection(); // execute SQL operations Connection.close(); txn.commit(); } catch (SQLException exp){ // rollback the transaction txn.rollback() } } ... }

The txn.begin() call initiates a new transaction. The getConnection() method typically enlists the corresponding XAResource, such as a database, into the transaction. By enlisting the database in the transaction, the database automatically participates in the 2PC protocol. The connection.close() method typically initiates the XAResource.end() method to delist the resource from the transaction. With the txn.commit() call, if it is a 2PC protocol (i.e., if more than one resource have been accessed within the transaction), the XAResource.prepare() and XAResource.commit() are called by the transaction manager. In case of failure or at least one negative prepare, the XAResource.rollback() is called to roll back the transaction in each remaining resource. A reasonably sophisticated transaction manager will also be able to optimize for the common case where only one resource is accessed. In that case, only a one-phase commit is necessary, and the overhead of preparation can be avoided. All this logic is transparent to the Java EE application developer, as can be seen in the code sample. A transaction manager is provided as part of the Java EE application server.

The previous example demonstrates how to demarcate transactions in a programmatic manner. Java EE programmatic transaction management is useful when the application relies on a sophisticated level of transaction logic. It is often the case that Java EE applications manage transactions in a declarative manner.

Architect's Note

The decision for whether to use programmatic or declarative transaction support depends on the level of transaction control and complexity required by the application design. With the declarative transaction support, also known as Container-Managed Transaction demarcation (CMT), boundaries and individual properties of a transaction are specified in a deployment descriptor of Enterprise Java Bean. With programmatic support to a transaction, also known as Bean-Managed Transaction demarcation (BMT), application logic encapsulates transactional characteristics in the code. A Session EJB or a message-driven bean can be designed with either of two demarcation models. An Entity Bean must always be designed with container managed transaction demarcation, whereas a POJO object has to use the programmatic transaction demarcation. (However, it has to be noted that with the introduction of pico-containers and so-called "inversion of control" frameworks, CMT is now also available for POJO applications.)

Declarative Transaction Model

With the declarative model, the Java EE container provides transaction demarcation based on the EJB transaction attributes. Transaction attributes are defined in the bean deployment descriptor and direct the container on how to manage transactions of an enterprise bean method. This model is somewhat similar to the one in .NET. In general, CMT is preferred over BMT because of the following reasons:

  • CMT allows more flexibility in composing and reusing business components (whether EJB or POJO-based). Indeed, specifying transaction preferences outside of the source code (as in CMT) allows one to change or adapt these preferences without the need for recompilation.

  • The Java EE specification implies a significant limitation on the reusability of BMT components, given that BMT components essentially ignore the transaction context in which they are called.

CMT also reduces the amount of transaction-related code developers have to write, test, and maintain, which is certainly beneficial in terms of application design.

Transaction Attributes

There are six values to choose from. Based on the application design, individual methods of an enterprise bean may have different transactional attribute values. Alternatively, all methods may share the same value or, conversely, none of the methods may support transactions.

  1. NotSupported The transaction context is unspecified. This results in suspending the transaction context that was propagated by the client. The context will be resumed after the enterprise bean method completes its execution.

  2. Required The container provides a transaction context when invoking a bean's method. If a client is associated with a transaction context, the same context will be used to execute the bean's method. Otherwise, the container starts a new transaction.

  3. Supports The container will not create any new contexts unless a client is associated with a context. In that case, the context will be used to invoke the bean's method.

  4. RequiresNew The container invokes the enterprise bean's method with a new transaction context, regardless of the client's context. If a client calls with a transaction context, the container suspends its context and creates a new transaction when invoking the business method. The original transaction is resumed on the thread when the method returns.

  5. Mandatory A client must always invoke the bean's methods in a client's transaction context, or an exception will be thrown by the container.

  6. Never Clients must not call the bean's methods within a transaction context. The container will throw an exception if a transaction context is detected for a client call.

The purchase order example implemented with CMT demarcation looks similar to the previous example. There is, however, a significant difference pertaining to the lack of transaction processing code. Refer to Listing 12-3.

Listing 12-3. POHandlerEJB Implementation

public class POHandlerEJB implements SessionBean { EJBContext ejbContext; public PurchaseOrder createPurchaseOrder(Product prod, UserInfo user) { // Create PurchaseOrder based on Product and User information PurchaseOrder po = new PurchaseOrder(prod, user); // Store PurchaseOrder in the database createPO(po); return PurchaseOrder; } protected void createPO( PurchaseOrder po) { javax.sql.DataSource dataSource; java.sql.Connection connection; java.sql.Statement stmt; java.sql.SQLException exc; try { InitialContext initialContext = new InitialContext(); // Obtain a connection and SQL statement dataSource = (javax.sql.DataSource) initialContext.lookup("java:comp/env/jdbc/ProcurementDB"); connection = dataSource.getConnection(); stmt = connection.createStatement(); // Execute query stmt.executeQuery("INSERT INTO PURCHASE_ORDER(USER_ID, PRODUCT_ID) VALUES (123, 345)"; } catch (SQLException e) { ... } finally { // Release resources stmt.close(); connection.close(); } } }

All transaction processing, such as commit or rollback, is delegated to the Java EE container's transaction manager and specified via transaction attributes. The deployment descriptor corresponding to the POHandlerEJB defines the transaction RequiresNew attribute for all methods of the bean. Transaction attributes can be alternatively defined on a per-method basis. Refer to Listing 12-4.

Listing 12-4. Assembly Descriptor for POHandlerEJB

<ejb-jar> ... <assembly-descriptor> ... <container-transaction> <method> <ejb-name>POHandlerEJB</ejb-name> <method-name>*</method-name> </method> <trans-attribute>RequiresNew</trans-attribute> </container-transaction> </assembly-descriptor> </ejb-jar>

Isolation Levels

Besides the ability to delineate transactional boundaries, the Java EE transaction model provides a mechanism to specify the transaction isolation level. Isolation levels allow or disallow changes made to the data within the scope of a transaction to be visible to other concurrently executed transactions. There are five different transaction isolation levels supported by Java EE:

  1. TRANSACTION_NONE Transactions not supported.

  2. TRANSACTION_READ_UNCOMMITTED Other concurrent transactions can see uncommitted changes made within a scope of transaction (seeing uncommitted data is commonly known as dirty reads).

  3. TRANSACTION_READ_COMMITTED Changes have to be committed before any other transactions can see changes made by the transaction (this prevents dirty reads). However, data read by one transaction might be changed by a second (concurrently committed) transaction, and when the previous transaction rereads the same data, it receives different values, that is, "non-repeatable reads."

  4. TRANSACTION_REPEATABLE_READ Prohibits a transaction from reading any uncommitted changes (dirty reads) just like in the previous level. Additionally, this level prohibits non-repeatable reads from happening. However, so-called phantom reads can still occur: One transaction reads all records satisfying a predicate condition (that is, where a clause, and then a second transaction, inserts new data satisfying the same predicate condition, causing the first transaction to reread the data and see new records).

  5. TRANSACTION_SERIALIZABLE Prevents dirty reads, non-repeatable reads, and phantom reads.

Transaction isolation levels can be specified based on the type of the underlying resource. For example, for a database access, an enterprise bean implementing the BMT model can set the isolation level using java.sql.Connection interface's setTransactionIsolation (txn IsolationLevel) method . All transaction isolation levels are defined as constant by the java.sql.Connection interface.

Категории