J2EE Connector Architecture

 


Overview

While developers concentrate on the JDBC API for accessing relational databases, system integrators need to learn about the J2EE Connector (J2C) architecture.

The J2C architecture enables J2EE components such as enterprise beans to interact with Enterprise Information Systems (EISs), which themselves include various types of systems, including:

  1. Enterprise Resource Planning (ERP)
  2. Mainframe transaction processing
  3. Databases
  4. Security servers among others

The J2C architecture simplifies the integration of diverse EISs. Each EIS requires just one implementation of the J2C architecture. Because an implementation adheres to the J2C Specification, it is portable across all compliant J2EE servers.

 


About Resource Adapters

A resource adapter is a J2EE component that implements the J2C architecture for a specific EIS. It is through the resource adapter that a J2EE app communicates with an EIS

Stored in a Resource Adapter Archive (RAR) file, a resource adapter may be deployed on any J2EE server, much like the EAR file of a J2EE app. A RAR file may be contained in an EAR file or it may exist as a separate file.

A resource adapter is analogous to a JDBC driver. Both provide a standard API through which an app can access a resource that is outside the J2EE server. For a resource adapter, the outside resource is an EIS; for a JDBC driver, it is a DBMS. Resource adapters and JDBC drivers are rarely created by app developers. In most cases, both types of software are built by vendors who sell products such as tools, servers, or integration software.

 

Resource Adapter Contracts

The figure below shows the two types of contracts implemented by a resource adapter. The app contract defines the API through which a J2EE component such as an enterprise bean accesses the EIS. This API is the only view that the component has of the EIS. The resource adapter itself and its system contracts are transparent to the J2EE component.

The system contracts link the resource adapter to important services--connection, transaction, and security--that are managed by the J2EE server.

Accessing an EIS Through a Resource Adapter

The connection management contract supports connection pooling, a technique that enhances app performance and scalability. Connection pooling is transparent to the app, which simply obtains a connection to the EIS.

Because of the transaction management contract, a call to the EIS may be enclosed in an XA transaction. XA transactions are global--they may contain calls to multiple EISs, databases, and enterprise bean business methods. Although often appropriate, XA transactions are not mandatory. Instead, an app may use local transactions, which are managed by the individual EIS, or it may use no transactions at all.

To protect the information in an EIS, the security management contract provides these mechanisms: authentication, authorization, and secure communication between the J2EE server and the EIS.

 

Administering Resource Adapters

Installing a resource adapter is a two-step process:

  1. Deploy the RAR file containing the resource adapter onto a server.

    The following command, for example, deploys a sample black box resource adapter onto the local host. (For Windows, in the following commands omit the backslash character, change $J2EE_HOME to %J2EE_HOME%, and enter the entire command on a single line.)

       deploytool -deployConnector \
          $J2EE_HOME/lib/connector/cciblackbox-tx.rar \
          localhost
    
    
  2. Add a connection factory for the resource adapter.

    Suppose that you wanted to add a connection factory for the resource adapter in the cciblackbox-tx.rar file. The JNDI name of the connection factory will be eis/MyCciBlackBoxTx. To override the default value of the property named ConnnectionURL, you specify the URL of a database. (A property is a name-value pair used to configure a connection factory.) To add the connection factory, you might enter the following j2eeadmin command:

       j2eeadmin -addConnectorFactory \
          eis/MyCciBlackBoxTx \ 
          cciblackbox-tx.rar \
          -props \
          ConnectionURL=jdbc:oracle:thin:@myhost:1521:ACCTDB
    
    

For the full syntax of the deploytool and j2eeadmin commands, see Appendix B. These commands also list and remove resource adapters and connection factories.

To list the resource adapters that have been deployed, use the following command:

deploytool -listConnectors localhost

To list the connection factories that have been added, use the following command:

j2eeadmin -listConnectorFactory

To uninstall the resource adapter deployed in step 1, use the following command:

deploytool -undeployConnector cciblackbox-tx.rar localhost

To remove the connection factory added in step 2, use the following command:

j2eeadmin -removeConnectorFactory eis/MyCciBlackBoxTx

 


