Oracle8i Enterprise JavaBeans Developer's Guide and Reference
Release 3 (8.1.7)

Part Number A83725-01

Library

Product

Contents

Index

Go to previous page Go to beginning of chapter Go to next page

JTA Client-Side Demarcation

For JTA, client-side demarcated transactions are explicitly demarcated programmatically through the UserTransaction object, which must be bound with the bindut command into the namespace. With client-side transaction demarcation, the client controls the transaction. The client starts a global transaction by invoking the UserTransaction begin method; it ends the transaction by invoking either the commit or rollback methods.

In addition, the client must always set up an environment including a Hashtable with authentication information and namespace location URL. It must also register an OracleDriver when retrieving the transaction objects from the namespace.

Figure 7-3 shows a client invoking a server object--which, in this example, is a container-managed transactional bean. The client starts a global transaction, then invokes the bean. Since the bean is a container-managed transactional bean and is specified with the transactional attribute of "Supports", the transactional context is propagated to include the server object.

Figure 7-3 Client Demarcated Global Transaction


The following must occur for the client to demarcate the transaction:

  1. Initialize a Hashtable environment with the namespace address and authentication information.

  2. Register the OracleDriver.

  3. Retrieve the UserTransaction object from the namespace within the client logic. When you retrieve the UserTransaction object from any client, the URL must consist of "jdbc_access://" prefix before the JNDI name.

  4. Start the global transaction within the client using UserTransaction.begin().

  5. Retrieve the server bean.

  6. Invoke any object methods to be included in the transaction.

  7. End the transaction through UserTransaction.commit() or UserTransaction.rollback().

Example 7-1 shows a client that invokes a server bean within the transaction.

Example 7-1 Employee Client Code for Client Demarcated Transaction

Before starting the client, you must first bind the UserTransaction object in the namespace.

Bind UserTransaction Object in the Namespace

You bind the UserTransaction object in the namespace through the bindut command of the sess_sh tool. To bind a UserTransaction object to the name "/test/myUT" in the namespace located on nsHost, execute the following:

sess_sh -service jdbc:oracle:thin:@nsHost:5521:ORCL -user SCOTT -password TIGER
& bindut /test/myUT

Verify that the user bound with the UserTransaction has FORCE ANY TRANSACTION granted to the user that bound this object. This privilege enables the user to commit this transaction. In this example, you would execute the following:

GRANT FORCE ANY TRANSACTION TO SCOTT


Note:

The client needs the same information to retrieve the UserTransaction as you give within the bindut command.  


Developing the Client Application

After binding the UserTransaction object, your client code can retrieve the UserTransaction object and start a global transaction. Since the client is retrieving the UserTransaction object from a remote site, the lookup requires authentication information, location of the namespace, the OracleDriver registration, and the "jdbc_access://" prefix.

import employee.Employee;
import employee.EmployeeHome;
import employee.EmployeeInfo;

import javax.naming.Context;
import javax.naming.InitialContext;
import java.util.Hashtable;
import javax.transaction.*;

