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

Part Number A83725-01

Library

Solution Area

Contents

Index

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

Configuring Two-Phase Commit Engine

If you have more than a single database involved in your transaction, you must designate a two-phase commit engine for managing all changes to all databases involved in the transaction. A two-phase commit engine is responsible for contacting all of the databases at the end of the transaction and managing the commit or rollback of all updates to all included databases. Thus, this two-phase commit engine must have access to database links to each database included within the transaction.

To configure for a two-phase commit, your system administrator must do the following:

  1. Designate an Oracle8i database as the two-phase commit engine.

  2. Configure database links from the two-phase commit engine to each database that may be involved in the global transaction. This is necessary for the two-phase commit engine to communicate with each database at the end of the transaction.

  3. Provide the database link name in the -dblink option of bindds for each individual database when binding that database's DataSource into the namespace.

    bindds /test/empDatabase -url jdbc:oracle:thin:@empHost:5521:ORCL 
    -dstype jta -dblink 2pcToEmp.oracle.com
  4. Provide the two-phase commit engine's fully-qualified database address, username, and password when binding the UserTransaction into the namespace.

    bindut /test/myUT -url sess_iiop://dbsun.mycompany.com:2481:ORCL 
    -user SCOTT -password TIGER


    Note:

    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  


  5. Update the Oracle-specific deployment descriptor with the JNDI name for the two-phase commit engine. This name should be included in the <transaction-manager> element. This element only needs to be defined in the bean where the transaction is started. See "Defining Two Phase Commit Engine for Transactions" for more information.

Once all of this configuration is complete, your application differs from the single-phase commit scenario in the following aspects:

Figure 7-4 shows the client invoking EmployeeBean, which opens a connection to its local database and a connection to a remote database.

Figure 7-4 Including Remote Oracle8i Databases in a Global Transaction


Example 7-8 Client Code

The only difference between this client and the client in Example 7-1 is that this client does not start the transaction. When the client invokes the EmployeeBean, the container starts the transaction.

package client;
import common.*;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.InitialContext;
import oracle.aurora.jndi.sess_iiop.ServiceCtx;

public class Client
{
  public static void main (String[] args) throws Exception
  {
    if (args.length != 6)
    {
      System.out.println ("usage: Client serviceURL jdbcURL objectName " + 
			  "user password");
      System.exit (1);
    }
    String serviceURL = args [0];
    String jdbcURL = args [1];
    String objectName = args [2];
    String user = args [3];
    String password = args [4];

    // set up the initial context
    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);
    Context ic = new InitialContext (env);    

    // lookup the home and remote interfaces fro the employee
    EmployeeHome home = (EmployeeHome)ic.lookup (serviceURL + objectName);
    EmployeeRemote remote = home.create ();
    Employee employee = null;

    // retrieve info abount this employee in this session
    employee = remote.getEmployeeForUpdate ("SCOTT");
    System.out.println ("Beginning salary for " + employee.name + " is " +
			employee.salary);

    // increase salary
    // employee.salary += 0.1 * employee.salary;
    employee.salary += 100;

    // update the infomation in the transaction
    remote.updateEmployee (employee);

    // Get and print the info in the transaction
    employee = remote.getEmployee ("SCOTT");
    System.out.println ("End salary for " + employee.name + " is " +
			employee.salary);
  }
}


Example 7-9 EmployeeBean EJB Deployment Descriptor

The EmployeeBean is specified as a container-managed transaction bean with the RequiresNew attribute. Also, the JDBC 2.0 and pre-2.0 objects are defined as environment variables.