The Black Box Resource Adapters

The J2EE SDK includes several black box resource adapters for performing end-to-end and compatibility testing. The underlying EIS of these adapters is a relational DBMS. The client API is the JDBC 2.0 API and the javax.sql.DataSource interface. Underneath, the black box adapters use JDBC drivers to communicate with relational databases. Although the black box adapters use JDBC, resource adapters are not meant to replace JDBC for accessing relational databases. The black box adapters are for testing purposes only. Because they use JDBC, they can be plugged into existing tests that also use JDBC.

 

Transaction Levels

The black box resource adapters reside in the $J2EE_HOME/lib/connector. The table below lists the black box RAR files and the different transaction levels that they support.

File
Transaction Level
blackbox-notx.rar NO_TRANSACTION
blackbox-tx.rar LOCAL_TRANSACTION
blackbox-xa.rar XA_TRANSACTION
cciblackbox-tx.rar LOCAL_TRANSACTION
cciblackbox-xa.rar XA_TRANSACTION

For the XA_TRANSACTION level, the underlying JDBC driver must support the XA requirements as defined by the JDBC 2.0 API.

 

Properties

A resource adapter may contain properties, that is, name-value pairs containing information specific to the resource adapter and its underlying EIS. These properties are defined in the deployment descriptor of each black box RAR file. Because the EIS of a black box adapter is a relational database, the properties contain information required for connecting to a database. The table below lists the properties of the black box adapter files. Table 17-3 shows the default values for the black box properties.

File
Property Name
Description
blackbox-notx.rar ConnectionURL URL of database
blackbox-tx.rar ConnectionURL URL of database
blackbox-xa.rar XADataSourceName JNDI name of XADataSource
cciblackbox-tx.rar ConnectionURL URL of database
cciblackbox-xa.rar XADataSourceName JNDI name of XADataSource

Property Name
Description
ConnectionURL jdbc:cloudscape:rmi:CloudscapeDB;create=true
XADataSourceName jdbc/XACloudscape_xa

To override a default property value, you set the value when adding a connection factory with the j2eeadmin command.

 

Configuring JDBC Drivers

If you are running the black box adapters against a Cloudscape database, you may skip this section. If you are using a database other than Cloudscape, you should perform the steps that follow.

The Non-XA Black Box Adapters

  1. Set the JDBC driver class. Use the j2eeadmin tool with the -addJdbcDriver option and specify the driver class name. The syntax for this option is as follows:

       j2eeadmin -addJdbcDriver <class name>
    

  2. Edit bin/userconfig.sh and set J2EE_CLASSPATH to the location of the JDBC driver classes.

  3. Restart the J2EE server.

The XA Black Box Adapters

  1. Set the XADatasource property. With the j2eeadmin tool and the -addJdbcXADatasource option, specify the JNDI name and class name for the XADatasource property. Optionally, you may specify the XA user name and password and you may override the default property value. The syntax follows:

       j2eeadmin -addJdbcXADatasource <jndi-name> <class-name>
          [<xa-user-name> <xa-password>]
          [-props (<name>=<value>)+]
    

    The preceding command results in two data sources. One is a DataSource object with the specified JNDI name from which the J2EE app gets a Connection instance. The other is an XADatasource object whose JNDI name is the <jndi-name> parameter appended with two underscores and xa (<jndi-name>__xa). Behind the scenes, the DataSource uses the XADataSource to create connections.
  2. Restart the J2EE server.

 

Resource Adapter

The following example shows how to deploy the black box resource adapter stored in the blackbox-tx.rar file. To test the resource adapter, you will modify the examples/src/ejb/savingsaccount/SavingsAccountBean.java file so that it accesses the Cloudscape database through the resource adapter. The SavingsAccountBean.java file is also used in another example; see Running the SavingsAccountEJB Example.

 

Setting Up

  1. Start the J2EE server.

    j2ee -verbose

  2. Follow the instructions in the section Setting Up the Database.

 

