The transaction objects maintained by the Transaction Manager do not do any actual transactional work. For example, a transaction object does not know how to commit or rollback changes. Instead, the transaction object is responsible for coordinating these actions in the data storage devices that do know how to commit and rollback. When a transaction object is committed or rolled back, the transaction object passes that request on to the data storage devices responsible for carrying out that actual work.

In order for a transaction to keep track of all the resources used during the transaction’s lifetime, those resources must be enlisted with the transaction. At the API level, resource enlistment is somewhat complicated. The resource connection to be enlisted must be able to produce an XAResource object, which is then enlisted into the Transaction object associated with the current thread.

Fortunately, resource enlistment is the job of the application server, not the application. When the application asks for a resource connection, the application server takes care of enlisting the connection with the current transaction before returning the connection to the application.

However, this means that applications must obtain resource connections in a manner that cooperates with this process. The model established by J2EE uses a combination of JNDI and resource connection factories. A connection factory is an object supplied by the application server that produces connections of the appropriate type. The interfaces for these connection factories are defined by Java standards—for example, JDBC connections are produced by javax.sql.DataSource objects, while JMS connections are produced by javax.jms.TopicConnectionFactory or javax.jms.QueueConnectionFactory objects.

These factory objects are available in Dynamo as Nucleus services. For example, the standard Dynamo DataSource object is found at /atg/dynamo/service/jdbc/JTDataSource. New resource factories can be added as needed by creating them like any other new Nucleus service.

Nucleus components should acquire resources through the proper connection factory services, rather than accessing drivers directly from their managers. This allows Dynamo applications containing both Nucleus components and standard J2EE components to interoperate.

An application can enlist multiple resources over the course of a single transaction. For example, an application might read a JMS message, then write a resulting database row through a JDBC connection. Both resources are enlisted into the same transaction, even if the resources were enlisted by the same or different components. At the end of the transaction, both resources are committed, as described below.

An application might use the same resource several times over the course of a transaction, perhaps through multiple disparate components. For example, a request might call an EJB that uses JDBC to perform a database operation, then call a second EJB that also uses JDBC. Each usage of the resource should go through the entire sequence outlined previously: use JNDI to get a pointer to the resource factory, acquire a connection from the factory, then close the connection when finished. The application should not attempt to acquire the resource once and pass it around from component to component in the interest of avoiding the code for acquiring or closing the connection.

The application server does what is necessary to make sure that the connection returned to each component refers to the same transaction. For JDBC drivers, this means that the same Connection object must be returned each time a connection is requested throughout a single transaction. (JDBC 2.0 drivers that support XA are not bound by this limitation.) The application server does this by maintaining an internal table mapping transactions to JDBC connections. When a component requests a JDBC connection, the server consults this table to see if a connection is already associated with the current transaction and if so, returns that connection. Otherwise, a new connection is checked out of the connection pool, and remains associated with the current transaction so that further requests for connections return the same Connection object.

Application components are required to close JDBC connections when they finish doing their individual portion of work. This might seem odd, especially if other components use the same connection later in the request. However, rather than actually closing the connection to the database, the application server intercepts these close requests and interprets them as signals from the application that it is done with the connection for the time being. The application server then responds to that signal by returning the connection to a pool, or by maintaining the connection’s transactional association.

This means that each individual component should be written as if it were the only component in the request that needs to access the database. The component should also be written without regard for how the connection is being managed. The same code should be used regardless of whether connections are being pooled or not, or whether XA connections are supported or not. These are all permutations that the application server supports—the application does not need to consider any of this in its compiled code.

loading table of contents...