Oracle8i Enterprise JavaBeans Developer's Guide and Reference Release 3 (8.1.7) Part Number A83725-01 |
Library | Service | Contents | Index

## 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

- Remote Oracle8i Database Enlistment

#### 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 () {}
    }

| Copyright © 1996-2000, Oracle Corporation. All Rights Reserved. |
Library | Service | Contents | Index