Deploying the Resource Adapter

  1. Deploy a black box resource adapter that is packaged in the blackbox-tx.rar file.

    deploytool -deployConnector \ $J2EE_HOME/lib/connector/blackbox-tx.rar localhost

  2. Add a connection factory for the resource adapter. The JNDI name for the connection factory is eis/MyBlackBoxTx.

    j2eeadmin -addConnectorFactory eis/MyBlackBoxTx blackbox-tx.rar
  3. Verify that the resource adapter has been deployed.

       deploytool -listConnectors localhost
    
    

    The deploytool utility displays these lines:

       Installed connector(s):
       Connector Name: blackbox-tx.rar
    
       Installed connection factories:
       Connection Factory JNDI Name: eis/MyBlackBoxTx
    
    

 

Testing the Resource Adapter

  1. If you are new to the J2EE SDK, you should first read the instructions in Chapter 2.
  2. Locate the SavingsAccountBean.java source code, which resides in the j2eetutorial/examples/src/ejb/savingsaccount directory.
  3. Edit the SavingsAccountBean.java source code, changing the value assigned to the dbName variable as follows:

       private String dbName = "java:comp/env/MyEIS";
    
    
  4. Compile the source code in the savingsaccount directory.

    1. Go to j2eetutorial/examples.
    2. Type ant savingsaccount.

  5. Replace the new SavingsAccountBean.class file in the existing SavingsAccountApp.ear file.

    1. In the GUI deploytool, open the j2eetutorial/examples/ears/SavingsAccountApp.ear file.
    2. On the General tab of the SavingsAccountJAR, click Edit.
    3. In the Available Files field, locate the j2eetutorial/examples/build/ejb/SavingsAccountBean.class file.
    4. Drag and drop the SavingsAccountBean.class file from the Available Files field to the Contents field.
    5. Click OK.

  6. Change the resource factory reference.

    1. Select the Resource Refs tab of SavingsAccountEJB.
    2. Select the item whose Coded Name entry is jdbc/SavingsAccountDB.
    3. Click Delete.
    4. Click Add.
    5. Enter the values specified in the table below:

      Field
      Value
      Coded Name MyEIS
      Type javax.sql.DataSource
      Authentication Container
      JNDI Name eis/MyBlackBoxTx

    The eis/MyBlackBoxTx JNDI name matches the name of the connection factory that you added in step 2 of Deploying the Resource Adapter. The MyEIS value of the Coded Name field corresponds to this line in the SavingsAccountBean.java source code:

    private String dbName = "java:comp/env/MyEIS";
    Although it is included in the source code, the java:comp/env/ subcontext is implicit in the Coded Name field of the Resource Refs tab.

  7. Save the SavingsAccountApp app (FileSave).

  8. Deploy the SavingsAccountApp app.

    1. Select...

      ToolsDeploy | Introduction dialog box | Return Client Jar

    2. In the JNDI Names dialog box, verify that the JNDI names have been specified.

      Component or Reference Name
      JNDI Name
      SavingsAccountEJB MySavingsAccount
      MyEIS eis/MyBlackBoxTx
      ejb/SimpleSavingsAccount MySavingsAccount

    3. Run the app.

 


Common Client Interface

The Common Client Interface (CCI) defines a set of interfaces and classes whose methods allow a client to perform data access operations. Typical methods include:

ConnectionFactory Provides an app component with a Connection instance to an EIS.
Connection Represents the connection to the underlying EIS.
ConnectionSpec Provides a means for an app component to pass connection-request-specific properties to the ConnectionFactory when making a connection request.
Interaction Provides a means for an app component to execute EIS functions, such as database stored procedures.
InteractionSpec Holds properties pertaining to an app component's interaction with an EIS.
Record The superclass for the different kinds of record instances. Record instances may be MappedRecord, IndexedRecord, or ResultSet instances, which all inherit from the Record interface.
RecordFactory Provides an app component with a Record instance.
IndexedRecord Represents an ordered collection of Record instances based on the java.util.List interface.

A client or app component that uses the CCI to interact with an underlying EIS does so in a prescribed manner. The component must establish a connection to the EIS's resource manager, and it does so using the ConnectionFactory. The Connection object represents the actual connection to the EIS and is used for subsequent interactions with the EIS.

