[ Team LiB ] Previous Section Next Section

Writing Resource Adapters

Writing resource adapters is a lot of work. It's not an easy task and should be done by EIS vendors and third-party providers. If you have to actually compile the classes needed for a resource adapter, the Java software for J2EE version 1.3 or greater is required. All the required classes are also included in the weblogic.jar found in the <weblogic_root>\server\lib directory. Sun has a tool that integrates into Sun ONE Studio to facilitate writing resource adapters. This tool is not free, but it could save significant time, especially if more than one adapter is being written. A trial version is downloadable from Sun at http://wwws.sun.com/software/download/index.html.

As previously covered in this chapter, resource adapters are separated into three main areas: connection management, security management, and transaction management. When writing a resource adapter, certain interfaces must be present and correctly implemented for the resource adapter to work in application servers. The implemented interfaces work together with the ra.xml file to provide the three management areas of a resource adapter. In fact, each of these interfaces and some of the objects that they return have to correspond exactly to the entries in the ra.xml file under the <resourceadapter> tag.

NOTE

This section is intended to give an overview of the minimum interfaces needed to write a resource adapter per the J2EE Connector Architecture 1.0 specification. It is meant as an informational illustration to understand resource architecture better, but is not intended as the sole reference for writing resource adapters. For a thorough examination of the subject, see Java Connector Architecture: Building Enterprise Adaptors by Atul Apte from Sams Publishing, ISBN 0672323109.


Interfaces Needed for the Connection

The ManagedConnectionFactory interface retrieves the connection from the EIS system. The main purpose of this interface is to provide a single point to manage EIS-specific ManagedConnection factory instances. This interface includes pooling support in the form of ManagedConnection instance management. Also, the security contract is supported through the createManagedConnection method. This object is identified to the application server with the <managedconnectionfactory-class> tag in the ra.xml file. The Object type returned by the createConnectionFactory methods has to implement the interface written in the <connectionfactory-interface> entry. The actual class returned must correspond to the <connectionfactory-impl-class> tag. Figure 31.3 shows the relationships between the classes in the resource adapter and the ra.xml file.

Figure 31.3. Relationships between the classes in the resource adapter and the ra.xml configuration file.

graphics/31fig03.gif

The following is the interface for ManagedConnectionFactory:


public interface javax.resource.spi.ManagedConnectionFactory
extends java.io.Serializable {
public Object createConnectionFactory(
       ConnectionManager connectionManager) throws ResourceException;
public Object createConnectionFactory() throws ResourceException;
public ManagedConnection createManagedConnection(javax.security.auth.Subject subject,
ConnectionRequestInfo cxRequestInfo)
throws ResourceException;
public ManagedConnection matchManagedConnections(java.util.Set connectionSet,
javax.security.auth.Subject subject,
ConnectionRequestInfo cxRequestInfo) throws ResourceException;
public boolean equals(Object other);
public int hashCode();
}

The ManagedConnection interface provides access to many functions within a resource adapter. The main function of the implemented class is to retrieve the LocalTransaction interface and the XAResource interface for use in transactions. This is also where the ConnectionEventListener is set to notify the application server of transaction events. Using this interface, log writers can be retrieved for writing output to the log files, and they can be substituted with custom writers. The Object returned by the getConnection method must implement the same interface as entered in the <connection-interface> tag in the ra.xml file. The actual class returned must match the <connection-impl-class> tag. The following code is the interface specification. To see this mapping graphically, refer to Figure 31.3 that was shown earlier.


public interface javax.resource.spi.ManagedConnection {
public Object getConnection(javax.security.auth.Subject subject,
                            ConnectionRequestInfo cxRequestInfo)
throws ResourceException;
public void destroy() throws ResourceException;
public void cleanup() throws ResourceException;
public void addConnectionEventListener(ConnectionEventListener listener);
public void removeConnectionEventListener(ConnectionEventListener listener);
public ManagedConnectionMetaData getMetaData() throws ResourceException;
public void associateConnection(java.lang.Object connection) throws ResourceException;
public LocalTransaction getLocalTransaction() throws ResourceException;
public XAResource getXAResource() throws ResourceException;
public java.io.PrintWriter getLogWriter() throws ResourceException;
public void setLogWriter(java.io.PrintWriter out) throws ResourceException;
}

The ManagedConnectionMetaData interface is just a utility class that's used to get information about the resource adapter. The implemented class needs to get information from the ManagedConnection interface about the underlying connection to the EIS system. This is needed specifically for the getUserName() method, which returns the current user. Notice that the specification leaves open how you retrieve this information. The following is the mandatory interface:


public interface javax.resource.spi.ManagedConnectionMetaData {
public String getEISProductName() throws ResourceException;
public String getEISProductVersion() throws ResourceException;
public int getMaxConnections() throws ResourceException;
public String getUserName() throws ResourceException;
}

