Transactions: Behind the Scenes

The EJB specification (downloadable from http://java.sun.com/products/ejb/index.html) starts off by listing nine goals. The second of these reads as follows:

“The Enterprise JavaBeans architecture will make it easy to write applications: Application developers will not have to understand low-level transaction and state management details, multi-threading, connection pooling, and other complex low-level APIs.”

The EJB specification largely succeeds in addressing this goal—as a developer, you really do not need much knowledge about how transactions are managed. The fact that this book only covers transactions today is testament to that.

Nevertheless, like most technical topics, it can be helpful to have an insight as to what is going on “behind the scenes.” But if you want to skip this material and make a shorter day of it, please do so. You can always re-read at a later date if you find yourself wanting to know more.

Transaction Managers, Resource Managers, and 2PC

You already know about the terms resource manager and transaction manager (or transaction coordinator). In most EJB applications, an RDBMS will take the place of a resource manager, and the EJB container itself will be the transaction manager.

This division of responsibilities is required because in an EJB architecture, the data used by Session beans or represented by Entity beans may reside in more than one persistent data store. For example, one Entity bean might map onto an Oracle RDBMS, and another Entity bean may map onto a Sybase RDBMS, as shown in Figure 8.6. (The numbers will be explained shortly.)

Figure 8.6. The J2EE platform separates resources and transaction managers.


Leaving the transaction management responsibilities with the RDBMSs is not suitable. Doing so would mean that each RDBMS would have its own transaction. If the first transaction succeeded but the second transaction failed, the logical data set represented by the Entity beans would no longer be consistent. The “C” of the ACID test would be broken.

Another case where only a single transaction is required is when interacting with JMS queues or topics. You can imagine that a queue might implement a To Do list. A task on the To Do list might be “invoice customer A for $20,” involving an update to an RDBMS. If the task is removed from the To Do list as one transaction, and the update to the RDBMS performed as another transaction, there is again the possibility that the second transaction fails. In other words, the task is removed from the To Do list, but no invoice is raised.

The two phase commit protocol (more commonly called just 2PC) is the mechanism by which the transaction manager (EJB container) interacts with each of the resource managers (RDBMS or JMS queues and topics). In the EJB environment, it works as follows (the numbers correspond to the steps in Figure 8.6):

1.
The transaction manager within the EJB container creates a new transaction as needed. Generally, this will be when a Session bean's method is invoked.

If the Session bean has been deployed under CMTD, the bean's proxy will make this request to the transaction manager. If the Session bean is deployed under BMTD, the bean itself will effectively make this request.

2.
The Session bean interacts with Entity beans. The current transaction will be propagated through to them (assuming they are deployed with Required or Mandatory value for their trans-attribute element).

3.
In turn, the Entity beans interact with the RDBMSs through an XA-compliant java.sql.Connection. “XA-compliant” means supporting the two phase commit protocol (or 2PC); more on this shortly. If the Entity bean is BMP, the interaction with the RDBMS will be done by the bean itself; if the bean is CMP, the interaction will be by the generated subclass. Either way, it amounts to the same thing.

4.
Each of the XA-compliant Connections registers itself with the EJB's transaction manager. More correctly, a javax.transaction.xa.XAResource representing and associated with the Connection is registered as part of the current javax.transaction.Transaction.

5.
When all method calls to the Entity beans have been made, the Session bean indicates to the transaction manager that the transaction should be committed.

6.
The transaction manager performs the first “prepare” phase of the commit. It iterates over each of the XAResources that constitute the transaction and requests confirmation that they are ready to be committed. In turn, the XAResource just delegates this request to its corresponding XA-compliant java.sql.Connection.

7.
When all resources have indicated that they are prepared, the transaction manager performs the second “commit” phase. It again iterates over each of the XAResources and requests them to commit.

The JTA API

An XA-compliant java.sql.Connection, as previously described, is one that provides the ability to return an XAResource for registering with the current transaction. The XA protocol is an industry standard, defined by the X/Open group, to allow a transactional resource manager to participate in a global transaction controlled by an external transaction manager. The JTA API is effectively a mapping into Java of this XA protocol.

In fact, the java.sql.Connection interface does not mandate XA-compliance, so the previous description was a slight simplification. Instead, XA-compliance is provided by classes and interfaces in the javax.sql package, part of the J2EE platform. Some of the more relevant classes of java.sql, javax.sql, javax.transaction, and javax.transaction.xa are shown in Figure 8.7.

Figure 8.7. The javax.sql and javax.transaction packages together provide support for 2PC against RDBMSs.


As you know, the java.sql.Connection interface represents a connection to an RDBMS. In J2SE applications, java.sql.DriverManager can be used to create a connection. Under J2EE, a javax.sql.DataSource object is used.

In J2EE enterprise applications, reusing connections through some sort of connection pool is critical to ensuring performance and scalability. Many implementations of DataSource provide built-in connection pooling. The connection returned by DataSource.getConnection() is effectively a logical connection temporarily associated with an underlying physical connection. When the close() method on the logical connection called, the underlying physical connection is not closed but is, instead, returned to a connection pool.

J2EE also offers another approach for RDBMS vendors to provide connection pooling, through the javax.sql.ConnectionPoolDataSource interface. This returns PooledConnections that are physical connections to the database. In turn, they provide a getConnection() method that returns a logical connection that wraps them, so the final effect is much the same as before.

Tip

If you want, you can think of the getConnection() method of PooledConnection as leasing the connection from the pool. The close() method releases the PooledConnection back into the pool to be used again.


Closely related to ConnectionPoolDataSource is the javax.sql.XADataSource, which returns javax.sql.XAConnections. This is a sub-interface of PooledConnection, so it works in the same way, providing the getConnection() to return a logical java.sql.Connection that wraps it. However, it also provides the getXAResource() method that returns a javax.transaction.xa.XAResource. Consequently, the XAConnection acts as a bridge between the resource manager's notion of connection and the transaction manager's notion of resource.

A J2EE-compliant resource manager must be able to support each of these three different DataSource interfaces (XADataSource, ConnectionPoolDataSource, and DataSource itself).

The J2EE RI deploytool can be used to configure XADataSources against Cloudscape by using the Tools, Server Configuration menu option. Alternatively, the resource.properties file under %J2EE_HOME%config can be edited directly. The dialog box and required entries are shown in Figure 8.8. (You'll also need to remove the previous definition of jdbc/Agency, listed under DataSources/Standard node of the Explorer).

Figure 8.8. deploytool can be used to configure XADataSources.


As you saw earlier today, the EJB application's interface into the EJB container's transaction manager implementation is solely through the javax.transaction.UserTransaction interface. Indeed, there is no direct way to access the javax.transaction.TransactionManager object or the javax.transaction.Transaction object that corresponds to the UserTransaction (though obviously they are available to the EJB container itself).

Tip

Some EJB containers make the Transaction and TransactionManager interfaces available—through JNDI. As ever, using these value-add features will compromise application portability.


Figure 8.9 shows an object instance diagram to indicate (some of) the objects that might be instantiated to represent the scenario shown back in Figure 8.6.

Figure 8.9. An object instance diagram showing XAConnections and XAResources.


In practice, there probably would not be two objects for the XAConnection and XAResource interfaces (oraConn/oraRes and sybConn/sybRes in the diagram). More likely, an RDBMS vendor would implement a concrete class that implements both of these two interfaces. The JTA API describes such a class as the ResourceAdapter. Consequently, each transaction managed by the EJB container's transaction manager will have a collection of ResourceAdapters, each also being a physical connection to some resource manager. Indeed, if you have used the Adapter design pattern, you'll recognize that the ResourceAdapter is well named, combining two orthogonal interfaces into a single instance.

What If It Goes Wrong?

You've seen how the 2PC protocol is intended to work. However, the whole point of 2PC is to ensure transactional consistency, even in the event of an unexpected failure. So, what happens when it goes wrong?

There are two cases to deal with. First, it could be that a resource manager enlisted into the transaction may no longer be available when the application (in an EJB context, the Session bean or its proxy) decides to commit. It could be that the network has failed since the original interaction with the Entity bean that represents some data residing on that resource manager.

In this first case, the prepare phase of the 2PC protocol fails. Because the transaction manager has been unable to get an acknowledgement within its timeout, it will roll back the transaction. When the resource manager that failed is restarted, it will (as part of its so-called recovery process) automatically roll back any work done as a part of the transaction.

In the second case, a resource manager becomes unavailable after it has acknowledged the prepare, but before the commit phase. This rare case causes more problems because the transaction manager may already have sent the commit message to some other resource managers. Nevertheless, the transaction manager will continue to send the commit message to all other resource managers.

When the resource manager that failed is restarted, it will detect that it had acknowledged a prepare. It then contacts the transaction manager to determine whether the transaction was actually committed or was rolled back. It then performs the same (commit or rollback) as part of its recovery process.

The prepare phase is more than the transaction manager checking that all resource managers are still available. It is also possible for resource managers to unilaterally decide to abort a transaction for some reason. When this happens, the transaction manager will send a rollback message to all participating resource managers, rather than a commit.

Tip

If you have done any JavaBean programming, some of this will be starting to sound familiar. The java.beans.VetoableChangeSupport class works in a very similar way.


In addition to failures of the prepare or the commit phase of the 2PC, there are also occasional cases when a resource manager may take a so-called heuristic decision that could be in conflict with the semantics of the transaction. For example, a resource manager could have a policy that, once prepared, it will commit if the transaction manager has not confirmed the outcome (commit or rollback) within a certain period. One reason that a resource manager might do this would be to free up resources.

If a heuristic decision is made, it is the responsibility of the resource manager to remember this decision. In an RDBMS, this is often stored in some sort of system table. Put bluntly, this information is required to allow the administrator to correct any issues with the data if the heuristic decision went against the transaction's actual outcome. You might have noted that the XAResource interface defines a forget() method; this allows the resource manager to finally forget that a heuristic decision was made.

This probably all sounds pretty arcane, but is needed if you want to understand the full set of status values defined by the javax.transaction.Status interface. You'll recall that a subset of these was presented in Table 8.2. Table 8.3 shows all of the constants.

Table 8.3. All of the Constants Defined in javax.transaction.Status
Constant Meaning Typical Actions
STATUS_ NO_TRANSACTION No transaction is active. tran.begin() to start new transaction.
STATUS_ACTIVE A transaction is active and can be used. Use resource manager.

tran.commit() to commit.

tran.rollback() to rollback.
STATUS_MARKED_ ROLLBACK A transaction is active, but has been marked for rollback. Any attempt to commit it will result in a javax. transaction.RollbackException being thrown. tran.rollback()
STATUS_PREPARED A transaction is active, and is in the process of being committed. The first “prepare” phase of the 2PC protocol has completed. Nothing; wait for transaction to complete.
STATUS_ PREPARING A transaction is active, and is in the process of being committed. The first “prepare” phase of the 2PC protocol is in progress.  
STATUS_ COMMITTING A transaction is active, and is in the process of being committed. The second “commit” phase of the 2PC protocol is in progress.  
STATUS_ ROLLING_BACK A transaction is active, and is in the process of being rolled back.  
STATUS_ COMMITTED The previous transaction has committed, but there is likely to be some heuristic data available (otherwise, the status returned would have been STATUS_NO_TRANSACTION). Use administrative tool to forget heuristics after checking data is valid in resource. tran.begin() to start new transaction, but heuristic data may be lost.
STATUS_ ROLLEDBACK The previous transaction has rolled back, but there is likely to be some heuristic data available (otherwise, the status returned would have been STATUS_NO_TRANSACTION).  
STATUS_UNKNOWN This is a transient status. Subsequent calls will resolve to another status. Wait.

JTA Versus JTS

There are two Java APIs relating to transactions:

  • The Java Transaction API (JTA)— (Already introduced) The JTA classes and interfaces reside in the javax.transaction and javax.transaction.xa packages.

  • The Java Transaction Services API (JTS)— This is a Java mapping for the OMG's Object Transaction Service v1.1 to interoperate with CORBA ORB/TS standard interfaces. This includes on-the-wire propagation of transactions over CORBA's IIOP network protocol. The JTS classes and interfaces reside in the javax.jts package.

For its part, the JTS specification (downloadable from http://java.sun.com/products/jts/index.html) mandates that any compliant Transaction Manager implementation must also provide complete support for the JTA API, so you can think of JTS as the “back-end” of JTA. Some Java APIs (such as JDBC and JNDI) use the terms API and SPI, where API is the application developer's programming interface, and SPI is supporting the service-provider's interface. For example, a JDBC driver written by Oracle Corp. would be an implementation of the JDBC SPI. Using this terminology, JTS is the SPI portion of JTA.

The EJB specification mandates that the EJB container must provide access to the JTA API, so some vendors will do this by implementing JTS. Such vendors can then offer transaction propagation between EJBs that reside on different EJB servers, because both such servers will effectively appear as CORBA servers implementing OTS. Figure 8.10 shows this.

Figure 8.10. JTS support means transactions can be propagated between EJB servers.


Obviously, both EJB servers need to support JTS, but they need not be implemented by the same EJB container vendor. The transaction manager on the called EJB server is enlisted as a resource of the transaction manager on the calling EJB server.

While the EJB specification mandates JTA support, it does not mandate that JTS be used to realize this support. In other words, the EJB container is free to provide any implementation of the interfaces in JTA, but it need not support the additional requirements (principally CORBA interoperability) that make up the JTS API. Some vendors—perhaps those from a database or a Web application background—may implement JTA entirely “within” their EJB container and offer no CORBA interoperability services. On the other hand, the EJB specification also indicates that if an EJB container vendor does elect to provide transactional propagation, it must do so by supporting OTS.

Caution

In fact, if the vendor elects to support transactional propagation, the EJB specification requires support for OTS v1.2 (see for example section 19.6.1.1). Strictly speaking, JTS 1.0 is a Java mapping only for OTS v1.1, so JTS 1.0 support does not in itself fully address the requirements of the EJB specification.

If the area of transaction propagation is of particular interest to you, you should make sure that your EJB container vendor can clarify its position to you.


Some EJB container vendors will also be vendors of CORBA products, and so are likely to implement JTA just by implementing JTS.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.177.39