In Part 1 of this series, we examined transactions and explored their basic properties -- atomicity, consistency, isolation, and durability. Transactions are the basic building blocks of enterprise applications; without them, it would be nearly impossible to build fault-tolerant enterprise applications. Fortunately, the Java Transaction Service (JTS) and the J2EE container do much of the work of managing transactions for you automatically, so you don't have to integrate transaction awareness directly into your component code. The result is almost a kind of magic -- by following a few simple rules, a J2EE application can automatically gain transactional semantics with little or no additional component code. This article aims to demystify some of this magic by showing how and where the transaction management occurs.
JTS is a component transaction monitor. What does that mean? In Part 1, we introduced the concept of a transaction processing monitor (TPM), a program that coordinates the execution of distributed transactions on behalf of an application. TPMs have been around for almost as long as databases; IBM first developed CICS, which is still used today, in the late 1960s. Classic (or procedural) TPMs manage transactions defined procedurally as sequences of operations on transactional resources (such as databases). With the advent of distributed object protocols, such as CORBA, DCOM, and RMI, a more object-oriented view of transactions became desirable. Imparting transactional semantics to object-oriented components required an extension of the TPM model, in which transactions are instead defined in terms of invoking methods on transactional objects. JTS is just that: a component transaction monitor (sometimes called an object transaction monitor), or CTM.
The design of JTS and J2EE's transaction support was heavily influenced by the CORBA Object Transaction Service (OTS). In fact, JTS implements OTS and acts as an interface between the Java Transaction API, a low-level API for defining transaction boundaries, and OTS. Using OTS instead of inventing a new object transaction protocol builds upon existing standards and opens the way for compatibility between J2EE and CORBA components.
At first glance, the transition from procedural transaction
monitors to CTMs seems to be only a change in terminology. However,
the difference is more significant. When a transaction in a CTM
commits or rolls back, all the changes made by the objects involved in
the transaction are either committed or undone as a group. But how
does a CTM know what the objects did during that transaction?
Transactional components like EJB components don't have commit() or rollback()
methods, nor do they register what they've done with the transaction
monitor. So how do the actions performed by J2EE components become
part of the transaction?
Transparent resource enlistment
While the application state is manipulated by components, it is still stored in transactional resource managers (for example, databases and message queue servers), which can be registered as resource managers in a distributed transaction. In Part 1, we talked about how multiple resource managers can be enlisted in a single transaction, coordinated by a transaction manager. Resource managers know how to associate changes in application state with specific transactions.
But this just moves the focus of our question from the component to the resource manager -- how does the container figure out what resources are involved in the transaction so it can enlist them? Consider the following code, which might be found in a typical EJB session bean:
Listing 1. Transparent resource enlistment with bean-managed transactions
InitialContext ic = new InitialContext();
UserTransaction ut = ejbContext.getUserTransaction();
ut.begin();
DataSource db1 = (DataSource) ic.lookup("java:comp/env/OrdersDB");
DataSource db2 = (DataSource) ic.lookup("java:comp/env/InventoryDB");
Connection con1 = db1.getConnection();
Connection con2 = db2.getConnection();
// perform updates to OrdersDB using connection con1
// perform updates to InventoryDB using connection con2
ut.commit();
|
Notice that there is no code in this example to enlist the JDBC connections in the current transaction -- the container does this for us. Let's look at how this happens.
Three types of resource managers
When an EJB component wants to access a database, a message queue server, or some other transactional resource, it acquires a connection to the resource manager (usually by using JNDI). Moreover, the J2EE specification only recognizes three types of transactional resources -- JDBC databases, JMS message queue servers, and "other transactional services accessed through JCA." Services in the latter class (such as ERP systems) must be accessed through JCA (the J2EE Connector Architecture). For each of these types of resources, either the container or the provider helps to enlist the resource into the transaction.
In Listing 1, con1 and con2
appear to be ordinary JDBC connections such as those that would be
returned from DriverManager.getConnection(). We get these
connections from a JDBC DataSource, which
was obtained by looking up the name of the data source in JNDI. The
name used in our EJB component to find the data source (java:comp/env/OrdersDB) is specific to the
component; the resource-ref section of the
component's deployment descriptor maps it to the JNDI name of some
application-wide DataSource managed by the
container.
Every J2EE container can create transaction-aware
pooled DataSource objects, but the J2EE
specification doesn't show you how, because it's outside the spec. If
you browse the J2EE documentation, you won't find anything on how to
create JDBC data sources. You'll have to look in the documentation
for your container instead. Depending on your container, creating a
data source might involve adding a data source definition to a
property or configuration file, or might be done through a GUI
administration tool.
Each container (or connection pool manager, like PoolMan) provides
its own mechanism for creating a DataSource, and it is in
this mechanism that the JTA magic is hidden. The connection pool
manager obtains a Connection from the specified JDBC
driver, but before returning it to the application, wraps it with a
facade that also implements Connection, interposing
itself between the application and the underlying connection. When
the connection is created or a JDBC operation is performed, the
wrapper asks the transaction manager if the current thread is
executing in the context of a transaction, and automatically enlists
the Connection in the transaction if one exists.
The other types of transactional resources, JMS message queues and JCA connectors, rely on a similar mechanism to hide resource enlistment from the user. When you make a JMS queue available to a J2EE application at deployment time, you again use a provider-specific mechanism to create the managed JMS objects (queue connection factories and destinations), which you then publish in a JNDI namespace. The managed objects created by the provider contain similar auto-enlistment code as the JDBC wrapper added by the container-supplied connection pool manager.
Transparent transaction control
The two types of J2EE transactions -- container-managed and bean-managed -- differ in how they start and end
a transaction. Where a transaction starts and ends is referred to as
transaction demarcation. The example code in Listing 1
demonstrates a bean-managed transaction (sometimes called a
programmatic transaction.) Bean-managed transactions are
started and ended explicitly by a component using the UserTransaction class. UserTransaction is made available to EJB
components through the ejbContext and to other J2EE
components through JNDI.
Container-managed transactions (or declarative transactions)
are started and ended transparently on behalf of the application by
the container, based on transaction attributes in the component's
deployment descriptor. You indicate whether an EJB component uses
bean-managed or container-managed transactional support by setting the
transaction-type attribute to either
Container or Bean.
With container-managed transactions, you can assign transactional
attributes at either the EJB class or method levels; you can specify a
default set of transactional attributes for the EJB class, and you can
also specify attributes for each method if different methods are to
have different transactional semantics. These transactional
attributes are specified in the
container-transaction section of the assembly descriptor.
An example assembly descriptor is shown in Listing 2. The supported
values for the trans-attribute are:
-
Supports -
Required -
RequiresNew -
Mandatory -
NotSupported -
Never
The trans-attribute determines if the method supports
execution within a transaction, what action the container should take
when the method is called within a transaction, and what action the
container should take if it is called outside of a transaction. The
most common container-managed transaction attribute is Required. When Required is set, a transaction in process will
enlist your bean in that transaction, but if no transaction is
running, the container will start one for you. We will investigate
the differences between the various transaction attributes, and when
you might want to use each, in Part 3 of this series.
Listing 2. Sample EJB assembly descriptor
<assembly-descriptor>
...
<container-transaction>
<method>
<ejb-name>MyBean</ejb-name>
<method-name>*</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
<container-transaction>
<method>
<ejb-name>MyBean</ejb-name>
<method-name>updateName</method-name>
</method>
<trans-attribute>RequiresNew</trans-attribute>
</container-transaction>
...
</assembly-descriptor>
|
Unlike the example in Listing 1, with declarative transaction demarcation there is no transaction management code in the component methods. Not only does this make the resulting component code easier to read (because it is not cluttered with transaction management code), but it has another, more significant advantage -- the transactional semantics of the component can be changed at application assembly time, without modifying or even accessing the source code for the component.
While being able to specify transaction demarcation separate from the code is a very powerful feature, making poor decisions at assembly time can render your application unstable or seriously impair its performance. The responsibility for correctly demarcating container-managed transactions is shared between the component developer and the application assembler. The component developer needs to provide sufficient documentation as to what the component does, so that the application deployer can make intelligent decisions on how to structure the application's transactions. The application assembler needs to understand how the components in the application interact, so that transactions can be demarcated in a way that enforces application consistency and doesn't impair performance. We'll discuss these issues in Part 3 of this series.
Transparent transaction propagation
In either type of transaction, resource enlistment is transparent; the container automatically enlists any transactional resources used during the course of the transaction into the current transaction. This process extends not only to resources used by the transactional method, such as the database connections acquired in Listing 1, but also by methods it calls -- even remote methods. Let's take a look at how this happens.
The container associates transactions with threads
Let's say that methodA() of object A
starts a transaction, and then calls methodB() of object
B, which acquires a JDBC connection and
updates the database. The connection acquired by B will be automatically enlisted in the
transaction created by A. How did the
container know to do this?
When a transaction is initiated, the transaction context is
associated with the executing thread. When
A creates the transaction, the thread in
which A is executing is associated with
that transaction. Because local method invocations execute in the
same thread as the caller, any methods called by A will also be in the context of that
transaction.
What if object B is really
a stub to an EJB component executing in another thread or even another JVM?
Amazingly, resources accessed by remote object B will still be enlisted in the current
transaction. The EJB object stub (the part that executes in the
context of the caller), the EJB protocol (RMI over IIOP), and the
skeleton object on the remote end all conspire to make this happen
transparently. The stub determines if the caller is executing a
transaction. If so, the transaction ID, or Xid, is propagated
to the remote object as part of the IIOP call along with the method
parameters. (IIOP is the CORBA remote-invocation protocol, which
provides for propagating various elements of execution context, such
as transaction context and security context; see Resources for more information on RMI over
IIOP.) If the call is part of a transaction, the skeleton object on
the remote system automatically sets the remote thread's transaction
context, so that when the actual remote method is invoked, it is
already part of the transaction. (The stub and skeleton objects also
take care of beginning and committing container-managed transactions.)
Transactions can be initiated by any J2EE component -- an EJB component, a servlet, or a JSP page (or an application client, if the container supports it). This means that your application can start a transaction in a servlet or JSP page when a request arrives, do some processing within the servlet or JSP page, access entity beans and session beans on multiple servers as part of the page's logic, and have all of this work be part of one transaction, transparently. Figure 1 demonstrates how the transaction context follows the path of execution from servlet to EJB to EJB.
Figure 1. Multiple components in a single transaction