The ConnectionManager interface is how the resource adapter supplies the application server with the connection request. The implemented class must be serializable and is called by the resource adapter's ManagedConnectionFactory class. The following is the interface and its lone method:


Public interface javax.resource.spi.ConnectionManager extends java.io.Serializable {
public java.lang.Object allocateConnection(ManagedConnectionFactory mcf,
ConnectionRequestInfo cxRequestInfo) throws ResourceException;
}

Transaction Management Contracts

Transaction management contracts between the resource adapter and the application server exist to maintain a standard set of protocols and interfaces. This allows different resource adapters to use the standard transaction support and deploy on any application server that adheres to the J2EE Connector Architecture specification.

Both local and XA transaction support have different contract obligations. If the transaction supports a local transaction, the resource adapter is required to support the LocalTransaction interface, and WebLogic or other application servers in turn support the ConnectionEventListener interface.

  • LocalTransaction— Resource adapter–defined begin, commit, and rollback methods for local transaction management. It is then required to notify the application server's ConnectionEventListener of the action if the transaction is not container managed.

    
    
    public interface javax.resource.spi.LocalTransaction {
    public void begin() throws ResourceException;
    public void commit() throws ResourceException;
    public void rollback() throws ResourceException;
    }
    
  • ConnectionEventListener— Application server's defined listeners for the associated LocalTransaction interface:

    
    
    public interface javax.resource.spi.ConnectionEventListener {
    // Local Transaction Management related events
    public void localTransactionStarted(ConnectionEvent event);
    public void localTransactionCommitted(ConnectionEvent event);
    public void localTransactionRolledback(ConnectionEvent event);
    }
    

With these two contracts in place, transaction management with local support works without a hitch. Because the application server knows of all the events happening in the local transaction, it can clean up any transactions with other components.

XA-compliant contracts are more complex. The specification gives the vendors of EIS systems the choice of being XA compliant, or not, to allow gradual migration to J2EE Connector Architecture. The interface for XA-compliant transactions is the XAResource interface. The following code is the interface for the XAResource:


public interface javax.transaction.xa.XAResource {
public void commit(Xid xid, boolean onePhase) throws XAException;
public void end(Xid xid, int flags) throws XAException;
public void forget(Xid xid) throws XAException;
public int prepare(Xid xid) throws XAException;
public Xid[] recover(int flag) throws XAException;
public void rollback(Xid xid) throws XAException;
public void start(Xid xid, int flags) throws XAException;
}

This interface follows the JTA/XA and JTS specifications. A vendor is not required to support two-phase commits within the resource adapter/resource manager. It is required only to handle the one-phase commit when XAResource.commit(XID, true); is called. This makes it imperative for programmers using vendor-specific connectors to read the documentation of each connector being used before setting up any transaction strategy for a specified system if JTA/XA or JTS is being used. Remember that if only one resource manager is being used, the server optimizes the transaction and uses a one-phase commit local transaction. Distributed transactions requiring two-phase commits need a fully JTA/XA or JTS compatible resource adapter. Consequently, this leaves out several of the most popular EIS systems, such as SAP, because they don't support two-phase commits.

With the connection interfaces in place, we can turn our attention to transaction management. Transaction management is configured by the <transaction-support> tag in ra.xml. Transaction management is not required by a resource adapter because NoTransaction can be used for this tag. If the connector supports local transactions as discussed in the previous section, the LocalTransaction interface must be present. If the transaction level supports XA transactions, both the LocalTransaction and the XAResource interfaces must be implemented and returned by the ManagedConnection implementation. If a transaction level is not supported or not correctly identified, a ResourceException is thrown when the adapter is deployed.

The XAResource interface is needed if the transaction level is set to XATransaction. This interface provides the capability to participate in JTA/XA and JTS transactions.

Error Resolution

The exception handling in a resource adapter is handled in most cases by throwing a javax.resource.ResourceException. This class extends java.lang.Exception and adds three key methods for retrieving, setting, and getting errors from EIS systems. These methods are listed here with a description:

  • getErrorCode()— This method returns a String of the specific vendor error code.

  • getLinkedException()— This method returns a java.lang.Exception linked to a lower-level problem. If there is none, null is returned.

  • setLinkedException(java.lang.Exception e)— This method sets a java.lang.Exception to retrieve via the getLinkedException() to find the lower-level problem.

Putting It All Together

So far in this section, the link between the ra.xml file and the actual interfaces and classes have been shown. The deployment procedure and the client code to connect to the resource adapter have not been shown.

There are two types of applications:

  • Managed applications— The client code relies on the server to access the resource adapter; for example, a J2EE application (Web, EJB, EAR applications).

  • Non-managed applications— The client calls the resource adapter classes directly.

Managed Applications

Figure 31.4 diagrams how a managed application interacts with WebLogic Server, the resource adapter, and the EIS system.

Figure 31.4. Managed connection architecture.