The component performs its interactions with the EIS, such as accessing data from a specific table, using an Interaction object. The app component defines the Interaction object using an InteractionSpec object. When the app component reads data from the EIS (such as from database tables) or writes to those tables, it does so using a particular type of Record instance, either a MappedRecord, IndexedRecord, or ResultSet instance. Just as the ConnectionFactory creates Connection instances, a RecordFactory creates Record instances.

Our example shows how a session bean uses a resource adapter to add and read records in a relational database. The example shows how to invoke stored procedures, which are business logic functions stored in a database and specific to an enterprise's operation. Stored procedures consist of SQL code to perform operations related to the business needs of an organization. They are kept in the database and can be invoked when needed, just as you might invoke a Java method. In addition to showing how to use the CCI to invoke stored procedures, we'll also explain how to pass parameters to stored procedures and how to map the parameter data types from SQL to those of the Java programming language.

 

Programming with the CCI

The code for the following example is in the examples/src/connector/cci directory.

To illustrate how to use a CCI resource adapter, we've written a session bean and a client of that bean. These pieces of code illustrate how clients invoke the different CCI methods that resource adapters built on CCI might make available. Our example uses the two sample CCI-specific resource adapters: cciblackbox_tx.rar and cciblackbox_xa.rar.

The Coffee session bean is much like any other session bean. It has a home interface (CoffeeHome), a remote interface (Coffee), and an implementation class (CoffeeEJB). To keep things simple, we've called the client CoffeeClient.

Let's start with the session bean interfaces and classes. The home interface, CoffeeHome, is like any other session bean home interface. It extends EJBHome and defines a create method to return a reference to the Coffee remote interface.

The Coffee remote interface defines the bean's two methods that may be called by a client.

public void insertCoffee(String name, int quantity)
throws RemoteException;
public int getCoffeeCount() throws RemoteException;

Now let's examine the CoffeeEJB session bean implementation class to see how it uses the CCI. To begin with, notice that CoffeeEJB imports the javax.resource CCI interfaces and classes, along with the javax.resource.ResourceException and the sample cciblackbox classes.

import javax.resource.cci.*;
import javax.resource.ResourceException;
import com.sun.connector.cciblackbox.*;


Prior to obtaining a database connection, the session bean does some set-up work in its setSessionContext method. Specifically, the setSessionContext method sets the user and password values, and instantiates a ConnectionFactory. These values and objects remain available to the other session bean methods. (In this and subsequent code examples, the numbers in the left margin correspond to the explanation that follows the code.)

   public void setSessionContext(SessionContext sc) {
        try {
            this.sc = sc;
1           Context ic = new InitialContext();
2           user = (String) ic.lookup("java:comp/env/user");
            password = (String) ic.lookup
               ("java:comp/env/password");
3           cf = (ConnectionFactory) ic.lookup
               ("java:comp/env/CCIEIS");
        } catch (NamingException ex) {
            ex.printStackTrace();
        }
    }

  1. Establish a JNDI InitialContext.
  2. Use the JNDI InitialContext.lookup method to find the user and password values.
  3. Use the lookup method to locate the ConnectionFactory for the CCI black box resource adapter and obtain a reference to it.

CoffeeEJB uses its private method getCCIConnection to establish a connection to the underlying resource manager or database. A client of the Coffee session bean cannot invoke this method directly. Rather, the session bean uses this method internally to establish a connection to the database. The following code uses the CCI to establish a database connection.

    private Connection getCCIConnection() {
        Connection con = null;
        try {
1           ConnectionSpec spec = 
                new CciConnectionSpec(user, password);
2           con = cf.getConnection(spec);
        } catch (ResourceException ex) {
            ex.printStackTrace();
        }
        return con;
    }

  1. Instantiate a new CciConnectionSpec object with the user and password values obtained by the setSessionContext method. The CciConnectionSpec class is the implementation of the ConnectionSpec interface.
  2. Call the ConnectionFactory.getConnection method to obtain a connection to the database. (The reference to the ConnectionFactory was obtained in the setSessionContext method.) Use the CciConnectionSpec object to pass the required properties to the ConnectionFactory. The getConnection method returns a Connection object.

