|
|
This topic includes the following sections:
This topic describes how to integrate transactions with CORBA Java, EJB, and RMI applications that use the WLE JDBC/XA driver and run under BEA WebLogic Enterprise (WLE). Before you begin, you should read Introducing Transactions.
This chapter describes handling transactions in CORBA Java, EJB, and RMI applications that use the WLE JDBC/XA driver to connect to resources.
For EJB applications, the information in this document supplements the Sun Microsystems, Inc. evolving Enterprise JavaBeans 1.1 Specification (Public Release 2 dated October 18, 1999). In this document, all references to this specification pertain to the Public Release version. For general information about implementing Enterprise JavaBeans in WLE applications, see The WLE Enterprise JavaBeans (EJB) Programming Environment.
This topic includes the following sections:
Before You Begin
About Transactions and the WLE JDBX/XA Driver
WLE provides a multithreaded JDBC/XA driver for Oracle Corporation's Oracle8i database management system. The WLE JDBC/XA driver fully supports XA, the bidirectional system-level interface between a transaction manager and a Resource Manager of the X/Open Distributed Transaction Processing (DTP) model. This driver is available to CORBA Java, EJB, and RMI applications and runs in the WLE environment only.
Java applications use the WLE JDBC/XA driver to establish concurrent connections to multiple Oracle8i databases via their associated Resource Managers. For distributed transactions, applications must obtain database connections from the JDBC connection pool. (However, this is not a requirement for other jdbcKona drivers in local transaction mode or for third-party drivers.) Thereafter, applications perform database operations using standard JDBC API calls.
A JDBC connection is governed by the pooled connection lifecycle in the JDBC connection pool. As such, the application server might implicitly close JDBC/XA connections to enforce certain personality-specific transactional resource restrictions, as described in JDBC Accessibility in Java Methods. For more information about using WLE JDBC connection pools with WLE JDBC/XA driver, see "Using JDBC Connection Pooling" in Using the JDBC Drivers.
The JavaServerXA
server hosts the WLE JDBC/XA driver. The JavaServerXA has the following characteristics:
Support for Transactions Using the WLE JDBC/XA Driver
Pooled Connections
Characteristics of JavaServerXA
WLE fully supports the JDBC 1.22 API (core functionality), the JDBC 2.0 Core API, and the distributed transactions (the javax.sql.DataSource
API), connection pooling, and JNDI capabilities in the JDBC 2.0 Optional Package API. See Using the JDBC Drivers for a complete list of WLE-supported JDBC 2.0 features.
WLE applications using the WLE JDBC/XA driver can perform local transactions as well as distributed (also called global) transactions. A local transaction involves updates to a single Resource Manager (such as a database), while a distributed transaction involves updates across multiple Resource Managers.
The WLE JDBC/XA driver never starts a local transaction on behalf of an application. However, if the application performs database operations without first explicitly starting a distributed transaction, then these database operations occur within an "unspecified transaction context" and WLE delegates the responsibility of handling this situation to the database.
In Oracle8i, for example, the database might start a local transaction to perform such database operations.
Supported JDBC Standards
Local Versus Distributed (Global) Transactions
Failure to commit a local transaction may result in XAER_OUTSIDE
error (indicating that the Resource Manager is performing work outside a distributed transaction) on subsequent distributed transaction operations, which includes beginning a distributed transaction. It is the responsibility of the application to be aware of the transaction context at any point and to complete distributed or local transactions appropriately.
Table 10-1 lists differences between local and distributed transactions.
Differences Between Local and Distributed Transactions
To use the WLE JDBC/XA driver, you must specify the ENABLEXA parameter (ENABLEXA=Y ) in the JDBCCONNPOOLS section of the UBBCONFIG , as shown in Listing 10-1. In this example, distributed transactions are enabled for the bank_pool connection pool.
Note: This setting applies only to the WLE JDBC/XA driver.
Listing 10-1 Specifying JDBCCONNPOOLS Information in UBBCONFIG
JDBCCONNPOOLS
bank_pool
SRVGRP = BANK_GROUP1
SRVID = 2
DRIVER = "weblogic.jdbc20.oci815.Driver"
URL = "jdbc:weblogic:oracle:Beq-local"
PROPS = "user=scott;password=tiger;server=Beq-Local"
ENABLEXA = Y
INITCAPACITY = 2
MAXCAPACITY = 10
CAPACITYINCR = 1
CREATEONSTARTUP = Y
For more information about configuring JDBC connection pools, see "Using JDBC Connection Pooling" in Using the JDBC Drivers.
Applications must carefully and explicitly demarcate transaction boundaries between distributed and local transaction contexts. For example, when an application uses the WLE JDBC/XA driver to connect to a database:
After completing local transactions, the application must then disable autocommit before beginning a new distributed transaction. Listing 10-2 provides a simple example to illustrate switching between a distributed and local transaction.
Listing 10-2 Switching Between Distributed and Local Transactions
// Assumes that javax.transaction.UserTransaction (tx) and
// java.sql.Connection (con) were initialized previously
// Begin a distributed transaction
System.out.println("Beginning distributed transaction...");
tx.begin();
// Database operations within scope of transaction tx
if(gotException){
try{
tx.rollback();
System.out.println("rolled back transaction");
}catch(Exception e){}
}
elseif{
tx.commit();
System.out.println("committed transaction");
}
// Local transactions
conn.setAutoCommit(true)
...[Database operations]...
conn.setAutoCommit(false)
// Begin another distributed transaction
System.out.println("Beginning distributed transaction...");
tx.begin();
...
For WLE JDBC/XA connections, database operations will always be performed in the current transaction context. For example, an application might obtain a JDBC/XA connection in a NULL
transaction context, begin a distributed transaction, and then perform database operations using that connection. These database operations will be performed in the context of the current distributed transaction.
Applications use WLE JDBC/XA connection API in the same way as other jdbcKona connections except that, while within a distributed transaction context:
Transaction Contexts in WLE JDBC/XA Connections
Listing 10-3 shows, in a sample CORBA Java application, how to determine the current transaction context and commit a local or global transaction accordingly.
Listing 10-3
Determining Whether the Application is in a Distributed Transaction
// Assumes that org.omg.CosTransactions.Current (tc) and Similarly, for bean-managed transactions in an EJB application, the application can determine whether the application is currently in a distributed transaction by calling the UserTransaction.getStatus()
method and testing for a returned STATUS_NO_TRANSACTION
.
This topic includes the following sections:
// java.sql.Connection (con) were initialized before
// database operations were attempted
if (tc.get_status() != org.omg.CosTransactions.Status.StatusNoTransaction)
{
// Application is currently in a distributed transaction
tc.commit(true);
}
else
{
// Application is currently in a local transaction
con.commit();
}JDBC Accessibility in Java Methods
Note: Attempting to use a WLE JDBC/XA connection in a method where it is not supported may have undefined behavior and possibly raise a SQLException .
Table 10-2 lists which methods in CORBA methods can access JDBC/XA connections.
JDBC/XA Accessibility in CORBA Methods
Server Method |
Accessibility |
---|---|
After completing the initialize method, WLE automatically closes any open connections and writes a warning message to the ULOG .
For transaction-bound and process-bound objects, the CORBA framework allows open connections to be retained at method end, and the transaction context of the retained connections will be as described in Transaction Contexts in WLE JDBC/XA Connections upon subsequent method invocations. However, for method-bound objects, applications must explicitly close open connections before method end. If not, WLE automatically closes any open connections and writes a warning message to the ULOG .
For EJB methods, accessibility to JDBC/XA connections varies depending on the EJB type. For details about retaining JDBC/XA connections across method invocations (for stateful session beans only), including examples, see Section 11.3.3 in Sun Microsystem's Enterprise JavaBeans Specification 1.1.
Note: For all bean types, after completing the ejbCreate method, WLE automatically closes any open connections and writes a warning message to the ULOG .
Table 10-3 lists which stateful session bean methods can access JDBC/XA connections.
Bean method |
Container-managed transaction |
Bean-managed transaction |
---|---|---|
For stateful session beans, the Bean Provider must close all JDBC connections in ejbPassivate and assign the instance's fields storing the connections to null. However, after completing the ejbPassivate method, WLE automatically closes any open connections and writes a warning message to the ULOG .
Table 10-4 lists which stateless session bean methods can access JDBC/XA connections.
Bean Method |
Container-Managed Transaction |
Bean-Managed Transaction |
---|---|---|
Note: For stateless session beans, after completing a business method, WLE automatically closes any open connections and writes a warning message to the ULOG .
Table 10-5 lists which entity bean methods can access JDBC/XA connections.
Bean Method |
Accessibility |
---|---|
Before applications can use the WLE JDBC/XA driver, the JDBC/XA driver must be integrated into your development environment by completing the following steps:
Listing 10-4 OPENINFO Setting in Sample UBBCONFIG
*GROUPS
SYS_GRP
LMID = SITE1
GRPNO = 1
BANK_GROUP1
LMID = SITE1
GRPNO = 2
OPENINFO = "ORACLE_XA:Oracle_XA+Acc=P/scott/tiger+SesTm=100+LogDir=.+DbgFl=0x7+MaxCur=15+Threads=true"
TMSNAME = TMS_ORA
TMSCOUNT = 2
For more information about the XA parameter, see the "A Oracle XA" chapter in the Fundamentals section of Oracle Corporation's Oracle8i Application Developer's Guide.
Note: For single-threaded JavaServerXA operation, skip this step.
Listing 10-5 shows an example of JavaServerXA configured for multithreading in a sample UBBCONFIG .
Listing 10-5 Multithreaded Server Configuration in Sample UBBCONFIG
*SERVERS
DEFAULT:
RESTART = Y
MAXGEN = 5
...
JavaServerXA
SRVGRP = BANK_GROUP1
SRVID = 2
SRVTYPE = JAVA
CLOPT = "-A -- -M 10 BankApp.jar TellerFactory_1 bank_pool"
RESTART = N
To specify connection pooling, you need to specify SRVTYPE=JAVA in the SERVERS section.
Listing 10-6 JDBC Connection Pool Settings in Sample UBBCONFIG
*JDBCCONNPOOLS
bank_pool
SRVGRP = BANK_GROUP1
SRVID = 2
DRIVER = "weblogic.jdbc20.oci815.Driver"
URL = "jdbc:weblogic:oracle:beq-local"
PROPS = "user=scott;password=tiger;server=Beq-Local"
ENABLEXA = Y
INITCAPACITY = 2
MAXCAPACITY = 10
CAPACITYINCR = 1
CREATEONSTARTUP = Y
This topic includes the following sections:
In addition to the fully supported examples supplied on the CD-ROM with this release of WLE, the BEA WLE team provides several unsupported code examples on a password protected web site for WLE customers. The code samples in this topic come from a version of the WLE XA Bankapp sample application that is available from the unsupported samples WLE web site. The URL for the unsupported samples WLE web site is specified in the product Release Notes under "About This BEA WLE Release" in the subsection "Unsupported Samples and Tools Web Page."
This application is different from the one described in Transactions Sample CORBA Java/C++ XA Application.
Note:
This topic does not attempt to fully describe this sample application. It merely uses code fragments to illustrate the use of the JDBC/XA driver in a CORBA application.
Listing 10-7 shows the packages that the application imports. In particular, note that:
Importing Packages
Listing 10-7 Importing Required Packages
import java.sql.*;
import javax.sql.*;
import javax.naming.*;
import com.beasys.Tobj.*;
Listing 10-8 shows initializing the TransactionCurrent
object reference, which will be used by the Teller
operations to start and stop transactions.
Listing 10-8
Initializing the TransactionCurrent
Object Reference
static org.omg.CosTransactions.Current trans_cur_ref; org.omg.CORBA.Object trans_cur_oref = TP.bootstrap().resolve_initial_references("TransactionCurrent"); Listing 10-9 shows finding the connection pool via JNDI. The connection pool name is registered on the server group and is passed in as a command-line parameter upon server startup. Subsequent database connections are obtained from this pool.
Listing 10-9
Finding the Connection Pool via JNDI
static DataSource pool; ... public void get_connpool(String pool_name) Listing 10-10 shows setting up XA distributed transactions by calling the open_xa_rm
method (in server.initialize
) and obtaining a reference to the TransactionCurrent
object.
Note:
This step is required for CORBA applications but not for EJB or RMI applications.
Listing 10-10
Setting Up XA Distributed Transactions
TP.open_xa_rm(); Listing 10-11 shows a complete distributed transaction that involves the transfer of money from one bank account to another.
The application performs the distributed application in the following sequence:
Initializing the TransactionCurrent Object Reference
Finding the Connection Pool via JNDI
throws Exception
{
try {
javax.naming.Context ctx = new InitialContext();
pool = (DataSource)ctx.lookup("jdbc/" + pool_name);
}
catch (javax.naming.NamingException ex){
TP.userlog("Couldn't obtain JDBC connection pool: " + pool_name);
throw ex;
}
}
} Setting Up XA Distributed Transactions
org.omg.CORBA.Object trans_cur_oref = TP.bootstrap().resolve_initial_references("TransactionCurrent");
trans_cur_ref = org.omg.CosTransactions.CurrentHelper.narrow(trans_cur_oref); Performing a Distributed Transaction
Sequence of Tasks
Listing 10-11 Performing a Distributed Transaction
public void transfer(int fromAccountID, int toAccountID, float amount, BalanceAmountsHolder balances)
throws AccountRecordNotFound, IOException, InsufficientFunds
{
boolean success = false;
try {
// Increment the number of requests the teller has received.
tellerStats.totalTellerRequests += 1;
// Begin the global transaction.
BankAppServerImpl.trans_cur_ref.begin();
// Flag this as a transfer.
transferInProgress = true;
// Perform the withdrawal first.
float withdrawalBalance = withdraw(fromAccountID, amount);
// Perform the deposit next.
float depositBalance = deposit(toAccountID, amount);
balances.value = new BalanceAmounts();
balances.value.fromAccount = withdrawalBalance;
balances.value.toAccount = depositBalance;
success = true;
// Catch any exceptions thrown during database operations
}
catch (AccountRecordNotFound e) {
throw e;
}
catch (InsufficientFunds e) {
throw e;
}
catch (IOException e) {
throw e;
}
catch(Exception e) {
TP.userlog("Exception caught in transfer(): "
+ e.getMessage());
e.printStackTrace();
throw new org.omg.CORBA.INTERNAL();
}
finally {
try {
// Complete the distributed transaction and
// update the Teller statistics.
if (success) {
tellerStats.totalTellerSuccess += 1;
BankAppServerImpl.trans_cur_ref.commit(true);
} else {
tellerStats.totalTellerFail += 1;
BankAppServerImpl.trans_cur_ref.rollback();
}
}
catch(Exception e) {
TP.userlog("Unexpected Exception thrown during commit or rollback: " + e.getMessage());
e.printStackTrace();
throw new org.omg.CORBA.INTERNAL();
}
transferInProgress = false;
}
}
Listing 10-12 shows the withdraw method that is invoked in Listing 10-11. The withdraw method shows accessing the database to withdraw money from the specified account.
Listing 10-12 withdraw method
public float withdraw(int accountID, float amount)
throws AccountRecordNotFound,
IOException,
InsufficientFunds,
TellerInsufficientFunds
{
boolean success = false;
try {
if (!transferInProgress) {
// This is just a plain withdrawal; it is NOT a transfer.
// Increment the number of requests that this teller
// has received.
tellerStats.totalTellerRequests += 1;
// Decrement the balance left in the Teller's ATM machine.
tellerStats.totalTellerBalance -= amount;
// Begin the global transaction.
BankAppServerImpl.trans_cur_ref.begin();
// Check to see if the minimum TELLER threshold balance
// has not been reached; if so, amount will be added back in
// in the finally clause.
if (tellerStats.totalTellerBalance < MinTellerBalance)
throw new TellerInsufficientFunds();
}
AccountDataHolder accountDataHolder =
new AccountDataHolder(new AccountData());
accountDataHolder.value.accountID = accountID;
accountDataHolder.value.balance = -amount;
// Withdraw the money from the account.
theDBAccess_ref.update_account(accountDataHolder);
success = true;
return(accountDataHolder.value.balance);
}
catch (AccountRecordNotFound e) {
throw e;
}
catch (InsufficientFunds e) {
throw e;
}
catch (TellerInsufficientFunds e) {
throw e;
}
catch (DataBaseException e) {
throw new IOException();
}
catch(Exception e) {
TP.userlog("Exception caught in withdraw(): "
+ e.getMessage());
e.printStackTrace();
throw new org.omg.CORBA.INTERNAL();
}
finally {
// Terminate the transaction and update the Teller statistics.
if (!transferInProgress) {
try {
if (success) {
tellerStats.totalTellerSuccess += 1;
BankAppServerImpl.trans_cur_ref.commit(true);
} else {
tellerStats.totalTellerFail += 1;
tellerStats.totalTellerBalance += amount;
BankAppServerImpl.trans_cur_ref.rollback();
}
}
catch(Exception e) {
TP.userlog("Unexpected Exception thrown during commit or rollback: " + e.getMessage());
e.printStackTrace();
throw new org.omg.CORBA.INTERNAL();
}
}
}
}
Listing 10-13 shows the deposit method that is invoked in Listing 10-11. The deposit method shows accessing the database deposit money to the specified account.
Listing 10-13 deposit method
public float deposit(int accountID, float amount)
throws AccountRecordNotFound, IOException
{
boolean success = false;
try {
// If this is a transfer request, then the global transaction
// was started in the TellerImpl.transfer method; otherwise,
// start the transaction here.
if (!transferInProgress) {
// This is just a plain deposit; it is NOT a transfer.
// Increment the number of requests that this teller
// has received.
tellerStats.totalTellerRequests += 1;
// Begin the global transaction.
BankAppServerImpl.trans_cur_ref.begin();
}
AccountDataHolder accountDataHolder =
new AccountDataHolder(new AccountData());
accountDataHolder.value.accountID = accountID;
accountDataHolder.value.balance = amount;
// Deposit the money in the account.
theDBAccess_ref.update_account(accountDataHolder);
success = true;
return(accountDataHolder.value.balance);
}
catch (AccountRecordNotFound e) {
throw e;
}
catch (DataBaseException e) {
throw new IOException();
}
catch(Exception e) {
TP.userlog("Exception caught in BankApp.deposit(): "
+ e.getMessage());
e.printStackTrace();
throw new org.omg.CORBA.INTERNAL();
}
finally {
try {
// Terminate the transaction and update the Teller statistics.
if (!transferInProgress) {
if (success) {
tellerStats.totalTellerSuccess += 1;
BankAppServerImpl.trans_cur_ref.commit(true);
} else {
tellerStats.totalTellerFail += 1;
BankAppServerImpl.trans_cur_ref.rollback();
}
}
}
catch(Exception e) {
TP.userlog("Unexpected Exception thrown during commit or rollback: "
+ e.getMessage());
e.printStackTrace();
throw new org.omg.CORBA.INTERNAL();
}
}
}
|
Copyright © 1999 BEA Systems, Inc. All rights reserved.
|