graphics/31fig04.gif

WebLogic Console Resource Connector Access

When a resource adapter is deployed, it has an entry in the WebLogic JNDI lookup. This name corresponds to the <jndi-name> inside the weblogic-ra.xml file. When an application uses this resource adapter (deployed using a managed application approach), there must be an entry in the deployment descriptor similar to this:


<resource-ref>
  <res-ref-name>eis/GAMSJCA_RA </res-ref-name>
  <res-type>javax.sql.DataSource</res-type>
  <res-auth>Application</res-auth>
</resource-ref>

The <res-ref-name> tag of the deployment descriptor of the application and the <jndi-name> tag from the weblogic-ra.xml file should match exactly. When the calling component looks up the JNDI reference, the server calls the class referenced in the ra.xml file under <managedconnectionfactory-class>, which is required to implement ManagedConnectionFactory. The application server then calls the createConnectionFactory method, which returns the interface specified in the <connectionfactory-interface> tag. The following sample code looks up the JNDI interface and maps the return value to the correct value. We assume that the ra.xml file setting for the return interface is javax.sql.DataSource.


InitialContext initCtx = null;
  try {
      initCtx = new InitialContext();
      DataSource ds = (javax.sql.DataSource) initCtx.lookup("GAMSJCA_RA");
  }catch(Exception e){
}

The next step is to obtain the connection from the datasource:


InitialContext initCtx = null;
Connection con = null;
  try {
      initCtx = new InitialContext();
      DataSource ds =
      (javax.sql.DataSource)
      initCtx.lookup("java:comp/env/eis/GAMSJCA_RA");
con = ds.getConnection();

  }catch(Exception e){}

The previous case is a JDBC example. If J2EE-CA implements the optional Common Client Interface (CCI), the code would look very similar, with only different implementing classes:


InitialContext initCtx = null;
javax.resource.cci.Connection con = null;
 try{
    initCtx = new InitialContext();
    javax.resource.cci.ConnectionFactory cf =
    (javax.resource.cci.ConnectionFactory)
    initCtx.lookup("java:comp/env/eis/GAMSJCA_RA");
    con = cf.getConnection();
 }catch(Exception e){}

After you get the connection, perform whatever duties you have to do, such as retrieving, updating, or inserting data into an EIS, and then close the connection like this:


con.close();

This is just an example of a connection. Let's look at doing something useful with an adapter. Listing 31.1 shows an excerpt from the ConnectJCASAPBean.java file in the SAP sample included on the CD that comes with this book. This is a good example of CCI code because we get the connection, create an interaction object to interact with the EIS system, create a mapped record to get the input fields and structures, and use the ResultSet object to insert and retrieve tables for the call to SAP.

Listing 31.1 SAP Connection, Population of Function, and Call
Interaction interaction = connection.createInteraction();

              // create Mapped record for BAPI
              MappedRecord in = connectionfactory.getRecordFactory(). createMappedRecord
                ("BAPI_MATERIAL_GETLIST");
        // Input
           in.put("MAXROWS", String.valueOf(maxrows));
        // Create Result set for Input Parameters
        ResultSet matnrSelection = (ResultSet)in.get("MATNRSELECTION");
        matnrSelection.moveToInsertRow();
        matnrSelection.updateString("SIGN", "I");
        matnrSelection.updateString("OPTION", "CP");
        matnrSelection.updateString("MATNR_LOW", searchtext);
        matnrSelection.insertRow();
       // get MappedRecord for output
       MappedRecord out = (MappedRecord) interaction.execute(null, in);
              // extract results
              ResultSet matnrList = (ResultSet) out.get("MATNRLIST");
              vec = toVector(matnrList);
              interaction.close();
Unmanaged Application

Figure 31.5 shows how an unmanaged application interacts with WebLogic Server, the resource adapter, and the EIS system.

Figure 31.5. Unmanaged connection architecture.

graphics/31fig05.gif

An application does not have to be managed by a server. The J2EE architecture also allows direct access to the resource adapter. In such a case, the code to implement a transaction using CCI would look much like this:


javax.resource.cci.ManagedConnectionFactory mcf = null;
javax.resource.cci.ConnectionFactory cxf = null;
javax.resource.cci.Connection connection = null;

try {
  mcf = new ECIManagedConnectionFactory();
   cxf = (javax.resource.cci.ConnectionFactory) mcf.createConnectionFactory();
   connection = cxf.getConnection();
...//do work
}catch(Exception e){
}
connection.close();

As you see, the main difference is the creation and instantiation of the ManagedConnectionFactory class. Everything else remains constant. Of course, you give up any container-managed features, such as credential mapping, transaction management, and so on. Most of the time, and particularly when using WebLogic, you'll take advantage of managed applications. If you need to call an EIS within a standalone application and don't want WebLogic to manage anything for you, you can still use the resource adapter this way.

    [ Team LiB ] Previous Section Next Section