The CoffeeEJB bean also includes a private method, closeCCIConnection, to close a connection. The method invokes the Connection object's close method from within a try/catch block. Like the getCCIConnection method, this is a private method intended to be called from within the session bean.

private void closeCCIConnection(Connection con) {
    try {
        con.close();
    } catch (ResourceException ex) {
        ex.printStackTrace();
    }
}

Database Stored Procedures

The sample CCI black box adapters call database stored procedures. It is important to understand stored procedures before delving into how to read or write data using the sample CCI black box adapters. The methods of these sample CCI adapters do not actually read data from a database or update database data. Instead, these sample CCI adapters enable you to invoke database stored procedures, and it is the stored procedures that actually read or write to the database.

A stored procedure is a business logic method or function that is stored in a database and is specific for the enterprise's business. Typically, stored procedures consist of SQL code, though in certain cases (such as with Cloudscape) they may contain code written in the Java programming language. Stored procedures perform operations related to the business needs of an organization. They are kept in the database, and apps can invoke them when needed.

Stored procedures are typically SQL statements. Our example calls two stored procedures: COUNTCOFFEE and INSERTCOFFEE. The COUNTCOFFEE procedure merely counts the number of coffee records in the Coffee table, as follows:

SELECT COUNT(*) FROM COFFEE

The INSERTCOFFFEE procedure adds a record with two values, passed to the procedure as parameters, to the same Coffee table, as follows:

INSERT INTO COFFEE VALUES (?,?)

Mapping to Stored Procedure Parameters

When you invoke a stored procedure from your app component, you may have to pass argument values to the procedure. For example, when you invoke the INSERTCOFFEE procedure, you pass it two values for the Coffee record elements. Likewise, be prepared to receive values that a stored procedure returns.

The stored procedure, in turn, passes its set of parameters to the database management system (DBMS) to carry out its operation and may receive values back from the DBMS. Database stored procedures specify, for each of their parameters, the SQL type of the parameter value and the mode of the parameter. Mode can be input (IN), output (OUT), or both input and output (INOUT). An input parameter only passes data in to the DBMS, and an output parameter only receives data back from the DBMS. An INOUT parameter accepts both input and output data.

When you use the CCI execute method to invoke a database stored procedure you also create an instance of an InputRecord, provided that you're passing a parameter to the stored procedure and that the stored procedure you're executing returns data (possibly an OutputRecord instance). The InputRecord and OutputRecord are instances of the supported Record types: IndexedRecord, MappedRecord, or ResultSet. In our example, we instantiate an InputRecord and an OutputRecord that are both IndexedRecord instances.


Note: The CCI black box adapters only support IndexedRecord types.

The InputRecord maps the IN and INOUT parameters for the stored procedure, and the OutputRecord maps the OUT and INOUT parameters. Each element of an input or output record corresponds to a stored procedure parameter. That is, there is an entry in the InputRecord for each IN and INOUT parameter declared in the stored procedure. Not only does the InputRecord have the same number of elements as the procedure's input parameters, but they also are declared in the same order as in the procedure's parameter list. The same holds true for the OutputRecord, though its list of elements matches only the OUT and INOUT parameters. For example, suppose you have a stored procedure X that declares three parameters. The first parameter is an IN parameter, the second is an OUT parameter, and the third is an INOUT parameter.

When you use the CCI black box adapter, you designate the parameter type and mode in the same way, though the underlying Oracle or Cloudscape DBMS declares the mode differently. Oracle designates the parameter's mode in the stored procedure declaration, along with the parameter's type declaration. For example, an Oracle INSERTCOFFEE procedure declares its two IN parameters as follows:

procedure INSERTCOFFEE (name IN VARCHAR2, qty IN INTEGER)

Mapping Stored Procedure Parameters to CCI Record Elements

An Oracle COUNTCOFFEE procedure declares its parameter N as an OUT parameter:

procedure COUNTCOFFEE (N OUT INTEGER)

