Distributed Transaction Sample
Application
Table Of Contents
A distributed transaction, sometimes referred
to as a global transaction, is a set of two or more related transactions
that must be managed in a co-ordinated way. The transactions that constitute
a distributed transaction might be in the same database, but more typically
are in different databases and often in different locations. Each individual
transaction of a distributed transaction is referred to as a transaction
branch.
Support for distributed transactions demands a JDBC driver to support
the standard two-phase commit protocol used by the Java Transaction API
(JTA).
Working of the Sample application
To demonstrate Distributed Transactions Support, the
scenario used by this sample application is described below.
The sample application uses two database users, say, TRAVEL
and GLOBAL. These users may be on two different
database instances or in the same database instance. You can change the
username in Connection.properties file found
under the top level directory, to match your database users. Look into
Description of Sample Files section for folder
and file details.
The user schema has two tables: OTN_COUNTRIES
which stores list of countries and its details; OTN_EXCHANGE_RATES
which keeps track of currency exchange rates between any two countries
stored in the OTN_COUNTRIES table. OTN_EXCHANGE_RATES
exists in both user schemas.
The sample application implements a servlet which allows the user to update
exchange rates between two different countries. As the table OTN_EXCHANGE_RATES
exists in two different users in two different database instances, the
updation of Exchange Rates forms a distributed transaction. In short,
this means either the new rates should get reflected in both the tables
or not at all.
When the servlet is loaded and invoked, it creates two separate connection
pools. First connection pool corresponds to TRAVEL user transaction branch
whereas second connection pool corresponds to GLOBAL user transaction
branch. These connection pools are used by the servlet to get database
connections and for further processing. The status and errors, if any,
will be displayed in the standard output. When the servlet gets a request
from the user it will return a page with all the Home Countries in one
table and Destination Countries in another table. It gets the connection
from the available pool to connect to the database. The user has to select
a Home Country and a Destination Country and can view the current Exchange
rate or update the Exchange rate.
Here is the code sample for using Transaction APIs. You can find more
details of the code in DistributedTransactionServlet.java
file under src/oracle/otnsamples/jdbc/dtran
folder. Look into Description of Sample Files
section for folder and file details.
private void updateTables(int homeID,int destinationID,float exchangeRate) {
Connection connectionTravel = null; // Connection to TRAVEL user
Connection connectionGlobal = null; // Connection to GLOBAL user
try {
// Get the database connection from Connection Pool
connectionTravel = xaConnectionTravel.getConnection();
connectionGlobal = xaConnectionGlobal.getConnection();
// Get the Transaction Resources
XAResource xaResourceTravel = xaConnectionTravel.getXAResource();
XAResource xaResourceGlobal = xaConnectionGlobal.getXAResource();
// Create the Transaction IDs
Xid xidTravel = createXid(1);
Xid xidGlobal = createXid(2);
// Start transaction
xaResourceTravel.start (xidTravel, XAResource.TMNOFLAGS);
xaResourceGlobal.start (xidGlobal, XAResource.TMNOFLAGS);
// Create Statements for updating
Statement stmtTravel = connectionTravel.createStatement();
Statement stmtGlobal = connectionGlobal.createStatement();
// Execute the Update Statements
int no = stmtTravel.executeUpdate("UPDATE otn_exchange_rates SET rate="+exchangeRate+
" WHERE home_con_id="+homeID+" AND new_con_id=" + destinationID);
no = stmtGlobal.executeUpdate("UPDATE otn_exchange_rates SET rate="+
exchangeRate+" WHERE home_con_id="+homeID+" AND new_con_id="
+ destinationID);
// Suspend the transactions
xaResourceTravel.end(xidTravel, XAResource.TMSUCCESS);
xaResourceGlobal.end(xidGlobal, XAResource.TMSUCCESS);
// Prepare the Resource Managers
int prepareGlobal = xaResourceGlobal.prepare (xidGlobal);
int prepareTravel = xaResourceTravel.prepare (xidTravel);
boolean doCommit = true; // Boolean to check whether to commit or not
// If both the branches are to the same Resource Manager, oracle does some
// optimization. All but one branch return XA_RDONLY. Only one branch will
// return XA_OK or failure. Commit or Rollback has to be called on this
// branch only accordingly.
if (!((prepareTravel == XAResource.XA_OK)||(prepareTravel==XAResource.XA_RDONLY)))
doCommit = false;
if (!((prepareGlobal == XAResource.XA_OK)||(prepareGlobal==XAResource.XA_RDONLY)))
doCommit = false;
if (prepareTravel == XAResource.XA_OK)
if (doCommit)
xaResourceTravel.commit(xidTravel, false);
else
xaResourceTravel.rollback(xidTravel);
if (prepareGlobal == XAResource.XA_OK)
if (doCommit)
xaResourceGlobal.commit(xidGlobal, false);
else
xaResourceGlobal.rollback(xidGlobal);
// Close the Statements
stmtTravel.close();
stmtGlobal.close();
} catch(SQLException ex) { // Catch SQL Errors
......
} catch (XAException xae) { // Catch Transaction Exception
.....
} finally { // return the connection objects to the pool
if (connectionTravel != null)
try {
connectionTravel.close();
} catch(SQLException e) { // Catch SQL Errors
......
}
if (connectionGlobal != null)
try {
connectionGlobal.close();
} catch(SQLException e) { // Catch SQL Errors
......
}
}
}
/**
* This method returns a Transaction ID for the Transaction. The transaction
* ID contains 2 parts, a global Transaction ID which is set to 9 in this
* method and a Branch ID that is unique to each branch which is set to the
* value passed as parameter.
*/
public static Xid createXid(int pbID) throws XAException {
byte[] gID = new byte[1]; // Global ID
gID[0] = (byte) 9; // Set Global Id to 9
byte[] bID = new byte[1]; // Branch ID
bID[0] = (byte) pbID; // Set branch ID to the parameter passed
byte[] globalID = new byte[64];
byte[] branchID = new byte[64];
// Copy the Global ID and branch ID to a 64 bit string
System.arraycopy (gID, 0, globalID, 0, 1);
System.arraycopy (bID, 0, branchID, 0, 1);
// Call OracleXid() to generate the Xid
Xid xid = new OracleXid(0x1234, globalID, branchID);
// Return the Transaction ID
return xid;
}
|
- Oracle9i JDeveloper
( Note: Oracle9i JDeveloper is
Oracle's Visual Java Development Tool and can be downloaded from
here )
or JDK1.2.x or above This can be downloaded from
here.
- Oracle9i Database
or higher running SQL*Net TCP/IP listener. This can be downloaded from
here.
- Oracle9i JDBC Driver.
This can be downloaded from
here.
- Oracle9i Containers
for J2EE - OC4J Release 9.0.2. This can be downloaded from
here.
- Ant project build tool. This can be downloaded from
here.
| Notation |
Description
|
|
<OC4J_HOME>
|
points to the directory where
oc4j is installed . For example, D:\oc4j
|
|
<JAVA_HOME>
|
points to the directory where
JDK1.2 or higher is installed. For example, D:\jdk1.3.1
|
|
<HOST_NAME>
|
points
to the hostname name where oc4j is running. For example, incq207a.idc.oracle.com
|
|
<UID>
|
is the oc4j admin username; default
is: admin
|
|
<ADMIN_PASSWORD>
|
Password of oc4j admin user.
|
|
<PORT>
|
Port number where OC4J is running,
default is: 8888
|
|
<ORACLE_HOME>
|
points to the directory where
Oracle9i database is installed.
For example, d:\oracle9i
|
|
<JDEV_HOME>
|
points to the directory where
Oracle9i JDeveloper is installed.
For example: d:\Jdev9i
|
- Unjar the provided DistributedTransaction.jar
using the following command.
> jar xvf DistributedTransaction.jar
Note: You will
find jar.exe in JDK_HOME\bin. Ensure JDK_HOME\bin is present in your
system path.
(JDK_HOME is the root directory of the JDKx.x installation). This creates
a folder DistributedTransaction with all the source
files.
- Edit DistributedTransaction/Connection.properties
file in your favorite editor. The application uses two usernames and
related database details. Provide these values by changing the HostName(n),Port(n),SID(n),UserName(n)
and
Password(n) to connect
to your own database instances where n
is either 1 or 2 mentioned below.
| HostName1 |
= |
incq212e.idc.oracle.com |
| SID1 |
= |
otn9i |
| Port1 |
= |
1521 |
| UserName1 |
= |
travel |
| Password1 |
= |
travel |
| HostName2 |
= |
insn104a.idc.oracle.com |
| SID2 |
= |
ora9idb |
| Port2 |
= |
1522 |
| UserName2 |
= |
global |
| Password2 |
= |
global |
The application requires 2 users who could be in the
same database or in two different databases. Depending on the credentials
provided in Connection.properties
file , UserName1, for example 'travel' requires two tables viz.. OTN_COUNTRIES
and OTN_EXCHANGE_RATES. UserName2, for example
'global' requires only the common table OTN_EXCHANGE_RATES.
Run the SQL Script file DistributedTransaction.sql
to create the database tables and records required by the application.
Look into Description of
Sample Files section for folder and file details of the SQL
file.
For example: SQL>@D:\DistributedTransaction\config\DistributedTransaction.sql
Variables prompted for values when SQL script is run are:
| username1 |
= |
username used in Connection.properties
file corresponding to UserName1. |
| password1 |
= |
password used in Connection.properties
file corresponding to Password1. |
| tnsname1 |
= |
tnsname for the database corresponding to HostName1 in Connection.properties. |
| username2 |
= |
username used in Connection.properties
file corresponding to UserName2. |
| password2 |
= |
password used in Connection.properties
file corresponding to Password2. |
| tnsname2 |
= |
tnsname for the database corresponding to HostName2 in Connection.properties. |
This sample application can be run in 2 different ways
listed below.
From Oracle9i
JDeveloper
- Open Oracle9i
JDeveloper and use File/Open option to select the
DistributedTransaction.jws
from the DistributedTransaction
directory.
- Next, select Project/DistributedTransaction.jpr
from main menu.
- Now, select Run/Run
DistributedTransaction.jpr
from main menu to run the application.
From Stand alone OC4J:
The application can be deployed and run from stand
alone OC4J:
On
Windows:
This section will describe steps to run the application
from Windows NT. The sample can be run either manually
or using a script file .
Deploy and run application
using batch file: run.bat
provided:
By setting few environment variables, the sample
application could be deployed by just executing the batch file: run.bat
from the command prompt, from DistributedTransaction
directory. Environmental variables like
JAVA_HOME, OC4J_HOME, HOST_NAME, ADMIN_PASSWORD have
to be set before running run.bat
file. Look at Notations section for more details
on these variables.
Note: Make sure that OC4J is already running.
Example:
D:\DistributedTransaction> set OC4J_HOME=d:\oc4j
D:\DistributedTransaction> set ANT_HOME=d:\ant\jarkarta-ant-1.4.1
D:\DistributedTransaction> set JAVA_HOME=d:\jdk1.3.1
D:\DistributedTransaction> set HOST_NAME=incq207a.idc.oracle.com
D:\DistributedTransaction> set ADMIN_PASSWORD=welcome
D:\DistributedTransaction> run
Access the application using the following url from
any browser:
http://<HOST_NAME>:<PORT>/dtrans/DistributedTransactionServlet
Example: http://incq207a.idc.oracle.com:8888/dtrans/DistributedTransactionServlet
Run application manually
from Windows:
- Step 1: Set the following environmental variables:
- PATH pointing to:
- the directory where JDK 1.2 or higher
is installed.
- the directory where the project build tool
ant is installed.
Example: D:\DistributedTransaction>
set PATH=d:\jdk1.3.1\bin;d:\ant\jakarta-ant-1.4.1\bin;%PATH%
- OC4J_HOME pointing to the directory where oc4j
is installed. See Notations for more details.
Example: D:\DistributedTransaction>
set OC4J_HOME=d:\oc4j
- Step 2: From the directory where sample is extracted
(example: D:\DistributedTransaction),
run the ant utility.
Example: D:\DistributedTransaction>
ant
This will create dtrans.war
and transaction.ear
files.
- Step 3: Start OC4J from <oc4j_home>\j2ee\home
directory.
Example: D:\oc4j\j2ee\home>
java -jar oc4j.jar
- Step 4: From the directory where sample is extracted
(example: D:\DistributedTransaction
), deploy the ear files.
java -jar <OC4J_HOME>\j2ee\home\admin.jar
ormi:\\<HOST_NAME> <UID> <ADMIN_PASSWORD> -deploy
-file transaction.ear -deploymentName transaction
Example: D:\oc4j\j2ee\home>
java -jar d:\oc4j\j2ee\home\admin.jar ormi://incq207a.idc.oracle.com
admin welcome -deploy -file transaction.ear -deploymentName transaction
- Step 5: Bind the web application to a context root
using from the same directory as above step 4.
java -jar <OC4J_HOME>\j2ee\home\admin.jar
ormi://<HOST_NAME> <UID><ADMIN_PASSWORD> -bindWebApp
transaction dtrans http-web-site dtrans
Example: D:\oc4j\j2ee\home>java
-jar d:\oc4j\j2ee\home\admin.jar ormi://incq207a.idc.oracle.com
admin welcome -bindWebApp transaction dtrans http-web-site dtrans
- Step 6: Access the application using the following
url from any browser:
http://<HOST_NAME>:<PORT>/dtrans/DistributedTransactionServlet
Example: http://incq207a.idc.oracle.com:8888/dtrans/DistributedTransactionServlet
On
Linux:
This section will describe steps to run the application
from console using JDK on Red Hat Linux Advanced Server Release 2.1.
The sample can be run either manually or using a
script file .
Deploy and run application
using script file: run.sh
provided:
By setting few environment variables, the sample
application could be deployed by just executing the batch file: run.sh
from the command prompt, from DistributedTransaction
directory. User will be prompted to enter
value for the environmental variables required to run the script.
Note: Make sure that OC4J is already running.
- Go to DistributedTransaction
directory and from the $
prompt use the command below to give execute permission to the file.
$chmod 777 run.sh
- Now run the file:
$run.sh
Access the application using the following url
from any browser:
http://<HOST_NAME>:<PORT>/dtrans/DistributedTransactionServlet
Example: http://incq207a.idc.oracle.com:8888/dtrans/DistributedTransactionServlet
Running the application
manually:
- Step 1: Set the following environmental variables:
- PATH pointing to:
- the directory where JDK 1.2 or higher is installed.
- the directory where the project build tool
ant is installed.
Example: $ export PATH=/home1/java/jdk1.3.1/bin:/home1/ant/bin:$PATH
- OC4J_HOME pointing to the directory where oc4j
is installed. See Notations for more details.
Example: $ export OC4J_HOME=/home1/oc4j
- JAVA_HOME pointing to the directory where JDK
1.2 or higher is installed.
Example: $ export JAVA_HOME=/home1/java/jdk1.3.1
- Step 2: From the directory where sample is extracted
(example /home1/jdbc/DistributedTransaction),
run the ant utility.
Example: $ ant
This will create dtrans.war
and transaction.ear
files.
- Step 3: Start OC4J from <oc4j_home>/j2ee/home
directory.
Example: $ java -jar
oc4j.jar
- Step 4: From the directory where sample is extracted
(example /home1/jdbc/DistributedTransaction),
deploy the ear files.
java -jar <OC4J_HOME>/j2ee/home/admin.jar
ormi://<HOST_NAME> <UID> <ADMIN_PASSWORD> -deploy
-file transaction.ear -deploymentName transaction
Example: $java -jar
/home1/oc4j/j2ee/home/admin.jar ormi://incq207a.idc.oracle.com admin
welcome -deploy -file transaction.ear -deploymentName transaction
- Step 5: Bind the web application to a context root
using from the same directory as above step 4.
java -jar <OC4J_HOME>/j2ee/home/admin.jar
ormi://<HOST_NAME> <UID><ADMIN_PASSWORD> -bindWebApp
transaction dtrans http-web-site dtrans
Example: $java -jar
/home1/oc4j/j2ee/home/admin.jar ormi://incq207a.idc.oracle.com admin
welcome -bindWebApp transaction dtrans http-web-site dtrans
- Step 6: Access the application using the following
url from any browser:
http://<HOST_NAME>:<PORT>/dtrans/DistributedTransactionServlet
Example: http://incq207a.idc.oracle.com:8888/dtrans/DistributedTransactionServlet
The directory structure of the deliverable DistributedTransaction.jar
will be as shown below. DistributedTransaction
is the top level directory.
|
Directory
|
Files
|
Description
|
DistributedTransaction
|
DistributedTransaction.jws
|
The Oracle9i
JDeveloper workspace file. |
DistributedTransaction.jpr
|
The Oracle9i
JDeveloper project file. |
| Connection.properties |
This file has the details of the database
connection parameters. |
| build.xml |
Project build file used
by ANT tool to create transaction.ear file
which is deployed to stand alone OC4J. |
| run.bat |
The batch file to compile
and deploy the sample in Windows environment. |
| run.sh |
The shell script to compile
and deploy the sample in Linux environment. |
DistributedTransaction\doc
|
Readme.html |
This file. |
DistributedTransaction\config
|
DistributedTransaction.sql |
This is the SQL script file to create
the required tables in the database. |
DistributedTransaction\META-INF
|
application.xml |
Application level J2EE deployment
descriptor. |
DistributedTransaction\WEB-INF
|
web.xml |
Deployment descriptor for the web
application. |
| DistributedTransaction\src\oracle\otnsamples\jdbc\dtran |
DistributedTransactionServlet.java
|
The servlet source file for the sample. |
| DistributedTransactionHTML.java |
This class contains static
methods, which generate HTML pages for the Distributed Transaction
Sample. |
From Oracle9i
JDeveloper, if the update of the tables does not happen, follow the steps
below to re-configure Oracle9i JDeveloper
to use classes12.zip shipped with Oracle9i
database.
- Take a backup of the present Oracle9i
JDBC Driver (classes12dms.jar)
by renaming it as classes12dms_backup.jar
- Copy the classes12.jar
from the <ORACLE_HOME>/jdbc/lib directory into <JDEV_HOME>/jdbc/lib
- Rename the copied classes12.jar
as classes12dms.jar
to complete the configuration.
- Stop the embedded OC4J and re-run the application.
|