Having transactions be managed by the container allows the container to make certain optimization decisions
for us. In Figure 1, we see a servlet and multiple EJB components access a
database within the context of a single transaction. Each obtains a
Connection to the database; it may well be the case that
each is accessing the exact same database. JTS can detect whether
multiple resources are involved in the transaction or not, even if
multiple connections are made to the same resource from different
components, and optimize the execution of the transaction. As you'll
recall from Part 1, involving multiple resources managers in a single
transaction requires the use of the two-phase commit protocol, which
is more expensive than the single-phase commit used by a single
resource manager. JTS is able to determine if only a single resource
manager is enlisted in a transaction. If it detects that all the
resources involved in the transaction are the same, it can skip the
two-phase commit and let the resource manager handle the transaction
by itself.
The magic that allows for transparent transaction control, resource enlistment, and transaction propagation is not a part of JTS, but instead a part of how J2EE containers use JTA and JTS services behind the scenes on behalf of J2EE applications. There are many entities that conspire behind the scenes to make this magic happen transparently; the EJB stubs and skeletons, the JDBC driver wrappers provided by the container vendor, the JDBC drivers provided by the database vendor, the JMS providers, and the JCA connectors. All of these entities interact with the transaction manager so that your application code doesn't have to.
In Part 3, we'll look at some of the practical issues associated with managing transactions in a J2EE context -- transaction demarcation and isolation -- and their effects on application consistency, stability, and performance.
- The Java theory and practice
column archive includes Part 1 of this series: "An introduction to transactions."
-
Transaction
Processing: Concepts and Techniques
by Jim Grey and Andreas Reuter is the definitive work on the subject of transaction processing.
-
Principles of Transaction Processing
by Philip Bernstein and Eric Newcomer is a fine introduction to the subject; it covers a lot of history as well
as concepts.
- The Java Transaction Service specification is quite readable and offers a high-level explanation of how an object transaction monitor fits into distributed applications.
- JTS was built on top of the existing CORBA OTS specification.
- The lower-level details of transactional support in J2EE is detailed in the Java Transaction API specification.
- The J2EE specification describes how JTS and JTA fit into J2EE and how transactions interact with other J2EE technologies, such as Enterprise JavaBeans technology.
- Interested in building J2EE applications using IBM tools? Check out the IBM Redbook
Programming J2EE APIs with WebSphere Advanced Edition
.
- "Optimistic locking pattern for EJBs" explores the practice of optimistic locking, which can make transaction processing more efficient.
- The IBM Redbook
Building Enterprise Web Transactions using VisualAge Generator JavaBeans and JSPs
explores automated generation of JSP pages which can access enterprise data transactionally.
- The IBM Redbook
Enterprise JavaBeans Development Using VisualAge for Java
describes IBM's rapid application development support for EJB applications.
- "Transaction management under J2EE 1.2" by Sanjay Mahapatra covers the basics of declarative transaction demarcation.
- Find other Java technology resources on the developerWorks
Java technology zone.
Brian Goetz is a software consultant and has been a professional software developer for the past 15 years. He is a Principal Consultant at Quiotix, a software development and consulting firm located in Los Altos, CA. See Brian's published and upcoming articles in popular industry publications.