Cloudscape, which declares a stored procedure as a method signature in the Java programming language, indicates an IN parameter using a single value, and an INOUT parameter using an array. The method's return value is the OUT parameter. For example, Cloudscape declares the IN parameters (name and qty) for insertCoffee and the OUT parameter (the method's return value) for countCoffee as follows:

public static void insertCoffee(String name, int qty)
public int countCoffee()

If qty were an INOUT parameter, then Cloudscape would declares it as

public static void insertCoffee(String name, int[] qty)

Oracle would declare it as

procedure INSERTCOFFEE (name IN VARCHAR2, qty INOUT INTEGER)

You must also map the SQL type of each value to its corresponding Java type. Thus, if the SQL type is an integer, then the InputRecord or OutputRecord element must be defined as an Integer object. If the SQL type is a VARCHAR, then the Java type must be a String object. Thus, when you add the element to the Record, you declare it to be an object of the proper type. For example, add an integer and a string element to an InputRecord as follows:

iRec.add (new Integer (intval));
iRec.add (new String ("Mocha Java"));


Note: The JDBC Specification defines the type mapping of SQL and the Java programming language.

Reading Database Records

The getCoffeeCount method of CoffeeEJB illustrates how to use the CCI to read records from a database table. This method does not directly read the database records itself; instead, it invokes a procedure stored in the database called COUNTCOFFEE. It is the stored procedure that actually reads the records in the database table.

The CCI provides interfaces for three types of records: IndexedRecord, MappedRecord, and ResultSet. These three record types inherit from the base interface, Record. They differ only in how they map the record elements within the record. Our example uses IndexedRecord, which is the only record type currently supported. IndexedRecord holds its record elements in an ordered, indexed collection based on java.util.List. As a result, we use an Iterator object to access the individual elements in the list.

Let's begin by looking at how the getCoffeeCount method uses the CCI to invoke a database stored procedure. Again, note that the numbers in the margin to the left of the code correspond to the explanation after the code example.

    public int getCoffeeCount() {
        int count = -1;
        try {
1           Connection con = getCCIConnection();
2           Interaction ix = con.createInteraction();
3           CciInteractionSpec iSpec =
               new CciInteractionSpec();
4           iSpec.setSchema(user);
            iSpec.setCatalog(null);
            iSpec.setFunctionName("COUNTCOFFEE");
5           RecordFactory rf = cf.getRecordFactory();
6           IndexedRecord iRec =
               rf.createIndexedRecord("InputRecord");
7           Record oRec = ix.execute(iSpec, iRec);
8           Iterator iterator = 
               ((IndexedRecord)oRec).iterator();
9           while(iterator.hasNext()) {   
                Object obj = iterator.next();
                if(obj instanceof Integer) {
                    count = ((Integer)obj).intValue();
                }
                else if(obj instanceof BigDecimal) {
                    count = ((BigDecimal)obj).intValue();
                }
            }
10          closeCCIConnection(con);
        }catch(ResourceException ex) {
            ex.printStackTrace();
        }
        return count;
    }

  1. Obtain a connection to the database.
  2. Create a new Interaction instance. The getCoffeeCount method creates a new Interaction instance because it is this object that enables the session bean to execute EIS functions such as invoking stored procedures.
  3. Instantiate a CciInteractionSpec object. The session bean must pass certain properties to the Interaction object, such as schema name, catalog name, and the name of the stored procedure. It does this by instantiating a CciInteractionSpec object. The CciInteractionSpec is the implementation class for the InteractionSpec interface, and it holds properties required by the Interaction object to interact with an EIS instance. (Note that our example uses a Cloudscape database, which does not require a catalog name.)
  4. Set values for the CciInteractionSpec instance's fields. The session bean uses the CciInteractionSpec methods setSchema, setCatalog, and setFunctionName to set the required values into the instance's fields. Our example passes COUNTCOFFEE to setFunctionName because this is the name of the stored procedure it intends to invoke.
  5. The getCoffeeCount method uses the ConnectionFactory to obtain a reference to a RecordFactory so that it can create an IndexedRecord instance. We obtain an IndexedRecord (or a MappedRecord or a ResultSet) using a RecordFactory.
  6. Invoke the createIndexedRecord method of RecordFactory. This method creates a new IndexedRecord using the name InputRecord, which is passed to it as an argument.
  7. The getCoffeeCount method has completed the required set-up work and can invoke the stored procedure COUNTCOFFEE. It does this using the Interaction instance's execute method. Notice that it passes two objects to the execute method: the InteractionSpec object, whose properties reference the COUNTCOFFEE stored procedure, and the IndexedRecord object, which the method expects to be an input Record. The execute method returns an output Record object.
  8. The getCoffeeCount method uses an Iterator to retrieve the individual elements from the returned IndexedRecord. It casts the output Record object to an IndexedRecord. IndexedRecord contains an iterator method that it inherits from java.util.List.
  9. Retrieve each element in the returned record object using the iterator.hasNext method. Each extracted element is an Object, and the bean evaluates whether it is an integer or decimal value and processes it accordingly.
  10. Close the connection to the database.