<?xml version="1.0"?>
<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems Inc.//DTD Enterprise JavaBeans 1.1
//EN" "ejb-jar.dtd">
<ejb-jar>
   <enterprise-beans>
      <session>
         <ejb-name>test/EmployeeBean</ejb-name>
         <home>common.EmployeeHome</home>
         <remote>common.EmployeeRemote</remote>
         <ejb-class>server.EmployeeBean</ejb-class>
         <session-type>Stateful</session-type>
         <transaction-type>Container</transaction-type>
         <env-entry>
           <env-entry-name>EmployeeBean.KPRB_URL</env-entry-name>
           <env-entry-type>java.lang.String</env-entry-type>
           <env-entry-value>jdbc:oracle:kprb:</env-entry-value>
         </env-entry>
         <env-entry>
           <env-entry-name>EmployeeBean.JDBCDriverName</env-entry-name>
           <env-entry-type>java.lang.String</env-entry-type>
           <env-entry-value>oracle.jdbc.driver.OracleDriver</env-entry-value>
         </env-entry>
         <env-entry>
           <env-entry-name>EmployeeBean.JDBC_URL</env-entry-name>
           <env-entry-type>java.lang.String</env-entry-type>
           <env-entry-value>
              JDBC_URL=jdbc:oracle:thin:@localhost:5521:jis1
           </env-entry-value>
         </env-entry>
         <env-entry>
           <env-entry-name>EmployeeBean.DB_LINK</env-entry-name>
           <env-entry-type>java.lang.String</env-entry-type>
           <env-entry-value>
              LOOP2.REGRESS.RDBMS.DEV.US.ORACLE.COM
           </env-entry-value>
         </env-entry>
         <resource-ref>
           <res-ref-name>jdbc/EmployeeDS</res-ref-name>
           <res-type>javax.sql.DataSource</res-type>
           <res-auth>Application</res-auth>
         </resource-ref>
         <resource-ref>
           <res-ref-name>jdbc/HRDS</res-ref-name>
           <res-type>javax.sql.DataSource</res-type>
           <res-auth>Application</res-auth>
         </resource-ref>
      </session>
   </enterprise-beans>
   <assembly-descriptor>
      <security-role>
         <description>no description</description>
         <role-name>PUBLIC</role-name>
      </security-role>
      <method-permission>
         <description>no description</description>
         <role-name>PUBLIC</role-name>
         <method>
            <ejb-name>test/EmployeeBean</ejb-name>
            <method-name>*</method-name>
         </method>
      </method-permission>
      <container-transaction>
         <description>no description</description>
         <method>
            <ejb-name>test/EmployeeBean</ejb-name>
            <method-name>*</method-name>
         </method>
         <trans-attribute>RequiresNew</trans-attribute>
      </container-transaction>
   </assembly-descriptor>
</ejb-jar>


Example 7-10 EmployeeBean Oracle-Specific Deployment Descriptor

The environment variables for the JDBC objects are mapped to the JNDI bound names. The UserTransaction that is bound with the two-phase commit engine URL is specified in the <transaction-manager> element.
<?xml version="1.0"?>
<!DOCTYPE oracle-descriptor PUBLIC "-//Sun Microsystems Inc.//DTD Enterprise Jav
aBeans 1.1//EN" "oracle-ejb-jar.dtd">
<oracle-descriptor>
  <mappings>
    <ejb-mapping>
      <ejb-name>EmployeeBean</ejb-name>
      <jndi-name>test/EmployeeBean</jndi-name>
    </ejb-mapping>
    <security-role-mapping>
       <security-role>
         <description>just a role</description>
         <role-name>SECURITY_CLERK</role-name>
       </security-role>
       <oracle-role>CLERK</oracle-role>
    </security-role-mapping>
    <resource-ref-mapping>
      <res-ref-name>jdbc/EmployeeDS</res-ref-name>
      <jndi-name>test/DataSource/empDS</jndi-name>
    </resource-ref-mapping>
    <resource-ref-mapping>
      <res-ref-name>jdbc/HRDS</res-ref-name>
      <jndi-name>test/DataSource/hrDS</jndi-name>
    </resource-ref-mapping>
    <transaction-manager>
      <jndi-name>test/UserTransaction/testut</jndi-name>
    </transaction-manager>
  </mappings>
</oracle-descriptor>


Example 7-11 EmployeeBean Using Bean-Managed Transactions

The container-managed transactional EmployeeBean retrieves connections to both the local and a remote Oracle8i database.

package server;

import common.*;
import java.sql.*;
import java.util.Hashtable;
import java.rmi.RemoteException;
import javax.sql.*;
import javax.naming.*;
import javax.ejb.*;
import oracle.aurora.jndi.sess_iiop.*;
import sqlj.runtime.ref.DefaultContext;
import javax.transaction.*;

