Bean-Managed Transaction Suspension in J2EE
by Dmitri Maximovich07/20/2005
Have you ever wondered why there are six types of
Transaction demarcation attributes
(NotSupported, Required,
Supports, RequiresNew,
Mandatory, and Never) supported in beans
using
container-managed transactions (CMT), but if you're using
bean-managed transactions (BMT), the only functionality
the EJB spec provides is to begin and commit/roll back transactions
via the
UserTransaction interface? Obviously the CMT model seems
more capable--BMT lacks, for example, the ability to suspend and
resume the current transaction, which means that you can't emulate
the RequiresNew and NotSupported
demarcations in BMT beans, at least not when you're using just the
UserTransaction interface.
While the EJB specification doesn't explain why above-mentioned
asymmetry exists, there is still a legitimate way to suspend and
resume transactions in the BMT model. If you ever studied the
contents of the
javax.transaction package, you've probably noticed that
along with the UserTransaction interface there is a
TransactionManager interface that basically looks like
an "extended" UserTransaction: the same methods--begin(), commit(), and
rollback()--plus
suspend() and
resume().
If we can get a TransactionManager implementation
from within our EJB, we will be able to achieve our goal of
suspending and resuming transactions programmatically. Both the J2EE
1.3 and EJB 2.0 specifications are quiet about the availability of
TransactionManager, but neither one of them explicitly
prohibits using it. And since Container uses the Java Transaction API (JTA)
internally for CMT transaction demarcation, we can be almost 100 percent
sure that TransactionManager is present, and that it's
just a matter of obtaining a reference to it in your code.
|
Related Reading
Head First EJB |
In this article, we will see how to get a
TransactionManager in several popular containers and
show how to use it to extend functionality of bean-managed
transactions, making them as powerful as container-managed
transactions. We'll also outline some risks involved in using this
advanced functionality, and at the end, explore how
TransactionManager is used in the popular Spring framework.
Obtaining a Reference to TransactionManager in Various J2EE
Servers
The J2EE and EJB specifications don't describe any standard
means to obtain a reference to TransactionManager.
Every J2EE container vendor is free to place it anywhere, or even
to not provide any mechanism to access it from application code. In
practice, though, all modern containers have mechanisms to obtain
it. Below are examples how to get TransactionManagers
from most popular J2EE containers.
Cast a UserTransaction (WebLogic, Orion, OC4J)
Any J2EE-compatible container must make the UserTransaction object available in JNDI under
java:comp/UserTransaction. Since the
UserTransaction interface is a subset of
TransactionManager, some J2EE container vendors choose
to provide a common implementation of both of them.
WebLogic 8, Orion 2, and
Oracle's
OC4J EJB3 preview are examples of such an approach. In these
containers, a reference to TransactionManager can be
obtained just by getting a UserTransaction object from
JNDI and casting it to TransactionManager. This is
probably the simplest case.
private TransactionManager getFromUserTransaction()
throws Exception {
InitialContext ctx = new InitialContext();
UserTransaction ut = (UserTransaction)
ctx.lookup("java:comp/UserTransaction");
if (ut instanceof TransactionManager) {
log("UserTransaction also TransactionManager");
return (TransactionManager)ut;
}
return null;
}
Get TransactionManager Directly from JNDI (JBoss,
WebLogic)
In JBoss 3 and WebLogic 8,
TransactionManager is available in JNDI, albeit under
different names, and therefore can be obtained by simple
lookup:
private TransactionManager getFromJNDI()
throws Exception {
InitialContext ctx = new InitialContext();
try {
// WebLogic
return (TransactionManager)
ctx.lookup("javax.transaction.TransactionManager");
}
catch (Exception e) { }
try {
// JBoss
return (TransactionManager)
ctx.lookup("java:/TransactionManager");
}
catch (Exception e) { }
return null;
}