New York
University
Computer
Science Department
Courant
Institute of Mathematical Sciences
JTS –
Demarcated Transactions Support
Course Title: Application Servers Course
Number: g22.3033-011
Instructor: Jean-Claude Franchitti Session: 5
EJB servers
are transactional servers that allow developers to concentrate on business
logic. The EJB model implements two-phase commits, transaction context
propagation and distributed transaction, although it's up to the vendors to
decide which technique to use.
A transaction is formally defined as an
"ACID" (atomic, consistent, isolated, durable) unit of work. Atomic
transactions are "all or nothing." They either work or they don't –
they're never left incomplete. Consistent transactions always leave
the system in a consistent state. Isolated transactions execute in a
safe manner – they won't fail if other transactions running on the same server
are failing. Durable transactions can survive system failures once they're
completed and committed.
For example, an online sale may involve the
following steps:
1.
Begin transaction. Charge card sale amount.
Update sale database. Update shipping database.
2. Commit transaction.
The transaction can end in one of two ways: (1) in a
commit and everything is saved; or (2) if any step within the transaction
fails, the effects of all preceding steps are rolled back or undone. For
example, if the shipping database can't be updated, the charge isn't made and
the sale database isn't updated.
In a distributed environment handling transactions
involves coordinating the various databases that participate in the
transaction. In the EJB framework the bean developer can simply define the
transaction policy for the bean during the deployment process, using
declarative statements, and let the container handle all distributed
transactions (called bean-demarcated
transactions). Alternatively, the developer can take explicit control of
transactions (called client-demarcated
transactions).
Transactional
Scope and Attributes
Transactional scope is an important part of
understanding how transactions work. In EJBs the scope of a transaction
includes every bean that participates in a unit of work - the bean method. The
scope of a transaction can be traced by looking at the thread of execution. The
transaction is propagated to a bean when that bean is invoked and included in
the scope of the transaction. Of course, the thread of execution is not the
only determining factor in transactional propagation; transactional attributes
is the other.
An enterprise bean can take one of the following six
attributes in the deployment descriptor, and the container manages the
transactions according to the specified attribute. Transaction attributes can
be specified for the entire bean or the bean can be fine-tuned by specifying
the attributes for individual methods.
1. TX_NOT_SUPPORTED (see Figure 1): This tells the container to
invoke bean methods without a transaction context. If a client invokes a bean
method from within a transaction context, the container suspends the
association between the transaction and the current thread before invoking the
method on the enterprise bean instance. The container then resumes the
suspended association when the method invocation returns. The suspended
transaction context isn't passed to any enterprise bean objects or resources
that are used by this bean method.
2.
TX_SUPPORTS (see Figure 2): This tells the container to include the
bean or
method within the transaction scope in which it is invoked. If a method is part
of a transactional scope and it invokes any bean with this attribute, the
invoked bean and everything it accesses become a part of the transaction.
If the client invokes the bean method without a transaction context, the
container invokes the bean method without a transaction context.
3. TX_REQUIRED (see Figure 3): This tells the container
that the bean method must be invoked within a transaction scope. If a client
invokes a bean method from within a transaction context, the container invokes
the bean method within the client transaction context. If a client invokes a
bean method without a transaction context, the container creates a new
transaction context for the invoked bean. The transaction context is then
passed to any beans that are used by this bean method.
4. TX_REQUIRES_NEW (see Figure 4): This tells the container to
always invoke the bean method within a new transaction context, regardless of
whether the client invokes the method within or without a transaction context.
The transaction context is passed to any enterprise bean objects or resources
that are used by this bean method.
5. TX_MANDATORY (see Figure 5): This directs the container
to always invoke the bean method within the transaction context associated with
the client. The difference between this and the TX_REQUIRED attribute is that
if the client attempts to invoke the bean method without a transaction context,
the container throws the javax.jts.TransactionRequiredException exception. The
transaction context is passed to any beans that are used by the invoked bean
method.
6. TX_BEAN_MANAGED (see Figure 6): This tells the container
that the bean class doesn't have its transactional context managed by the
server but it uses JTA, more specifically the javax.jts.UserTransaction, to explicitly
manage transaction boundaries.
Using this attribute, however, imposes a restriction
that the attributes of different methods cannot be mixed; if even one method
has this attribute, then all methods must manage transaction on their own.
Making a bean transactional is expensive at runtime;
since it participates in a transaction and conforms to ACID rules, its services
can't be shared during the life of a transaction. Declaring a bean to be
TX_NOT_SUPPORTED improves performance and may be desirable for EJBs that
provide stateless service as they need to conform to the ACID rules.
FIGURE 1
Transaction propagation for TX_NOT_SUPPORTED
FIGURE 2 Transaction propagation for
TX_SUPPORTS
FIGURE 5
Transaction propagation for TX_MANDATORY
Transaction Isolation Levels
The transaction isolation level determines how
isolated one transaction is from another for read purposes only. These
isolation levels are defined in terms of three phenomena (defined in the
ANSI/ISO SQL standard -SQL92) that must be prevented between concurrently
executing transactions. Dirty reads:
A transaction reads data written by another transaction that hasn't been
committed yet. In other words, a transaction reads a database row containing
uncommitted changes from a second transaction. Nonrepeatable reads: A transaction rereads data it has previously
read and finds that another committed transaction has modified or deleted the
data. In other words, one transaction reads a row in a table, a second
transaction changes the same row and the first transaction rereads the row and
gets a different value. Phantom reads:
A transaction reexecutes a query, returning a set of rows that satisfies a
search condition, and finds that another committed transaction has inserted
additional rows that satisfy the condition. In other words, one transaction
reads all rows that satisfy a SQL WHERE condition and a second transaction
inserts a row that also satisfies the WHERE condition. The first transaction
applies the same WHERE condition and gets the row inserted by the second
transaction.
Isolation levels aren't new to EJBs; EJB defines
these levels based on the ANSI-SQL92 standards. They're mapped in JDBC to the
static variables defined in the java.sql.Connection interface. Isolation level,
like attributes, can be fine-tuned by specifying them at the method level for
EJBs; however, all methods invoked in the same transaction must have the same
isolation level.
1.
TRANSACTION_READ_UNCOMMITTED: The transaction can read uncommitted data
(data changed by another transaction still in progress).
2.
TRANSACTION_READ_COMMITTED: The transaction can't read uncommitted data.
3.
TRANSACTION_REPEATABLE_READ: The transaction can't change data that's
being read by another transaction. Methods with this isolation level, besides
having the same behavior as TRANSACTION_READ_COMMITTED, can only execute
repeatable reads.
4.
TRANSACTION_SERIALIZABLE: The transaction has exclusive read and update
privileges to data by locking it; other transactions can neither write nor read
the same data. (This does to transaction what the synchronized keyword does to
methods.) It is the most restrictive transaction.
Make no mistake –
although the TX_SERIALIZABLE attribute guarantees the highest level of data
integrity, it is offset by a performance slag because even simple reads must
wait in line. EJBs that need to handle a large number of concurrent
transactions should avoid this level. By understanding the level of reads that
will occur on the database and how the database handles locking and choosing
the correct isolation level, the EJB can be fine-tuned to peak performance (see
Table 1).
Client-Demarcated
Transactions
JTS is a specification based on the CORBA OTS 1.1
for implementing a Java transaction manager that serves as an intermediary
between an application and one or more transaction-capable resource managers,
such as database servers and messaging systems. The JTS specification includes
the JTA API that is used by application programmers to group operations into
one or more logical transactions. The Java mapping of the OMG OTS 1.1
specification is specified in two packages: org.omg.CosTransactions and
org.omg.CosTSPortability. JTA actually provides three types of services:
·
Transactional
operations in client applications
·
Transactional
operations in application servers performed on behalf of clients
·
Global
transactional management in a Java transaction manager, coordinating multiple
transaction-capable resource managers such as database servers and messaging
systems
EJBs use the high-level transaction manager
interface provided by JTA, while the EJB server uses the JTA high-level
transaction manager interface and a standard Java mapping of the X/Open XA
protocol to handle transactions (javax.transaction.xa package).
EJBs use the simple
javax.transaction.UserTransaction interface to communicate with the transaction
manager and control transaction boundaries programmatically. (The EJB
specification doesn't stipulate any specific transaction service or protocol
but requires that the javax.transaction.UserTransaction interface of the JTS be
exposed to enterprise beans.) Important methods in UserTransaction interface
are:
·
public
void begin()
·
public
void commit()
·
public
int getStatus()
·
public
void rollback()
·
public
void setRollbackOnly()
·
public
void setTransactionTimeout()
The UserTransaction.begin() method starts a global
transaction and associates a javax.transaction.Transaction with the execution
thread. It throws the NotSupportedException when the calling thread is already
associated with a transaction and the transaction manager implementation
doesn't support nested transactions.
The UserTransaction.commit() method completes the
transaction. If at this point the transaction needs to be rolled back instead
of being committed, the transaction manager does so and throws a RollbackException
to indicate it.
The UserTransaction.rollback() method undoes any
changes made since the start of the transaction and removes the association
between the Transaction and the execution thread.
Getting
Transaction Access
As
mentioned earlier, the ability of the bean to access the transaction service
can't be selectively applied to only certain methods of the bean. All methods
must have the TX_BEAN_MANAGED attribute. That said, bean methods can get access
to the transaction service through the getUserTransaction() method of the
javax.ejb.EJBContext interface (the superinterface for SessionContext and
EntityContext).
public
void doTransaction(String customerName, String password,int age) throws
MyException{
try{
UserTransaction trans=sessionctx. ctx.getUserTransaction().begin();
Context ctx = new InitialContext();
TranstestHome home = (TranstestHome)
ctx.lookup("ejb.TranstestHome");
Transtest
bean = home.create();
bean.putUser("Sameer","word");
bean.putAge("Sameer",10);
trans.commit();
}catch(Exception e){
trans.rollback();
throw new MyException("an exception occurred" +e);
}
}
This UserTransaction also defines two methods:
setRollbackOnly() and getRollbackOnly(). The first method allows the bean to
veto a transaction explicitly. Once invoked, the transaction can't be committed
by anyone, including the container. The second method remains true if the
transaction has been so marked and can be used to avoid further unnecessary
work in the method.
JTA allows the UserTransaction object to be exposed
via JNDI. EJBs shouldn't use this approach as it compromises the
"middleware portability"; that is, other EJB servers might not
support that approach.
Context
ctx = new InitialContext();
UserTransaction utx = (UserTransaction)ctx.lookup("ajndiname");
utx.begin();
// do work
utx.commit();
With entity beans and stateless session beans, a
transaction managed with the UserTransaction must start and end in the same
method. The reason is that entity and stateless session bean instances are
shared across many clients by instance pooling and instance swapping on the
server.
Stateful session beans allow the UserTransaction
object to span multiple method calls because there's always one instance
associated with a client, and it maintains conversational state. This bean
state (and state of the transaction) is consistent even when the container
makes it undergo an internal activation-passivation cycle to conserve server
resources.
public
class MyStatefulBean implements SessionBean {
public SessionContext ctx;
public void setSessionContext(SessionContext ctx){
this.ctx=ctx;
}
public void method1(){
ctx.getUserTransaction().begin();
// do some work
}
public void method2(){
// do some more work
}
public void method3(){
// do yet some more work
// and finally commit
ctx.getUserTransaction().commit();
}
Repeated calls to getUserTransaction() in a stateful
session bean return a reference to the same UserTransaction object. Its state
can be checked using a UserTransaction.getStatus() call.
Stateful session beans involve conversational state
between method calls. Sometimes it may be desirable to cache this transactional
state and postpone database updates. The javax.ejb.SessionSynchronization
allows the server to inform a stateful session bean of the various stages in
the transaction by invoking callback methods such as: afterBegin(): Notifies the bean that a new transaction has started
– called before the EJB delegates the business methods to the instance afterCompletion(): Notifies the bean
that the transaction has completed – can be used to reset instance variables
since it will always be invoked beforeCompletion():
Notifies the bean that the transaction is about to be committed
Client-demarcated transactions in stateful session
beans across methods should be avoided since they tend to be overtly complex
and any improper method invocation locks up resources. Once a stateful session
bean is a part of a transaction, there's no way for it to be accessed by any
other transaction context (e.g., a method with TX_REQUIRES_NEW is invoked) and
the bean can't be removed. In the example above, resource locking will happen
when the client doesn't invoke method3().
Exception
Handling and Transactions
What happens when exceptions occur depends on the type of exception (checked or
unchecked), isolation level and the transactional attribute of the bean method.
The first rule, which may sound strange, is that any exception thrown outside
the transaction scope causes the transaction to roll back.
Consider an example of a client invoking a method called doTransaction() on a
stateless session bean called MiddleBean. The bean internally then invokes
methods on another stateless session bean called TranstestBean. The required
code for these beans and the client can be seen in Listings 1 to 4. The
sequence of events that occur as a result of the client call is shown in Figure
7.
The two beans are
deployed with the transaction attributes shown in Table 2 and what happens as a
result of these attributes is summarized in Figure 8.
Figure 8 The
three scenarios that occur when a deliberately planted exception is thrown
In the first case the transaction context is
propagated to the TranstestBean bean.
When the exception is thrown in the second bean, it
falls within the transaction context; it propagates up and the container traps
it in the first bean and rolls back the transaction.
In case two the second bean doesn't participate in
the transaction and the transaction context is not propagated to it. The
exception thrown falls outside the transaction context; the container detects
this and rolls back the transaction. There is no way, however, to undo any
changes made in the second bean.
In the third case the second bean has a new
transaction context for each of the methods. The transaction for the
exception-throwing method is rolled back, the exception moves up and the
container rolls back the initial transaction. The other method executes
successfully in its own transaction.
In general, methods should be logically atomic – all
or nothing. If an exception is intended to indicate that the method can't
complete successfully, it shouldn't be caught. If it is caught, the method
should try to correct the problem and continue. The method must throw the
exception and propagate out of the method for the transaction to be rolled
back.
Application exceptions won't cause a rollback if
they're thrown and caught within the transactional scope. Runtime exceptions or
unchecked exceptions, on the other hand, always cause a transaction to roll
back, regardless of the transaction attribute or transactional scope.
Unilateral
Decisions
The transaction manager allows certain heuristic or
speculative decisions to be made based on the state of all participating
resources in a transaction and the underlying two-phase commit protocol. A
heuristic decision occurs when one of the resources in the transaction
unilaterally decides to commit or roll back the transaction without permission
from the transaction manager. This breaks the atomicity of the transaction and
is captured by the manager in the following exceptions: javax.transaction.HeuristicCommitException is thrown when a
rollback is requested but a heuristic decision was made and all updates were
committed. javax.transaction.HeuristicMixedException
is thrown when a heuristic decision was made and some updates have been
committed and others were rolled back. javax.transaction.HeuristicRollbackException
is thrown when a commit is requested but a heuristic decision was made and all
relevant updates were rolled back.
References
1. A complete definition of two-phase commits by SEI: www.sei.cmu.edu/activities/str/descriptions/dtpc_body.html
2. Datamation Magazine. "What a two-phase commit is and how it
works": www.datamation.com/datab/twophase.html
3. SQL 92 standards: www.ansi.org
4. Java Transaction API: http://java.sun.com/jta
5. EJB home page at SUN: http://java.sun.com/products/ejb
6. The Oracle technical network: http://technet.oracle.com/
7. The org.omg.CosTSPortability and other similar packages: http://java.sun.com/products/jts/javadoc/org/omg/CosTransactions/package-summary.html