public class Client
{
  public static void main (String[] args) throws Exception {

    //Set up the service URL to where the UserTransaction object 
    //is bound. Since from the client, the connection to the database
    //where the namespace is located can be communicated with over either
    //a Thin or OCI8 JDBC driver. This example uses a Thin JDBC driver.
    String namespaceURL = "jdbc:oracle:thin:@nsHost:1521:ORCL";
 
    //User and password are case sensitive.
    String user = "SCOTT";
    String password = "TIGER";

    //1.(a) Authenticate to the database.
    // create InitialContext and initialize for authenticating client
    Hashtable env = new Hashtable ();
    env.put (Context.URL_PKG_PREFIXES, "oracle.aurora.jndi");
    env.put (Context.SECURITY_PRINCIPAL, user);
    env.put (Context.SECURITY_CREDENTIALS, password);
    env.put (Context.SECURITY_AUTHENTICATION, ServiceCtx.NON_SSL_LOGIN);
    //1.(b) Specify the location of the namespace where the transaction objects
    //   are bound.
    env.put(jdbc_accessURLContextFactory.CONNECTION_URL_PROP, namespaceURL);
    Context ic = new InitialContext (env);

    //2. Register a JDBC OracleDriver. Requirement for opening JDBC connection
    // to retrieve UserTransaction object from namespace
    DriverManager.registerDriver (new oracle.jdbc.driver.OracleDriver());

    //3. Retrieve the UserTransaction object from JNDI namespace
    UserTransaction ut = (UserTransaction)ic.lookup ("jdbc_access://test/myUT");

    //4. Start the transaction
    ut.begin();

    //5. Retrieve the EJB 
    // get an handle to the employee_home object
    EmployeeHome employee_home =
      (EmployeeHome)ic.lookup ("sess_iiop://myhost:1521:orcl/test/employee");

    // get an handle to the remote bean
    Employee employee = employee_home.create ();

    //6. Perform bean business logic.
    // get an info of an employee
    EmployeeInfo info = employee.getEmployee ("SCOTT");
    System.out.println ("Beginning salary = " + info.salary);

    // do work on the info-object
    info.salary += (info.salary * 10) / 100;

    // call update on the server-side
    employee.updateEmployee (info);

    //7. End the transaction
    //Commit the updated value
    ut.commit();
  }
}


Example 7-2 Container-Managed Transactional Bean with Supports Attribute

In the EJB deployment descriptor, the container-managed transactional session bean is specified with the Supports attribute. These transactional specifications are defined in the <transaction-type> and <container-transaction> elements.

XML Deployment Descriptor
<?xml version="1.0"?>
<!DOCTYPE ejb-jar PUBLIC "-//Oracle Corporation//DTD Enterprise JavaBeans 1.1
//EN" "ejb-jar.dtd">
<ejb-jar>
   <enterprise-beans>
      <session>
         <ejb-name>test/myEmployee</ejb-name>
         <home>employee.EmployeeHome</home>
         <remote>employee.Employee</remote>
         <ejb-class>employeeServer.EmployeeBean</ejb-class>
         <session-type>Stateful</session-type>
         <transaction-type>Container</transaction-type>
      </session>
   </enterprise-beans>
   <assembly-descriptor>
     <container-transaction>
       <method>
         <ejb-name>EmployeeBean</ejb-name>
         <method-name>*</method-name>
       </method>
       <trans-attribute>Supports</trans-attribute>
     </container-transaction>
   </assembly-descriptor>
</ejb-jar>

Since the session bean allows the container to manage the transaction and the bean is specified as supporting existing transactions, the transaction context is propagated to the bean when the client invokes it. The bean itself contains no transactional logic within it. It only contains the bean implementation code, as follows:


Note:

The database that this bean resides on is automatically enlisted. Thus, any SQLJ statement is executed against the database and its changes are included in the global transaction. See "Local Database Enlistment" for full details on the automatic enlistment of a local database.  


package employeeServer;

import employee.*;

import javax.ejb.SessionBean;
import javax.ejb.CreateException;
import javax.ejb.SessionContext;
import java.rmi.RemoteException;

import java.sql.SQLException;

