The previous two parts of this series discussed the fundamentals of transaction processing in a distributed, component-based environment, as well as the way explicit transaction management is done for the most common J2EE configurations. This part continues the discussion by describing declarative transaction management of session, entity, and message-driven beans. I also describe guidelines for implicit or explicit global transaction management. Finally, local transaction management is described along with common pitfalls and their solutions.
The EJB container is responsible for managing transaction boundaries in the case of declarative transaction management. The way the container manages transactions for a method in an EJB is determined by its transaction attribute. The benefits of using container-managed transactions are listed below.
The ability to specify transactional behavior of an enterprise bean frees the application component provider from writing transaction specification logic in the code.
Since the container automatically handles transaction demarcation at run time, it's more reliable.
Since transactions are viewed as a property of the overall application, it's easier an application assembler who understands the application to customize the transaction attributes in the deployment descriptor to change the application behavior.
Even in container-managed demarcation, an EJB has some control over
the transaction. For example, an enterprise bean can choose to roll
back a transaction started by the container using the method
setRollbackOnly on the SessionContext or
EntityContext object.
A transaction attribute is a value associated with a method of a
session, or an entity bean's remote or home interface, or with the
onMessage method of a message-driven bean. It specifies
how the container must manage transactions for a method when a client
invokes it via the home or remote interface or when the method gets
invoked as the result of the arrival of a JMS message. In the majority
of cases, all methods of an enterprise bean will have the same
transaction attribute. However, it is possible to have different
attributes for different methods in the same EJB for special purposes,
like when an EJB may have methods that don't need to be
transactional.
The transaction attribute must be specified for the following methods:
javax.ejb.EJBObject
interface. Transaction attributes must not be specified for the
methods of a session bean's home interface.getEJBHome, getHandl,
getPrimaryKey, and isIdentical methods; and
for the methods defined in the bean's home interface and all the
direct and indirect superinterfaces of the home interface, excluding
the getEJBMetaData and getHomeHandle
methods.onMessage method.The description for each type of transaction attribute follows. In the explanations of the different attributes, calling client and parent are meant to be the same as is the called method of the server EJB and child.
The container always ensures that the method of the EJB is invoked with a JTA transaction. If the calling client is associated with a JTA transaction, the enterprise bean method will be invoked in the same transaction context. However, if a client is not associated with a transaction, the container will automatically begin a new transaction and try to commit the transaction when the method completes.
|
The container invokes a message driven bean method with its
transaction attribute set to Required with a valid
transaction context. Since there is no client transaction context
available for a message-driven bean, the container automatically
starts a new transaction before the dequeuing of the JMS message which
is before the invocation of the onMessage method. The
container automatically enlists the resource manager associated with
the arriving message and all the resource managers accessed by the
onMessage method with the transaction. If the
onMessage method invokes other enterprise beans, the
container propagates the transaction context with the
invocation. Finally, it attempts to commit the transaction when the
onMessage method has completed. In case the
onMessage method does not successfully complete, the
transaction is rolled back by the container and typical JMS message
redelivery semantics apply.
This is the only scenario in which the interface to the JMS topic
or queue is involved in the EJB transaction. For EJBs with the
Required attribute, the JMS server will be enlisted as an
XAResource. If the JMS server provider does not support XA
transactions, the commit or rollback on the JMS session will typically
be synchronized with the result of the container's global transaction.
In this case, the container will not be able to involve the JMS server
in the 2PC process used in managing distributed transactions. A
rollback on the JMS session will alert the JMS server that the message
should be delivered.
The container must handle the
EJBContext.setRollbackOnly() method invoked from a
onMessage method executing with the Required
transaction attribute by making sure that the transaction will never
commit. Typically the transaction manager is instructed by the
container to mark the transaction for rollback. When the method
invocation completes, the container must roll back rather than commit
the transaction. The Container must throw and log the
java.lang.IllegalStateException if the
EJBContext.setRollbackOnly() method is invoked from an
onMessage method executing with the
NotSupported transaction attribute
The container must handle the
EJBContext.getRollbackOnly() method invoked from an
onMessage method by throwing and logging the
java.lang.IllegalStateException if the
EJBContext.getRollbackOnly() method is invoked from an
onMessage method executing with the
NotSupported transaction attribute
If an instance of a message-driven bean with container-managed
transaction demarcation attempts to invoke the
getUserTransaction() method of the
EJBContext interface, the container must throw and log
the java.lang.IllegalStateException.
|
The container always creates a new transaction before invoking the enterprise bean method and commits the transactions when the method returns. If the calling client is not associated with a transaction context, container will automatically begin a new transaction and try to commit the transaction when the method completes. If the calling client is associated with a transaction context, the container suspends the association of the transaction context with the current thread before starting the new transaction. When the method and the transaction complete, the container resumes the suspended transaction.
|
|
If the transaction attribute is NotSupported, the transactional context of the calling client is not propagated to the enterprise bean. If a client calls with a transaction context, the container suspends the client's transaction association before invoking the enterprise bean's method. After the method completes, the container resumes the suspended transaction association.
The container invokes a message-driven bean method with its
transaction attribute set to NotSupported with an
unspecified transaction context. If the onMessage method
invokes other enterprise beans, the Container passes no transaction
context with the invocation.
|
It the transaction attribute is Supports, and the
client is associated with a transaction context, the context is
propagated to the enterprise bean method, like the way the container
treats the Required case. If the client call is not
associated with any transaction context, the container behaves
similarly to the NotSupported case. The transaction
context is not propagated to the enterprise bean method.
|
The transaction attribute Mandatory requires the container to
invoke a bean's method in a client's transaction context. If the
client is not associated with a transaction context when calling this
method, the container throws
javax.transaction.TransactionRequiredException. If the
calling client has a transaction context, the case is treated as
Required by the container.
|
The transaction attribute Never requires that the
enterprise bean method not be called within a transaction context. If
the client calls with a transaction context, the container throws the
java.rmi.RemoteException. If the client is not associated
with any transaction context, the container invokes the method without
initiating a transaction.
If an enterprise bean implements the
javax.ejb.SessionSynchronization interface, only the
following values for the transaction attributes of the bean's methods
can be specified: Required, RequiresNew,, or
Mandatory.
This restriction is necessary to ensure that the enterprise bean is
invoked only in a transaction. If the bean were invoked without a
transaction, the container would not be able to send the transaction
synchronization calls. The tools used by the application assembler can
determine if the bean implements the
javax.ejb.SessionSynchronization interface, for example,
by using the Java reflection API on the enterprise bean's class.
The enterprise bean's business methods or onMessage
method must not use any resource manager specific transaction
management methods that would interfere with the container's
demarcation of transaction boundaries. For example, the enterprise
bean methods must not use the following methods of the
java.sql.Connection interface: commit(),
setAutoCommit(), and rollback(). It must not
use the following methods of the javax.jms.Session
interface: commit() and roll-back(). The
enterprise bean's business methods or onMessage method
must not attempt to obtain or use the
javax.transaction.UserTransaction interface.
An enterprise bean with container-managed transaction demarcation
can use the setRollbackOnly() method of its
EJBContext object to mark the transaction so that the
transaction can never commit. Typically an enterprise bean marks a
transaction for rollback to protect data integrity before throwing an
application exception because they don't automatically cause the
container to rollback the transaction. Such a bean can also use the
getRollbackOnly() method of its EJBContext
object to test if the current transaction has been marked for
rollback.
The bean provider of a session bean or a message-driven bean must use the transaction-type element to declare whether the session bean or message-driven bean is of the bean-managed or container-managed transaction demarcation type. The transaction-type element is not supported for entity beans because all entity beans must use container-managed transaction demarcation. The bean provider of an enterprise bean with container=managed transaction demarcation may optionally specify the transaction attributes for the enterprise bean's methods.
This section shows examples of deployment descriptors for
specifying transaction attributes for methods of home and remote
interfaces of session and entity beans and onMessage
methods of message-driven beans. The container-transaction element is
used to define transaction attributes. Each container-transaction
element consists of a list of one or more method elements and the
transattribute element which specifies that all the listed methods are
assigned the specified transaction attribute value. All the methods
specified in a single container-transaction element be must be methods
of the same enterprise bean. The method element uses the ejb-name,
method-name, and method-params elements to denote one or more methods
of an enterprise bean's home and remote interfaces. There are three
legal formats of composing the method element:
<method>
<ejb-name>MyEjb</ejb-name>
<method-name>*</method-name>
</method>
|
This format is used to specify a default value of the transaction attribute for all the methods. There must be at most one container-transaction element for a given enterprise bean.
<method>
<ejb-name>MyEjb</ejb-name>
<method-name>myMethod</method-name>
</method>
This format is used to refer to a specified method of the remote or home interface of the specified enterprise bean. If there are multiple methods with the same overloaded name, this format refers to all the methods with the same name. There must be at most one container-transaction element for a given method name. If there is also a container-transaction element that uses format-1 element for the same bean, the value specified by the format-2 element takes precedence
<method>
<ejb-name>MyEjb</ejb-name>
<method-name>myMethod</method-name>
<method-params>
<method-param>myParameter1</method-param>
...
<method-param>myParameterN</method-param>
</method-params>
</method>
This format is used to refer to a single method within a set of
methods with an overloaded name. The method must be defined in the
remote or home interface of the specified enterprise bean. If there is
also a container-transaction element that uses the format-2 element
for the method name, or the format-1 element for the bean, the value
specified by the format-3 element takes precedence. The optional
method-intf element can be used to differentiate between
methods with the same name and signature that are defined in both the
remote and home interfaces.
The following is an example of the specification of the transaction
attributes in the deployment descriptor. The myMethod
method of MyEJBean bean is assigned the transaction
attribute Mandatory; all other methods of
MyEJBean bean are assigned the attribute
Required. All the methods of the enterprise bean
MyOtherEJBean are assigned the attribute
RequiresNew.
<ejb-jar>
...
<assembly-descriptor>
...
<container-transaction>
<method>
<ejb-name>MyEJBean</ejb-name>
<method-name>*</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
<container-transaction>
<method>
<ejb-name>MyEJBean</ejb-name>
<method-name>myMethod</method-name>
</method>
<trans-attribute>Mandatory</trans-attribute>
</container-transaction>
<container-transaction>
<method>
<ejb-name>MyOtherEJBean</ejb-name>
<method-name>*</method-name>
</method>
<trans-attribute>RequiresNew</trans-attribute>
</container-transaction>
</assembly-descriptor>
</ejb-jar>
The container may use a local transaction optimization for enterprise beans whose deployment descriptor indicates that connections to a resource manager are shareable. The container manages the use of the local transaction optimization transparent to the application.
The container may use the optimization for transactions initiated
by the container for a bean with container-managed transaction
demarcation and for transactions initiated by a bean with bean-managed
transaction demarcation with the UserTransaction
interface. The container cannot apply the optimization for a bean with
container-managed transaction demarcation if the client invokes the
bean with a transaction context that is imported by the container and
the bean method has the Required or
Mandatory transaction attribute.
The recommended way to manage transactions in J2EE is through container-managed specification. One of the major benefits of the J2EE platform is that it frees the designer and developer of application components from the burden of managing transactions by means of declarative transaction specification. Moreover, the transaction characteristics of an application can be changed with absolutely no modification of the developed code by just changing the transaction attributes in the deployment descriptors. It also results in a separation of roles. Transaction attributes are selected by someone who understands the application well, rather than someone who may just know one part of it. Bean-managed transaction specification is recommended in applications where more control over workflow is needed.
Almost all enterprise beans perform transactional work by accessing some form of resource like an EIS or an RDBMS via JDBC. The recommended use of different transaction attributes for enterprise beans taking part in transactional work is given below.
Required since it ensures that the methods of an
enterprise bean are invoked under a JTA transaction. In addition,
enterprise beans with the Required transaction attribute
can be easily composed to perform work under the scope of a single JTA
transaction.RequiresNew transaction attribute is recommended. An
example of this requirement is a bean method that performs
logging. This bean method should be invoked with
RequiresNew transaction attribute so that the logging
records are created even if the calling client's transaction is rolled
back.NotSupported transaction attribute can be used
when the resource being accessed for a transaction cannot be part of a
JTA transaction. This is the case when the resource manager
responsible for the transaction is not supported by the J2EE
product. For example, if a bean method is invoking an operation on an
enterprise resource planning system that is not integrated with the
J2EE server, the server has no control over that system's
transactions. In this case, it is best to set the transaction
attribute of the bean to be NotSupported to clearly
indicate that the enterprise resource planning system is not accessed
within a JTA transaction.Supports isn't
recommended. An enterprise bean with this attribute would have
transactional behavior that differed depending on whether the caller
is associated with a transaction context, possibly leading to a
violation of the ACID rules for transactions.Mandatory and
Never can be used when it is necessary to verify the
transaction association of the calling client. They reduce the
composability of a component by putting constraints on the calling
client's transaction context.
|
Transactions are specified and handled either by the container
(container-managed demarcation) or by a component (component-managed
demarcation). In component managed demarcation, an application
component can use the JTA UserTransaction interface or a
transaction demarcation API specific to an EIS (for example, JDBC
transaction demarcation using java.sql.Connection). The
EJB specification requires an EJB container to support both
container-managed and component-managed transaction demarcation
models. The JSP and servlet specifications require a web container to
support component-managed transaction demarcation. The requirements
demanded from a resource manager and a transaction manager for the
transaction management contract are as follows.
Resource Manager
A resource manager can be
classified based on the level of transaction support, as follows.
NO_TRANSACTION: The resource manager supports
neither resource manager local nor JTA transactions. It implements
neither XAResource nor LocalTransaction
interfaces.LOCAL_TRANSACTION: The resource manager supports
local transactions by implementing the LocalTransaction interface as
defined in the specification.XA_TRANSACTION: The resource manager supports both
local and JTA transactions by implementing
LocalTransaction and XAResource interfaces
respectively.Application Server
An application server is
required to support resource managers with all three levels of
transactional support, namely, NO_TRANSACTION,
LOCAL_TRANSACTION and XA_TRANSACTION.
LocalTransaction interface-based contract to manage local
transactions for a resource manager.javax.resource.spi.ConnectionEventListener interface to
get transaction-related event notifications.The table below shows the typical combinations one is likely to
encounter with transaction managers built into an application server
and resource managers. Only the recommended options are shown in the
cells. When multiple resource managers participate in a transaction,
the EJB container uses a transaction manager to coordinate the
transaction. The contract between the transaction manager and resource
manager is defined using the XAResource interface. So, if
a single resource manager instance participates in either a
container-managed or component-managed transaction, the container can
choose to do one of the following.
|
Resource Manager
|
Resource Manager
|
Resource Manager
|
App Server
|
- |
Use RML Tran |
Use RML Tran |
App Server
|
Use Local JTA |
Use RML Tran |
Use RML Tran |
App Server XA_TRANSACTION |
Use Local JTA |
Use RML Tran |
Use JTA/XA Tran |
The EJB container uses the transaction manager to manage the transaction. The transaction manager uses one-phase commit optimization to coordinate the transaction for this single resource manager instance as there is no other resource manager involved.
The EJB container lets the resource manager of the single resource participating in this transaction take control and coordinate it internally without involving an external transaction manager of the container. If an application accesses a single resource manager using a JTA/XA transaction, it has a performance overhead as opposed to using local transactions. The overhead is due to the involvement of an external transaction manager in the coordination of the JTA/XA transaction. To avoid the overhead of using an JTA/XA transaction in a single resource manager scenario, the application server may optimize by using a local transaction instead of a JTA/XA transaction. This scenario is shown in the figure below.
|
A resource manager local transaction is specific to a particular resource (RDBMS or EIS) connection and is completely managed by the underlying resource manager. The application server with its EJB container and built-in transaction manager has neither any control nor knowledge about any local transactions started by application components. The typical cases when local transactions are involved fall into the following categories.
The only way to undo the effect of a mistake made in a transaction is to run another transaction to invert the prior effect of the committed transaction. A compensating transaction is such a group of operations constituting a transaction used to undo the effect of a previously committed transaction. There are instances where multiple accesses to enterprise information systems need to be grouped under a single transaction but not all of the systems support JTA transactions. In such cases it is necessary to define a compensating transaction for each enterprise information system access that is under the scope of a local transaction. If a component needs to access an enterprise information system that does not support JTA transactions or access an enterprise information system that is not supported by a particular J2EE platform, compensating transactions need to be defined.
In such cases the enterprise information systems are accessed under the scope of resource manager local transactions. If multiple enterprise information systems are involved, this creates the challenge of having to group all the work to multiple enterprise information systems into an atomic unit. For example, suppose an application needs to perform an atomic operation that involves updating three enterprise information systems: two JDBC databases that supports JTA transactions and an enterprise resource planning system that does not. The application would need to define a compensating transaction for the update to the enterprise resource planning system. The approach is illustrated in the following example.
updateEIS();
try {
UserTransaction.begin();
UpdateJDBC_RDBMS1();
UpdateJDBC_RDBMS2();
UserTransaction.commit();
}
catch (RollbackException ex) {
undoUpdateEIS();
}
The methods updateEIS(),
updateJDBC_RDBMS1(), and updateJDBC_RDBMS2()
have implementations to perform work on the EIS, RDBMS1 and RDBMS2
respectively. The undoUpdateEIS() method implements the
logic to undo the effect of updateEIS() in case one of
the JTA transactions involving RDBMS1 or RDBMS2 does not commit
successfully. Ideally this kind of implementation of compensation
logic should be encapsulated in a session bean with bean-managed
transaction. The transaction attribute of such a session bean should
be set to NotSupported if its only responsibility is to
access an enterprise information system that does not support JTA
transactions. Finally, there are a few important observations that one
needs to be careful about in compensating transactions.
It may not always be possible to undo the effect of a
committed transaction by using compensation logic. Consider what would
happen in the example above if one of the JTA transactions does not
commit and for some reason the method undoUpdateEIS()
does not succeed!
In case of a server crash there would be a violation of the
atomicity property when a compensating transaction is used. For
example, if the system crashes after the method
updateEIS(), the updates to the two databases will not
take place.
It is possible that an inconsistent state of data is visible due to concurrent enterprise information system access since local resource manager transactions are actually committed but undone subsequently. In the previous example, a concurrent enterprise in-formation system access might see the update to the enterprise resource planning system, i.e., uncommitted data which might be rolled back later.
In order to deal with all sorts of potential failures and inconsistencies, any application using compensating transactions must have extra logic implemented. Because of all the complexity and drawbacks of using compensating transactions it should be avoided unless there is no other way to deal with such situations. It is strongly recommended to use JTA/XA transactions as they provide a simple and safe way to achieve the ACID properties across multiple components in multiple tiers accessing multiple relational database management systems or enterprise information systems.
This series has barely scratched the surface of a very complex topic encountered in everyday distributed enterprise computing environments. Although some of the core technologies have been around for a while, there is a resurgence of interest with the advent of the large scale web applications being developed in enterprises.
It's important to understand the choices one needs to make in choosing an application server so that it can work seamlessly with resource managers to provide the best transactional robustness and performance. The key questions to be asked while designing such a system concern which industry standard interfaces are implemented by both the transaction manager (built into the application servers) and the resource managers.
Dibyendu Baksi is a J2EE transactions systems and frameworks designer and developer for Sun Microsystems, Inc.
Return to ONJava.com.
Copyright © 2007 O'Reilly Media, Inc.