Inserting Database Records

The CoffeeEJB session bean implements the insertCoffee method to add new records into the Coffee database table. This method invokes the INSERTCOFFEE stored procedure, which inserts a record with the values (name and qty) passed to it as arguments.

The insertCoffee method shown here illustrates how to use the CCI to invoke a stored procedure that expects to be passed argument values. This example shows the code for the insertCoffee method and is followed by an explanation.

    public void insertCoffee(String name, int qty) {
        try {
1           Connection con = getCCIConnection();
2           Interaction ix = con.createInteraction();
3           CciInteractionSpec iSpec = 
               new CciInteractionSpec();
4           iSpec.setFunctionName("INSERTCOFFEE");
            iSpec.setSchema(user);
            iSpec.setCatalog(null);
5           RecordFactory rf = cf.getRecordFactory();
6           IndexedRecord iRec =
               rf.createIndexedRecord("InputRecord");
7           boolean flag = iRec.add(name);
            flag = iRec.add(new Integer(qty));
8           ix.execute(iSpec, iRec);
9           closeCCIConnection(con);
        }catch(ResourceException ex) {
            ex.printStackTrace();
        }
    }

  1. Establish a connection to the database.
  2. Create a new Interaction instance for the connection so that the bean can execute the database's stored procedures.
  3. Instantiate a CciInteractionSpec object so that the bean can pass the necessary properties--schema name, catalog name, and stored procedure name--to the Interaction object. The CciInteractionSpec class implements the InteractionSpec interface and holds properties that the Interaction object requires to communicate with the database instance.
  4. Set the required values into the new CciInteractionSpec instance's fields, using the instance's setSchema, setCatalog, and setFunctionName methods. Our example passes INSERTCOFFEE to setFunctionName, and user to setSchema.
  5. Obtain a reference to a RecordFactory using the ConnectionFactory object's getRecordFactory method.
  6. Invoke the RecordFactory object's createIndexedRecord method to create a new IndexedRecord with the name InputRecord.
  7. Use the IndexedRecord add method to set the values for the two elements in the new record. Call the add method once for each element. Our example sets the first record element to the name value and the second element to the qty value. Notice that qty is set to an Integer object when it is passed to the add method. The CoffeeEJB session bean is now ready to add the new record to the database.
  8. Call the Interaction instance's execute method to invoke the stored procedure INSERTCOFFEE. Just as we did when invoking the COUNTCOFFEE procedure, we pass two objects to the execute method: the InteractionSpec object with the correctly set properties for the INSERTCOFFEE stored procedure, and the IndexedRecord object representing an input Record. The execute method is not expected to return anything in this case.
  9. Close the connection to the database.

 

Writing a CCI Client

A client app that relies on a CCI resource adapter is very much like any other J2EE client that uses enterprise bean methods. Our CoffeeClient app uses the methods of the CoffeeEJB session bean to access the Coffee table in the underlying database. CoffeeClient invokes the Coffee.getCoffeeCount method to read the Coffee table records and invokes Coffee.insertCoffee to add records to the table.

 

CCI Tutorial

