/*
* @author Stephen Raj
* @version 1.0
*
* Development Environment : JDeveloper 2.0
* Name of the Application : DistributedTransactionServlet.java
* Creation/Modification History :
*
* sarokias.in 20-Jan-2000 Created
* Reghu 15-May-2002 Certified on Oracle9i JDeveloper
* Savitha 03-Feb-2003 Certified on Linux
* Removed System.out.println and used
* ServletContext to log error messages.
*/
package oracle.otnsamples.jdbc.dtran;
// Java IO support classes
import java.io.IOException;
import java.io.PrintWriter;
// Package for JDBC related Classes
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
// Java utility classes
import java.util.Enumeration;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.Vector;
// Java servlet APIs
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
// JTA APIs
import javax.sql.XAConnection;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
// Oracle JDBC Transaction APIs
import oracle.jdbc.xa.OracleXid;
import oracle.jdbc.xa.client.OracleXADataSource;
/**
* Support for distributed transactions demands a JDBC driver to support the
* standard two-phase commit protocol used by the Java Transaction API (JTA).
* To demonstrate Distributed Transactions Support, the Sample uses the
* following scenario. In this Sample, we assume that apart from 'TRAVEL'
* user, there exists another user namely 'GLOBAL'. This user may exist in the
* same database instance or some other database instance. We assume that
* there exists a table called 'OTN_EXCHANGE_RATES' in 'GLOBAL' user. The
* table 'OTN_EXCHANGE_RATES' keeps track of currency exchange rates between
* two countries. This servlet 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.
*/
public class DistributedTransactionServlet extends HttpServlet {
/** ServletContext object to log errors. **/ private ServletContext context = null;
/** Exchange Rate **/ private String rate = null;
/** Servlet Path **/ private String servletPath = null;
/** Vector for getting the values from table. **/ private Vector tableVector = null;
/** Transaction connection object **/ private XAConnection xaConnectionGlobal;
/** Distributed Transaction Connection Pool Objects. **/ private XAConnection xaConnectionTravel;
/**
* This method is called when the servlet is called for the first time. This
* method acts as the initializer for the servlet. Database connection
* object is initialized and country tables are displayed.
*/
public void init( ServletConfig config ) throws ServletException {
super.init( config ); // Call Super Class init() method.
// Establish database connection.
boolean status = this.createConnection( );
if ( status == true ) {
tableVector = this.populateTable( ); // Populate the table.
}
context = config.getServletContext( ); // get ServletContext
}
/**
* This method is called whenever the servlet is invoked. The value of
* parameter 'REQ_TYPE' is checked and depending on it's value, different
* methods are called which return HTML page in form of a String that is
* displayed.
*/
public void service( HttpServletRequest req, HttpServletResponse res )
throws ServletException, IOException {
res.setHeader( "pragma", "no-cache" ); // Disable cache.
res.setContentType( "text/html" ); // Set content type to HTML.
// Get the output stream.
PrintWriter out = new PrintWriter( res.getOutputStream( ) );
// get the value of REQ_TYPE.
String reqType = req.getParameter( "REQ_TYPE" );
// If REQ_TYPE is null display the main page.
if ( reqType == null ) {
out.println( DistributedTransactionHTML.generateMainPageHTML( ) );
}
// If REQ_TYPE is HOME, show the countries as Home countries from the
// OTN_COUNTRIES table present in the the user TRAVEL.
else if ( reqType.equals( "HOME" ) ) {
out.println( DistributedTransactionHTML.generateTableFrameHTML(
tableVector,
servletPath,
"Home Country" ) );
}
// If REQ_TYPE is DESTINATION, show the countries as Destination countries
// from OTN_COUNTRIES table present in the user TRAVEL.
else if ( reqType.equals( "DESTINATION" ) ) {
out.println( DistributedTransactionHTML.generateTableFrameHTML(
tableVector,
servletPath,
"Destination Country" ) );
}
// If REQ_TYPE is BUTTON display the various buttons.
else if ( reqType.equals( "BUTTON" ) ) {
out.println( DistributedTransactionHTML.generateButtonFrameHTML( ) );
}
// If REQ_TYPE is INPUT display the text box to show and
// get Exchange rate.
else if ( reqType.equals( "INPUT" ) ) {
out.println( DistributedTransactionHTML.generateInputFrameHTML( "" ) );
}
// If REQ_TYPE is UPDATE update the Exchange rates.
else if ( reqType.equals( "UPDATE" ) ) {
// Get Home country ID.
int hotelID = Integer.parseInt( req.getParameter( "HOME_ID" ) );
// Get Destination Country ID.
int destinationID =
Integer.parseInt( req.getParameter( "DESTINATION_ID" ) );
// Get Exchange rate.
float value = Float.parseFloat( req.getParameter( "VALUE" ) );
// Call update method.
this.updateTables( hotelID, destinationID, value );
this.getExchangeRate( hotelID, destinationID ); // Get Exchange rates.
// Display new Exchange rate.
out.println( DistributedTransactionHTML.generateInputFrameHTML(rate));
}
// If REQ_TPYE is INPUTUPDATE, display the Exchange rate.
else if ( reqType.equals( "INPUTUPDATE" ) ) {
// Get Home country ID.
int hotelID = Integer.parseInt( req.getParameter( "HOME_ID" ) );
// Get Destination Country ID.
int destinationID =
Integer.parseInt( req.getParameter( "DESTINATION_ID" ) );
this.getExchangeRate( hotelID, destinationID ); // Get Exchange rates.
// Display new Exchange rate.
out.println( DistributedTransactionHTML.generateInputFrameHTML( rate ));
}
out.close( ); // Close the output stream.
}
/**
* This method reads the properties file Connection.properties which is
* passed as the parameter and loads it into the java Properties object and
* returns the properties object.
*/
private static Properties loadParams( String file )
throws IOException {
// Loads a ResourceBundle and creates Properties from it.
Properties prop = new Properties( );
ResourceBundle bundle = ResourceBundle.getBundle( file );
Enumeration enum = bundle.getKeys( );
String key = null;
while ( enum.hasMoreElements( ) ) {
key = (String) enum.nextElement( );
prop.put( key, bundle.getObject( key ) );
}
return prop;
}
/**
* This method creates database connection pool objects that can participate
* in Distributed Transaction using JDBC 2.0.
*/
private boolean createConnection( ) {
boolean status = false;
try {
// Load the properties file to get the connection information.
Properties prop = this.loadParams( "Connection" );
// Create a OracleConnectionPoolDataSource instance.
OracleXADataSource oxadsTravel = new OracleXADataSource( );
OracleXADataSource oxadsGlobal = new OracleXADataSource( );
// Set connection parameters.
String urlTravel = "jdbc:oracle:thin:@" +
(String) prop.get( "HostName1" ) + ":" +
(String) prop.get( "Port1" ) + ":" +
(String) prop.get( "SID1" );
// Set connection parameters.
String urlGlobal = "jdbc:oracle:thin:@" +
(String) prop.get( "HostName2" ) + ":" +
(String) prop.get( "Port2" ) + ":" +
(String) prop.get( "SID2" );
// Set the URL.
oxadsTravel.setURL( urlTravel );
oxadsGlobal.setURL( urlGlobal );
// Create the connection pools.
xaConnectionTravel =
oxadsTravel.getXAConnection((String) prop.get( "UserName1" ),
(String) prop.get( "Password1" ));
xaConnectionGlobal =
oxadsGlobal.getXAConnection( (String) prop.get( "UserName2" ),
(String) prop.get( "Password2" ) );
if ( ( xaConnectionTravel == null ) || ( xaConnectionGlobal == null )){
status = false;
}
else {
status = true;
}
}
catch ( SQLException ex ) { // Catch SQL errors.
context.log( ex.toString( ) );
}
catch ( IOException ex ) { // Catch IO errors
context.log( ex.toString( ) );
}
return status;
}
/**
* This method returns a Transaction ID for the Transaction. The transaction
* ID contains 2 parts, a global Transaction ID set to 9 in this method and
* a Branch ID that is unique to each branch set to the value passed as
* parameter.
*/
private 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;
}
/**
* This method retrieves all the rows from the otn_countries table and
* populates the Vector with all the hotel details. This Vector will be
* used to display main HTML page.
*/
private Vector populateTable( ) {
Vector hotelDetails = new Vector( ); // Get new vector.
Connection connection = null; // Connection used
try {
// Get a connection from the pooled Transaction connection object.
connection = xaConnectionTravel.getConnection( );
// Statement Context to execute SQL query.
Statement statement = connection.createStatement( );
// Retrieve all rows from the otn_countries table into a ResultSet.
ResultSet resultSet = statement.executeQuery(
"SELECT id,name,currency FROM otn_countries ORDER BY id" );
// Loop through the result-set, obtain column values and add to Vector.
while ( resultSet.next( ) ) {
Vector hotel = new Vector( );
hotel.addElement( resultSet.getString( 1 ) ); // Hotel ID
hotel.addElement( resultSet.getString( 2 ) ); // Hotel Name
hotel.addElement( resultSet.getString( 3 ) ); // Hotel Address
hotelDetails.addElement( hotel ); // Add to vector
}
statement.close( ); // Close the statement.
}
catch ( SQLException ex ) { // Catch SQL Errors.
context.log( ex.toString( ) );
}
finally { // return the connection object to the pool.
if ( connection != null ) {
try {
connection.close( );
}
catch ( SQLException e ) { // Catch SQL Errors.
context.log( e.toString( ) );
}
}
}
return hotelDetails; // Return the Vector.
}
/**
* This method returns the Exchange rate. This method gets the home country
* ID destination country ID and queries the OTN_EXCHANGE_RATES table in the
* TRAVEL user to return the exchange rate.
*/
private void getExchangeRate( int homeID, int destinationID ) {
Connection connection = null; // connection Object
try {
connection = xaConnectionTravel.getConnection( ); // Get connection
// Prepare statement for query.
Statement stmt = connection.createStatement( );
// Execute Query.
ResultSet rs = stmt.executeQuery( "SELECT rate FROM otn_exchange_rates " +"WHERE home_con_id=" + homeID
+" AND new_con_id=" + destinationID );
if ( rs.next( ) ) {
rate = rs.getString( 1 ); // Get Exchange rate from Result Set.
}
}
catch ( SQLException ex ) { // Catch SQL Errors.
context.log( ex.toString( ) );
}
finally { // return the connection object to the pool.
if ( connection != null ) {
try {
connection.close( );
}
catch ( SQLException e ) { // Catch SQL Errors.
context.log( e.toString( ) );
}
}
}
}
/**
* This method updates the OTN_EXCHANGE_RATES table in TRAVEL user and in
* GLOBAL user. If any one of the tables fails to get updated, then the
* database rolls back to the previous state.
*/
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 ( !( ( 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
context.log( ex.toString( ) );
}
catch ( XAException xae ) { // Catch Transaction Exception
context.log( xae.toString( ) );
}
finally { // return the connection objects to the pool
if ( connectionTravel != null ) {
try {
connectionTravel.close( );
}
catch ( SQLException e ) { // Catch SQL Errors
context.log( e.toString( ) );
}
}
if ( connectionGlobal != null ) {
try {
connectionGlobal.close( );
}
catch ( SQLException e ) { // Catch SQL Errors
context.log( e.toString( ) );
}
}
}
}
}

E-mail this page
Printer View Printer View
Oracle Is The Information Company About Oracle | Oracle RSS Feeds | Careers | Contact Us | Site Maps | Legal Notices | Terms of Use | Privacy