import oracle.aurora.transaction.xa.OracleJTADataSource;

public class EmployeeBean implements SessionBean
{
  SessionContext ctx;
  Context ic = null; // inSession Lookup Context
  Connection localConn = null;
  Connection remoteConn = null;
  DefaultContext remoteCtx = null;
  Employee remoteEmployee = null;
  String remoteEmpName = "SMITH";

  private void setupDSConnections ()
       throws SQLError, RemoteException
  { 
    try { 
      if (ic == null) 
	ic = new InitialContext ();

      //retrieve the remote connection
      remoteConn = getRemoteDSConnection ();
      if (remoteConn == null)
	System.out.println ("remote connection is NULL");
      else
	System.out.println ("got remote connection");

      //setup the context for issuing SQLJ against the remote database
      remoteCtx = new DefaultContext (remoteConn);

      //retrieve the local connection
      localConn = getLocalDSConnection ();
      if (localConn == null)
	System.out.println ("local connection is NULL");
      else
	System.out.println ("got local connection");
    } catch (NamingException e) {
      e.printStackTrace ();
      throw new SQLError ("setupDSConnection failed:" + e.toString ());
    } catch (SQLException e) {
      e.printStackTrace ();
      throw new SQLError ("setupDSConnection failed:" + e.toString ());
    }
  }

  private Connection getLocalDSConnection ()
       throws SQLException, SQLError
  {
    try {
      System.out.println ("looking up EmployeeDS in JNDI");
      // get a connection to the local DB using an environment variable
      //specified in the deployment descriptor
      DataSource localDS = (DataSource)ic.lookup 
	("java:comp/env/jdbc/EmployeeDS");
      System.out.println ("getLocalDSConnection: " + 
			  ((OracleJTADataSource)localDS).getURL());

      // get a connectoin to the local DB
      return localDS.getConnection ();
    } catch (NamingException e) {
      e.printStackTrace ();
      throw new SQLError ("getLocalDSConnection failed:" + e.toString ());
    }
  }

  private Connection getRemoteDSConnection ()
       throws SQLException, SQLError
  {
    try {
      // get a connection to the remopte DB
      System.out.println ("looking up HRDS in JNDI");
      //retrieve the remote database DataSource HRDS using the environment 
      //variable specified in the deployment descriptor
      DataSource remoteDS = (DataSource)ic.lookup ("java:comp/env/jdbc/HRDS");
      System.out.println ("getRemoteDSConnection: "+ 
			  ((OracleJTADataSource)remoteDS).getURL());

      // get a connection to the remote DB passing in the usernamd and 
      // password for this database. (Otherwise, it would have had to be
      // specified in the Context environment.
      return remoteDS.getConnection ("scott", "tiger");
    } catch (NamingException e) {
      e.printStackTrace ();
      throw new SQLError ("getRemoteDSConnection failed:" + e.toString ());
    }
  }
 
  public void updateEmployee (Employee employee)
    throws SQLError, RemoteException
  {
    try {
      setupDSConnections ();

      //issue SQL DML statements against the local database
      #sql { update emp set ename = :(employee.name), sal = :(employee.salary)
                    where empno = :(employee.number) };

      remoteEmployee.salary += 200;

      //issue SQL DML statemetns against the remote database
      #sql [remoteCtx] { update emp set ename = :(remoteEmployee.name), 
	sal = :(remoteEmployee.salary) 
	  where empno = :(remoteEmployee.number) };


      //close both database connections
      localConn.close();
      remoteConn.close ();
    } catch (SQLException e) {
      e.printStackTrace ();
      throw new SQLError ("updateEmployee failed: " + e.toString ());
    }
  }

  public void setSessionContext(SessionContext ctx)
  {
    this.ctx = ctx;
  }

  public void ejbCreate() throws CreateException, RemoteException {}
  public void ejbActivate() {}
  public void ejbPassivate() {}
  public void ejbRemove() {}

}


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

All Rights Reserved.

Library

Solution Area

Contents

Index