This tutorial shows how to deploy and test the sample CCI black box adapter with the code described in the preceding sections. This code has been packaged into a J2EE app EAR file named CoffeeApp.ear, which is located in the j2eetutorial/examples/ears directory. The source code is in j2eetutorial/examples/src/connector/cci. To compile the source code, go to the j2eetutorial/examples directory and type ant cci.

Deploying the Resource Adapter

  1. Use the deploytool utility to deploy the CCI black box resource adapter. Specify the name of the resource adapter's RAR file (cciblackbox-tx.rar), plus the name of the server (localhost).

    deploytool -deployConnector $J2EE_HOME/lib/connector/cciblackbox-tx.rar localhost

  2. Next, add a connection factory for the deployed CCI adapter. The connection factory supplies a data source connection for the adapter. Use j2eeadmin to create the connection factory, specifying the adapter's JNDI name plus the server name. Here, we add a connection factory for our CCI adapter whose JNDI name is eis/CciBlackBoxTx on the server localhost.

    j2eeadmin -addConnectorFactory eis/CciBlackBoxTx cciblackbox-tx.rar

  3. Verify that the resource adapter has been deployed.

       deploytool -listConnectors localhost
    
    

    The deploytool utility displays these lines:

       Installed connector(s):
          Connector Name: cciblackbox-tx.rar
       Installed connection factories:
          Connection Factory JNDI name: eis/CciBlackBoxTx
    
    

Setting Up the Database

For Cloudscape, use the following procedure.

  1. Create the stored procedure.

    1. To compile the stored procedure, go to the j2eetutorial/examples directory and type ant procs. This command will put the Procs.class file in the j2eetutorial/examples/build/connector/procs directory.

    2. Edit bin/userconfig.sh so that the J2EE_CLASSPATH variable points to the directory that contains the Procs.class file.

    3. Restart the Cloudscape server.

    4. Go to the j2eetutorial/examples directory and type ant create-procs-alias. This command creates aliases for the methods in Procs.class. Cloudscape uses method aliases to simulate stored procedures.

  2. To create the Coffee table, go to the j2eetutorial/examples directory and type ant create-coffee-table.

For Oracle, use the following procedure.

  1. Start the database server.
  2. Run the j2eetutorial/examples/sql/oracle.sql script, which creates both the stored procedures and the Coffee table.

Browsing the CoffeeApp Application

  1. In the GUI deploytool, open the j2eetutorial/examples/ears/CoffeeApp.ear file.
  2. Select the Resource Refs tab of the CoffeeBean component and note the following:

    • The Coded Name of CCIEIS corresponds to the following line in the CoffeeEJB.java source code:

            cf = (ConnectionFactory)
               ic.lookup("java:comp/env/CCIEIS");
      

    • The JNDI Name of eis/CciBlackBoxTx matches the name of the connection factory you added in step 2 of Deploying the Resource Adapter.

    • The User Name and Password fields contain dummy values (XXX), since this EAR file was tested with a Cloudscape database. For other types of databases, you may be required to insert actual values in these fields. For these databases, you should also insert actual values in the Env. Entries tab of CoffeeBean.
  3. Select the JNDI Names tab of CoffeeApp. Note that the CCIEIS value in the Reference Name field has been mapped to the eis/CciBlackBoxTx value in the JNDI Name field.

Resource Refs Tab of the CoffeeApp Application

JNDI Tab of the CoffeeApp Application

Deploying and Running the CoffeeApp Application

  1. Deploy the app.

    1. In the GUI deploytool, select ToolsDeploy.
    2. In the Introduction dialog box, select Return Client Jar.

  2. In a terminal window, go to the j2eetutorial/examples/ears directory.
  3. Set the APPCPATH environment variable to the name of the stub client JAR file: CoffeeAppClient.jar.
  4. Run the client by typing the following on one line.

       runclient -client CoffeeApp.ear -name CoffeeClient 
          -textauth
    
    
  5. At the login prompts, enter guest as the user name and guest123 as the password.
  6. The client should display the following lines:

       Coffee count = 0
       Inserting 3 coffee entries...
       Coffee count = 3