public class EmployeeBean implements SessionBean
{
  // Methods of the Employee interface
  public EmployeeInfo getEmployee (String name) 
throws RemoteException, SQLException { int empno = 0; double salary = 0.0; #sql { select empno, sal into :empno, :salary from emp where ename = :name }; return new EmployeeInfo (name, empno, salary); } public void updateEmployee (EmployeeInfo employee) throws RemoteException, SQLException { #sql { update emp set ename = :(employee.name), sal = :(employee.salary) where empno = :(employee.number) }; return; } // Methods of the SessionBean public void ejbCreate () throws RemoteException, CreateException {} public void ejbRemove () {} public void setSessionContext (SessionContext ctx) {} public void ejbActivate () {} public void ejbPassivate () {} }

JTA Client-Side Demarcation Including Databases

The previous example showed how a transaction context was propagated to server objects from a client within the JTA global transaction. When you execute the server object, the transaction is propagated over the IIOP transport layer. In addition to invoking IIOP server objects, you may wish to update databases over JDBC connections. This section shows how you enlist databases using a JDBC connection in tandem with the IIOP server object propagation.

To include a remote database within the transaction from a client, you must use a DataSource object, which has been bound in the namespace as a JTA DataSource. Then, invoke the getConnection method of the DataSource object after the transaction has started, and the database is included in the global transaction. See "Enlisting Resources" for more information.

The following must occur in the client runtime to demarcate the transaction:

  1. Initialize a Hashtable environment with the namespace address and authentication information.

  2. Register the OracleDriver.

  3. Retrieve the UserTransaction object from the namespace within the client logic. When you retrieve the UserTransaction object from the client, the URL must consist of "jdbc_access://" prefix before the JNDI name.

  4. Start the global transaction within the client using UserTransaction.begin().

  5. Enlist any database resources to be included in the transaction by opening a connection to the specified database, as follows:

    1. Retrieve the DataSource object from the namespace within the client logic. When you retrieve the DataSource object from any client, the URL must consist of "jdbc_access://" prefix before the JNDI name.

    2. Open a connection to the database through DataSource.getConnection method.

  6. Retrieve the bean reference.

  7. Invoke any object methods to be included in the transaction.

  8. Invoke SQL DML statements against any enlisted databases.

  9. End the transaction through UserTransaction.commit() or UserTransaction.rollback().

Example 7-3 shows a client that invokes a bean and enlists a single database within the transaction.

Example 7-3 Employee Client Code for Client Demarcated Transaction

Before starting the client, you must first bind the UserTransaction and DataSource objects in the namespace. See "Bind UserTransaction Object in the Namespace" for directions on the binding the UserTransaction object.

Bind DataSource Object in the Namespace

Use the bindds command of the sess_sh tool to bind an DataSource object in the namespace. The full command is detailed in the Oracle8i Java Tools Reference.

To bind a DataSource object for a single-phase commit transaction with the empHost database to the name "/test/empDatabase" in the namespace located on nsHost, execute the following:

sess_sh -service jdbc:oracle:thin:@nsHost:5521:ORCL -user SCOTT -password TIGER
& bindds /test/empDatabase -url jdbc:oracle:thin:@empHost:5521:ORCL -dstype jta

After binding the DataSource object in the namespace, the server can enlist the database within a global transaction.


Note:

If using more than one database, you will need to setup for a two-phase commit. See "Configuring Two-Phase Commit Engine" for more information.  


Developing the Client Application

The following example follows the steps listed in "JTA Client-Side Demarcation Including Databases".

import employee.Employee;
import employee.EmployeeHome;
import employee.EmployeeInfo;

import javax.naming.Context;
import javax.naming.InitialContext;
import java.util.Hashtable;
import javax.transaction.*;

public class Client
{
  public static void main (String[] args) throws Exception {

    //Set up the service URL to where the UserTransaction object 
    //is bound. Since from the client, the connection to the database
    //where the namespace is located can be communicated with over either
    //a Thin or OCI8 JDBC driver. This example uses a Thin JDBC driver.
    String namespaceURL = "jdbc:oracle:thin:@nsHost:1521:ORCL";
 
    //User and password are case sensitive.
    String user = "SCOTT";
    String password = "TIGER";

    //1.(a) Authenticate to the database.
    // create InitialContext and initialize for authenticating client
    Hashtable env = new Hashtable ();
    env.put (Context.URL_PKG_PREFIXES, "oracle.aurora.jndi");
    env.put (Context.SECURITY_PRINCIPAL, user);
    env.put (Context.SECURITY_CREDENTIALS, password);
    env.put (Context.SECURITY_AUTHENTICATION, ServiceCtx.NON_SSL_LOGIN);
    //1.(b) Specify the location of the namespace where the transaction objects
    //   are bound.
    env.put(jdbc_accessURLContextFactory.CONNECTION_URL_PROP, namespaceURL);
    Context ic = new InitialContext (env);

    //2. Register a JDBC OracleDriver. Required for retrieving UserTransaction
    // and the DataSource objects from namespace over JDBC connection.
    DriverManager.registerDriver (new oracle.jdbc.driver.OracleDriver());

    //3. Retrieve the UserTransaction object from JNDI namespace
    UserTransaction ut;
    ut = (UserTransaction)ic.lookup ("jdbc_access://test/myUT");

    //4. Start the transaction
    ut.begin();

    //5.(a) Retrieve the DataSource (that was previously bound with bindds in
    // the namespace. After retrieving the DataSource...
    // get a connection to a database. You need to provide authentication info
    // for a remote database lookup, similar to what you would do from a client.
    // In addition, if this was a two-phase commit transaction, you must provide
    // the username and password.
    DataSource ds = (DataSource)ic.lookup ("jdbc_access://test/empDB");

    // 5.(b) Get connection to the database through DataSource.getConnection
    // in this case, the database requires the same username and password as
    // set in the environment.
    Connection conn = ds.getConnection ();

    //6. Retrieve the EJB 
    // get an handle to the employee_home object
    EmployeeHome employee_home =
      (EmployeeHome)ic.lookup (serviceURL + objectName);

    // get an handle to the remote bean
    Employee employee = employee_home.create ();

    //7. (a) Perform bean business logic.
    // get an info of an employee
    EmployeeInfo info = employee.getEmployee ("SCOTT");
    System.out.println ("Beginning salary = " + info.salary);

    // do work on the info-object
    info.salary += (info.salary * 10) / 100;

    // call update on the server-side
    employee.updateEmployee (info);

    //7. (b) Execute SQL statements against the enlisted database.    
    Statement stmt = conn.createStatement ();
    int cnt = stmt.executeUpdate ("insert into my_tab values (39304)");

    //8. Close the database connection.
    conn.close ();

    //9. End the transaction
    //Commit the updated value
    ut.commit();
  }
}

The deployment descriptor and bean implementation code is the same as described in Example 7-2.

Enlisting Resources on the Server-side

Whether your bean instance uses bean-managed or container-managed transactions, the databases that the bean accesses must be enlisted to be included within the global transaction. This is discussed more in "Enlisting Resources" and "Bind DataSource Object in the Namespace".

Once bound, you can enlist the database after the transaction begins.

Local Database Enlistment

Because your bean has been deployed into an Oracle8i database, the container enlists this database automatically within the transaction. This database is known as the local database. Thus, if you execute any SQL against this database, the results are committed when the transaction is committed.

  • SQLJ

 

As discussed in the Oracle8i SQLJ Developer's Guide and Reference, an implicit local connection is supplied to the database that the object is running in. Any statements executed within the SQLJ statement is executed against the local database. However, in order for the statement results to be part of the transaction, you must execute the SQLJ statement within an open global transaction. That is, the SQLJ statement must be executed after the UserTransaction.begin method is invoked. However, do not commit the transaction within a SQLJ statement. Commitment of the transaction should only occur within by the UserTransaction.commit method.  

  • JDBC

 

You can create a local connection by executing the methods specified in Table 7-6.  

Table 7-6 Local Database Retrieval Methods
Retrieval Method  Description 

OracleDriver().
defaultConnection()
 

See Example 7-6  

DriverManager.getConnection
("jdbc:oracle:kprb:")
 

See Example 7-5  

DataSource.getConnection
("jdbc:oracle:kprb:")
 

See Example 7-4  

Always execute these methods and subsequent SQL statements after the global transaction has started.

Example 7-4 DataSource.getConnection Method Example

The following example is a container-managed transactional bean with RequiresNew attribute, so the global transaction is initialized when the bean is first invoked. The local connection is retrieved through the DataSource.getConnection method, which enlists the database in the transaction. SQL statements are executed against the local database. These statements are committed when the global transaction is committed by the container when the bean exits.

public EmpRecord query (int empNumber) throws SQLException, RemoteException
  {
   //Retrieving the UserTransaction and DataSource using in-session activation
    Context ic = new InitialContext ( );

    //Retrieve the DataSource using in-session activation
    DataSource ds = (DataSource) ic.lookup("/test/myDB");

    //Retrieve the local connection object to the local database
    Connection conn = ds.getConnection ();  

    //prepare and execute a sql statement against the local database.
    PreparedStatement ps =
      conn.prepareStatement ("select ename, sal from emp where empno = ?");
    try {
      ps.setInt (1, empNumber);
      ResultSet rset = ps.executeQuery ();
      if (!rset.next ())
        throw new RemoteException ("no employee with ID " + empNumber);
      return new EmpRecord (rset.getString (1), empNumber, rset.getFloat (2));
    } finally {
      ps.close();
    }

    //close the database connection
    conn.close();
  }


Example 7-5 DriverManager.getConnection Method Example

The following example is the same as Example 7-4, except for the database retrieval method. Substitute the following to retrieve the connection:

//Retrieve the local connection object to the local database
    Connection conn = 
DriverManager.getConnection ("jdbc:oracle:kprb:");

Example 7-6 OracleDriver().defaultConnection Method Example

The following example is the same as Example 7-4, except for the database retrieval method. Substitute the following to retrieve the connection:

//Retrieve the local connection object to the local database
    Connection conn =
      new oracle.jdbc.driver.OracleDriver().defaultConnection ();

Remote Oracle8i Database Enlistment

If you access a remote Oracle8i database from the server that should be included in the transaction, you must open the connection to the database after the global transaction starts.


Note:

At this time, the Oracle JTA implementation does not support including non-Oracle databases in a global transaction.  


Example 7-7 Enlist Database in Single Phase Transaction

The following example enlists a database in the global transaction within a bean-managed transactional bean.

The following example is a container-managed transactional bean, so it does not retrieve the UserTransaction. However, it does retrieve a DataSource to start a JDBC 2.0 connection.

package employeeServer;

import employee.*;

import javax.ejb.SessionBean;
import javax.ejb.CreateException;
import javax.ejb.SessionContext;
import java.rmi.RemoteException;
import java.sql.SQLException;
import javax.transaction.*;


public class EmployeeBean implements SessionBean
{
  SessionContext ctx;

  // Methods of the Employee interface
  public EmployeeInfo getEmployee (String name) 
       throws RemoteException, SQLException 
  {
    // get a connection to the local database. If this was a two-phase commit 
    // transaction, you would provide the username and password 
// for the 2pc engine DataSource ds = (DataSource)ic.lookup ("/test/myDS"); // get connection to the local database through DataSource.getConnection Connection conn = ds.getConnection (); //perform your SQL against the database. //prepare and execute a sql statement.
//retrieve the employee's selected benefits PreparedStatement ps = conn.prepareStatement ("update emp set ename = :(employee.name),
sal = :(employee.salary) where empno = :(employee.number)"); try { ps.setInt (1, empNumber); ResultSet rset = ps.executeQuery (); if (!rset.next ()) throw new RemoteException ("no employee with ID " + empNumber); return new EmpRecord (rset.getString (1), empNumber, rset.getFloat (2)); } finally { ps.close(); } //close the connection conn.close(); return new EmployeeInfo (name, empno, salary); } // Methods of the SessionBean public void ejbCreate () throws RemoteException, CreateException {} public void ejbRemove() {} public void setSessionContext (SessionContext ctx) { this.ctx = ctx; } public void ejbActivate () {} public void ejbPassivate () {} }


Go to previous page
Go to beginning of chapter
Go to next page
Oracle
Copyright © 1996-2000, Oracle Corporation.

All Rights Reserved.

Library

Product

Contents

Index