Oracle8i Enterprise JavaBeans and CORBA Developer's Guide Release 2 (8.1.6) A81356-01 |
|
This chapter covers transaction management for both CORBA and EJB applications. Transaction handling in the two distributed component development models has some fundamental similarities, but there are also some differences. For example, the application developer who is using EJBs can elect to have the EJB container manage all transactions in a way that is transparent to the client application and to the bean developer. The developer does not have to write any transaction code at all--the transactional properties of the application can be declared at bean deployment time. In this sense, EJBs are said to have declarative transactional capability.
The CORBA developer, on the other hand, must use the transactional APIs provided--usually a mapping of a subset of the OMG Object Transaction Service (OTS) API, such as the Java Transaction Service (JTS) that is supplied with Oracle8i JServer. The CORBA developer must code calls to a transaction service to enable transactional properties for distributed objects, where this is required.
But the EJB developer might require finer-grained control of the application's transactional properties than that offered by the declarative transactional capabilities built-in to the EJB container. In this case, the developer can use explicit calls to transaction API methods, either on the client side or in the bean implementations themselves.
This chapter discusses the following topics:
A transaction is a unit of work, usually associated with a database management system. Transactions are described in terms of the so-called ACID properties. A transaction is:
Most of the transactional features that are part of the Oracle8i database server are available to the CORBA or EJB distributed application developer.
Oracle8i JServer supports two transaction APIs for use in CORBA and EJB applications:
The JTS is a Java binding to the OMG Object Transaction Service (OTS). It is used for client-side demarcated transactions, and for transaction management in CORBA server objects.
The UserTransaction interface is used in EJBs, where the bean is running using the transaction attribute TX_BEAN_MANAGED.
The implementations of JTS that is supplied for this Oracle8i release is intended mostly to support client-side transaction demarcation. As such it has some limitations that you should be aware of when designing your application.
This implementation of JTS does not manage distributed transactions. Transaction control distributed among multiple database servers, with support for the required two-phase commit protocol, will be available in an upcoming release of Oracle8i JServer.
The JTS transaction API supplied with Oracle8i JServer manages only one resource: an Oracle8i database session. A transaction cannot span multiple servers or multiple database sessions in a single service.
Transaction contexts are never propagated outside a server. If a server object calls out to another server, the transaction context is not carried along.
However, a transaction can involve one or many objects. The transaction can encompass one or many methods of these objects. The scope of a transaction is defined by a transaction context that is shared by the participating objects.
Nested transactions are not supported in this release. If you attempt to begin a new transaction before committing or rolling back any existing transaction, the transaction service throws a SubtransactionsUnavailable
exception.
Methods of the JST that support transaction timeout, such as setTimeout()
, do not work in this release. You can invoke them from your code, and no exception is thrown, but they have no effect.
The transaction services supplied with this release do not interoperate with other OTS implementations.
A transaction is said to be demarcated. This simply means that it has a definite beginning and definite end point. For example, in an interactive tool such as SQL*Plus, each SQL DML statement implicitly begins a new transaction, if it is not already part of a transaction. A transaction ends when a SQL COMMIT or ROLLBACK statement is issued.
In a distributed object application, transactions are often described as client-side demarcated (or sometimes just client demarcated), or server-side demarcated (equivalently server demarcated).
In client-side demarcation, a transactional client explicitly encloses one or more method invocations on a server object with demarcation methods that begin and end transactions. The begin and end demarcaters are method calls on the client-side transaction service. See "Client-Side Demarcation" for specific examples.
Server-side transaction demarcation implies that the server-side object begins and either commits or rolls back a transaction. Note that a transaction can span several objects, any one of which can suspend, resume, or end the transaction.
The transaction context is a pseudo-object that is passed to the server object from the client, or from one server object to another, in the case where one server object is invoking methods on another, and hence acting as its client. The transaction context carries the state of the transaction.
After a client-side transaction service is initialized, and a begin transaction method is invoked, the transaction service implicitly creates a transaction context, and assigns a transaction ID number to the context. The client transaction service then propagates the transaction context to each participant in the transaction, that is, to each object that the client calls.
Propagation of the transaction context on each method invocation is normally transparent to the client program. The transaction context is maintained by the transaction service for each client. Transaction contexts are propagated transparently from the transaction initiator to the server object. On the client side, an interceptor is engaged to submit the transaction context on any method call to a server object. A server-side interceptor extracts the transaction context information, and makes it available to the server object.
As stated in "Limitations", a transaction context cannot span multiple sessions. Each new session connection requires a new transaction context.
Oracle8i supports a version of the JTS. The JTS is a Java mapping of the OMG Object Transaction Service (OTS). There are two classes that the application developer can use:
TransactionService
UserTransaction
, implemented by oracle.aurora.jts.client.AuroraTransactionService
The section below describes the TransactionService
interface. Because it is used with EJBs, the UserTransaction
class is described in "AuroraUserTransaction".
Use the TransactionService
to initialize a transaction context on the client. Include the AuroraTransactionService
package in your Java client source with the following import statements:
import oracle.aurora.jts.client.AuroraTransactionService; import javax.jts.*; import oracle.aurora.jts.util.*;
These classes are included in the library file aurora_client.jar
, which must be in the classpath when compiling and executing all source files that use the JTS.
There is only one method in this package that you can call:
public synchronized static void initialize(Context initialContext, String serviceName)
This method initializes the transaction context on a client. The parameters are:
serviceName |
The complete service name. For example |
An example of using initialize()
is:
Hashtable env = new Hashtable(); env.put(Context.URL_PKG_PREFIXES, "oracle.aurora.jndi"); env.put(Context.SECURITY_PRINCIPAL, "scott"); env.put(Context.SECURITY_CREDENTIALS, "tiger"); env.put(Context.SECURITY_AUTHENTICATION, ServiceCtx.NON_SSL_LOGIN); Context initialContext = new InitialContext(env); AuroraTransactionService.initialize (initialContext, "sess_iiop://localhost:2481:ORCL");
See also the complete example in "clientside".
The JTS package itself contains methods that a client-side or server-side object uses to begin transactions, commit or roll back a transaction, and perform utility functions such as setting the transaction timeout. The JTS methods should be used in CORBA or EJB clients, or in CORBA server objects. Developers implementing EJBs, and who need fine-grained transaction control within beans should use the UserTransaction
interface in a bean-managed state. See "Transaction Management for EJBs" for more information.
To use the JTS methods, code the following import statements in your source:
import oracle.aurora.jts.util.TS; import javax.jts.util.*; import org.omg.CosTransactions.*;
The oracle.aurora.jts.util
package is included in the library file aurora_client.jar
, which must be in the classpath for all Java sources that use the JTS.
You use the static methods in the TS
class to get the transaction service.
The JTS includes the following methods:
public static synchronized TransactionService getTS()
getTS()
returns a transaction service object. Once a transaction service has been obtained, you can invoke the static method getCurrent()
on it to return a Current
pseudo-object, the transaction context. Then you can invoke methods to begin, suspend, resume, commit, or roll back the current transaction on the Current
pseudo-object.
Here is an example that begins a new transaction on a client, starting with getting the JNDI initial context:
import oracle.aurora.jndi.sess_iiop.ServiceCtx; import oracle.aurora.jts.client.AuroraTransactionService; import javax.naming.Context; import javax.naming.InitialContext; import java.util.Hashtable; ... Context ic = new InitialContext(env); ... AuroraTransactionService.initialize(ic, serviceURL); ... Employee employee = (Employee)ic.lookup (serviceURL + objectName); EmployeeInfo info; oracle.aurora.jts.util.TS.getTS().getCurrent().begin();
If there is no transaction service available, then getTS()
throws a NoTransactionService
exception.
The methods that you can call to control transactions on the current transaction context are the following:
public void begin()
Begins a new transaction.
Can throw these exceptions:
NoTransactionService
--if you have not initialized a transaction context.
SubtransactionsUnavailable
--if you invoke a begin()
before the current transaction has been committed or rolled back.
See the section "TransactionService"
for information about initialization.
public Control suspend()
Suspends the current transaction in the session. Returns a Control
transaction context pseudo-object. You must save this object reference for use in any subsequent resume()
invocations. Invoke suspend()
in this way:
org.omg.CosTransactions.Control c = oracle.aurora.jts.util.TS.getTS().getCurrent().suspend();
suspend()
can throw these exceptions:
NoTransactionService
--if you have not initialized a transaction context.
TransactionDoesNotExist
--if not in an active transaction context. This can occur if a suspend()
follows a previous suspend()
, with no intervening resume()
.
If suspend()
is invoked outside of a transaction context, then a NoTransactionService
exception is thrown. If suspend()
is invoked before begin()
has been invoked, or after a suspend()
, the a exception is thrown.
public void resume(Control which)
Resumes a suspended transaction. Invoke this method after a suspend()
, in order to resume the specified transaction context. The which
parameter must be the transaction Control
object that was returned by the previous matching suspend()
invocation in the same session. For example:
org.omg.CosTransactions.Control c = oracle.aurora.jts.util.TS.getTS().getCurrent().suspend(); ... // do some non-transactional work oracle.aurora.jts.util.TS.getTS().getCurrent().resume(c);
resume()
can throw:
InvalidControl
--if the which
parameter is not valid, or is null.
public void commit(boolean report_heuristics)
Commits the current transaction. Set the report_heuristics
parameter to false.
(The report_heuristics
parameter is set to true for extra information on two-phase commits. Because this release of JServer does not support the two-phase commit protocol for distributed objects, use of the report_heuristics
parameter is not meaningful. It is included for compatibility with future releases.)
commit()
can throw:
HeuristicMixe
d--if report_heuristics
was set true, and a two-phase commit is in progress.
HeuristicHazard
--if report_heuristics
was set true, and a two-phase commit is in progress.
The HeuristicMixe
d and HeuristicHazard
exceptions are documented in the OTS specification. See "For More Information" for the location of the OTS specification.
If there is no active transaction, commit()
throws a NoTransaction
exception.
public void rollback()
Rolls back the effects of the current transaction.
Invoking rollback()
has the effect of ending the transaction, so invoking any JTS method except begin()
after a rollback()
throws a NoTransaction
exception.
If not in a transaction context, rollback()
throws the NoTransaction
exception.
public void rollback_only() throws NoTransaction {
rollback_only()
modifies the transaction associated with the current thread so that the only possible outcome is to roll back the transaction. If not in a transaction context, rollback_only()
throws the NoTransaction
exception.
public void set_timeout(int seconds)
This method is not supported, and has no effect if invoked. The default timeout value is 60 seconds in all cases.
public Status get_status()
You can call get_status()
at any time to discover the status of the current transaction. Possible return values are:
javax.transaction.Status.StatusActive
javax.transaction.Status.StatusMarkedRollback
javax.transaction.Status.StatusNoTransaction
The complete set of status ints is defined in javax.transaction.Status
.
public String get_transaction_name() {
Invoke get_transaction_name()
to see the name of the transaction, returned as a String. If this method is invoked before a begin()
, after a rollback()
, or outside of a transaction context, it returns a null string.
This section shows some examples that use the JTS interface for CORBA client code and CORBA server objects. See "Transaction Examples" for a set of complete examples that you can run and modify.
Follow these steps to use JTS methods in your CORBA client code:
AuroraTransactionService.initialize()
, passing to it the service URL for the application (for example, sess_iiop://localhost:2481:ORCL
) and the JNDI initial context.
TS.getTS().getCurrent().begin()
.
For example:
import oracle.aurora.jndi.sess_iiop.ServiceCtx; import oracle.aurora.jts.client.AuroraTransactionService; import oracle.aurora.jts.util.TS; import org.omg.CosTransactions.*; // Include normal startup code... // Initialize a transaction context... AuroraTransactionService.initialize(ic, serviceURL); // Begin a transaction... oracle.aurora.jts.util.TS.getTS().getCurrent().begin(); // Call methods that involve SQL DML... employee.updateEmployee(info); ... // Commit (or roll back) the SQL statements... oracle.aurora.jts.util.TS.getTS().getCurrent().commit(false);
For a complete example that uses these techniques for client-side transaction demarcation, see "clientside".
Follow these steps to use JTS methods in your CORBA server object code:
oracle.aurora.jts.util.TS
org.omg.CosTransactions
AuroraTransactionService.initialize()
on the server, as the server does this for you.
TS.getTS().getCurrent().begin()
. You can do this in a separate method, or as part of a method that does the first SQL DML using JDBC or SQLJ. A transaction spans methods--its scope is within the session where it is begun.
TS.getTS().getCurrent().commit(false), or
TS.getTS().getCurrent().rollback().
You can also invoke other JTS methods, such as set_timeout()
, from within a server object.
See the complete example at "serversideJTS" for a demonstration of CORBA server-side transaction demarcation.
See the complete example at "multiSessions" for an example that establishes multiple server sessions, each with its own transaction context.
The previous sections focused on general aspects of transaction management for distributed objects, and on transaction management for CORBA applications using the JTS.
An EJB application can also use JTS--on the client side only--to manage transactions. More typically an EJB application uses declarative transaction management, letting the EJB container provide the transaction control. You do this by specifying the appropriate value for the TransactionAttribute of the EJB deployment descriptor, either for the whole EJB, or on a method-by-method basis, where applicable.
For example, if the deployment descriptor for a bean declares that the bean has the transaction attribute TX_REQUIRES_NEW, then the bean container starts a transaction before each invocation of bean method, and attempts to commit the transaction when the method completes.
The following sections describe the values that you can set for the EJB transaction attribute.
The bean deployer declares the transaction handling characteristics of a bean in the deployment descriptor. This is specified in the transaction attribute, which has six possible values:
The semantics of these attribute values are described in this section. See "Programming Restrictions" for more information about the EJB deployment descriptor itself.
When TX_NOT_SUPPORTED is declared for the bean itself, it means that Oracle8i does not invoke transaction support for the bean methods. However, a method declaration in the deployment descriptor can over-ride this declaration. If the client is in a transaction (has established an active transaction context), then the bean container suspends transaction support during delegation of method calls on the bean, and resumes the transaction context when the method call completes.
The suspended transaction context is not propagated to other objects that are invoked from within the bean code.
A bean that is running under TX_NOT_SUPPORTED cannot perform any SQL operations. An exception is thrown by the EJB server if this is attempted.
If the client invokes a bean method with the TX_REQUIRED attribute, there are two possibilities:
The transaction context is passed to other Enterprise JavaBean objects that are invoked from the enterprise Bean object, as long as they are in the same session.
If the client has established a transaction context, then the bean container uses that context. If the client has no established transaction context, then the EJB methods are invoked with no transaction support.
The container always invokes the bean methods with a new transaction. The container commits the transaction, if possible, before sending the method results to the client.
If the client has established a transaction content, the client transaction is suspended before the bean transaction is started, and is resumed when the bean transaction completes (at the end of each method call).
If the client has established a transaction context, the association is suspended before the new transaction is started and is resumed when the new transaction has completed.
The container-managed transaction context is passed to the resources or other EJB objects that the bean invokes.
An enterprise Bean that has the TX_REQUIRES_NEW transaction attribute is always invoked in the scope of a new transaction. The container starts a new transaction before delegating a method call to the enterprise Bean object, and attempts to commit the transaction when the method call on the enterprise Bean object has completed. The container performs the commit protocol before the method result is sent to the client.
The new transaction context is passed to the resources or other enterprise Bean objects that are invoked from the enterprise Bean object.
If an EJB method is invoked with the TX_MANADATORY attribute, the client transaction context is always used. If the client has not established a transaction context, the container throws the TransactionRequired
exception to the client.
The client transaction context is propagated to the resources or other enterprise Bean objects that are invoked from the enterprise Bean object.
The bean-managed attribute value is the one that lets the bean get access to the transaction service on its own behalf. Session beans get access to the transaction service through the session context that is supplied to the bean at initialization, as a parameter in the setSessionContext()
call. The SessionContext
interface subclasses EJBContext.
The bean implementation must use the javax.jts.UserTransaction
interface methods to manage transactions on its own. See "Using The Java Transaction Service" for a description of these methods.
The TX_BEAN_MANAGED attribute value cannot be mixed with other transaction attribute values. For example, if the bean-level descriptor, or one of the method-level descriptors, specifies TX_BEAN_MANAGED, then all method-level descriptors present must specify TX_BEAN_MANAGED. When using bean-managed transactions, the transaction boundaries span bean methods. You can begin a transaction in one method, and the transaction can be rolled back or committed in a separate method, called later.
The container makes the javax.jts.UserTransaction
interface available to the enterprise Bean though the EJBContext.getUserTransaction()
method, as illustrated in the following example.
import javax.jts.UserTransaction; ... EJBContext ic = ...; ... UserTransaction tx = ic.getUserTransaction(); tx.begin(); ... // do work tx.commit();
The container must manage transactions on a TX_BEAN_MANAGED Bean as follows.
When a client invokes a stateful TX_BEAN_MANAGED Bean, the container suspends any incoming transaction. The container allows the session instance to initiate a transaction using the javax.jts.UserTransaction
interface. The instance becomes associated with the transaction and remains associated until the transaction terminates.
When a Bean-initiated transaction is associated with the instance, methods on the instances run under that transaction.
It is possible that a business method that initiated the transaction completes without committing or rolling back the transaction. The container must retain the association between the transaction and the instance across multiple client calls until the transaction terminates.
Table 5-1 Effect of declarative transaction attribute (Cont.)
An EJB can optionally implement the session synchronization interface, to be notified by the container of the transactional state of the bean. The following methods are specified in the javax.ejb.SessionSynchronization
interface:
public abstract void afterBegin() throws RemoteException
The afterBegin()
method notifies a session Bean instance that a new transaction has started, and that the subsequent methods on the instance are invoked in the context of the transaction.
A bean can use this method to read data from a database and cache the data in the bean's fields.
This method executes in the proper transaction context.
public abstract void beforeCompletion() throws RemoteException
The container calls the beforeCompletion()
method to notify a session bean that a transaction is about to be committed. You can implement this method to, for example, write any cached data to the database.
public abstract void afterCompletion(boolean committed) throws RemoteException
The container calls afterCompletion()
to notify a session bean that a transaction commit protocol has completed. The parameter tells the bean whether the transaction has been committed or rolled back.
This method executes with no transaction context.
If you are using JDBC calls in your bean to update a database, you should not also use JDBC to perform transaction services, by calling methods on the JDBC connection. Do not code JDBC calls on a connection, for example:
Connection conn = ... ... conn.commit(); // DO NOT DO THIS!!
You also avoid doing direct SQL commits or rollbacks through JDBC. Code the bean to either handle transactions directly using the javax.jts.UserTransactions
interface (if the TransactionAttribute
value is TX_BEAN_MANAGED), or let the bean container manage the bean transactions.
You use the UserTransaction
interface to manage transactions in Enterprise JavaBeans. The UserTransaction
interface is a higher-level interface than the raw JTS, although its functionality is almost identical. However, EJB developers must use the UserTransaction
interface for EJB bean-managed transaction support. The UserTransaction
interface is used only for bean-managed EJBs.
See "serversideJTS" for a complete example of bean-managed transaction control.
To incorporate UserTransaction
methods in your bean implementation code, follow these steps:
javax.jts.UserTransaction
package.
setSessionContext()
SessionBean
method. See the example.
UserTransaction
methods on the transaction context. See the example.
public void begin()
begin()
creates a new transaction and associates it with the current bean.
Throws IllegalStateException
if you attempt to invoke it in the context of an existing transaction.
public void commit()
commit()
commits the transaction results, and completes the transaction associated with the current bean. When the commit()
method completes, the bean is no longer associated with a transaction.
commit()
can throw any of the following exceptions:
TransactionRolledbackException
HeuristicMixedException
HeuristicRollbackException
SecurityException
IllegalStateException
public int getStatus()
Returns the status of the current transaction. See "Java Transaction Service Methods" for more information about the status values that can be returned.
public void resume()
Resumes a suspended transaction.
public void rollback()
Rolls back the effects of the current transaction.
rollback()
can throw the following exceptions:
The effect of a setRollbackOnly()
invocation is that the only possible conclusion to the current transaction is a roll back operation. Any attempt to perform a commit()
after setRollbackOnly()
is invoked results in a exception.
setRollBackOnly()
throws an IllegalStateException
, if not in a transaction.
public void setTransactionTimeout(int arg1)
This method is implemented, but has no effect in this release. The timeout value is always 60 seconds.
An EJB can optionally implement the session synchronization interface, to be notified by the server of the state of the transaction. The following methods are specified in the javax.ejb.SessionSynchronization
interface:
public abstract void afterBegin() throws RemoteException
The afterBegin()
method notifies a session Bean instance that a new transaction has started, and that the subsequent methods on the instance are invoked in the context of the transaction.
A bean can use this method to read data from a database and cache the data in the bean's fields.
This method executes in the proper transaction context.
public abstract void beforeCompletion() throws RemoteException
The container calls the beforeCompletion()
method to notify a session bean that a transaction is about to be committed. You can implement this method to, for example, write any cached data to the database.
public abstract void afterCompletion(boolean committed) throws RemoteException
The container calls afterCompletion()
to notify a session bean that a transaction commit protocol has completed. The parameter tells the bean whether the transaction has been committed or rolled back.
This method executes with no transaction context.
This section shows a few abbreviated examples of transaction management for EJB applications. For a set of complete programs, see "Transaction Examples".
If your EJB application requires client-side transaction demarcation, you use the JTS interface, as explained in "Using The Java Transaction Service". See the section "clientside" for a complete example of EJB client-side transaction demarcation.
Use the UserTransaction
interface to set up a transaction context within an EJB. In the bean implementation, make sure to import the javax.jts.UserTransaction
package. Unlike the TransactionService
when used on the client side, you do not need to initialize the UserTransaction
interface from within an EJB. The container does that for you.
In the EJB, use the setSessionContext()
session bean method to obtain the session context, and save it in an instance variable. For example, code this implementation of the setSessionContext()
method:
public class XBean implements SessionBean { SessionCtx ctx; ... public void setSessionContext(SessionContext ctx) { this.ctx = ctx; }
You can then use the session context ctx
to invoke UserTransaction
methods.
Invoke the begin()
method as follows:
ctx.getUserTransaction.begin();
to start a transaction.
Invoke the commit()
method as follows:
ctx.getUserTransaction().commit();
to end the transaction with a commit.
Invoke other methods of the UserTransaction
interface in the same way that you do a begin()
or a commit()
--invoke them on a UserTransaction
object of the session context.
See the section "serversideJTS" for a complete example that uses the UserTransaction
interface in an EJB.
If you are using JDBC calls in your CORBA server object or EJB to update a database, and you have an active transaction context, you should not also use JDBC to perform transaction services, by calling methods on the JDBC connection. Do not code JDBC transaction management methods. For example:
Connection conn = ... ... conn.commit(); // DO NOT DO THIS!!
Doing so will cause a SQL exception to be thrown.
You must also avoid doing direct SQL commits or rollbacks through JDBC. Code the bean to either handle transactions directly using the javax.jts.UserTransactions
interface (if the TransactionAttribute
value is TX_BEAN_MANAGED), or let the bean container manage the bean transactions.
Information on the Java Transaction Service is available at:
http://java.sun.com:/products/jts/index.html
The Sun JTA specification is available at:
http://java.sun.com/products/jts/index.html
The OTS specification is part of the CORBA services specification. Chapter 10 (individually downloadable) contains the OTS specification. Get it at:
http://www.omg.org/library/csindx.html