Connect the enterprise with the JCA, Part 2

Build your own J2EE Connector Architecture adapter

In "Connect the Enterprise with the JCA, Part 1," I introduced JCA (J2EE Connector Architecture), explained how it compares to EAI (enterprise application integration) products, and offered details on JCA interfaces.

JCA, an important new Java standard addressing enterprise integration, provides a framework, much like JDBC (Java Database Connectivity), to read and write data to different enterprise systems such as SAP, PeopleSoft, or Siebel. Reflecting its importance, the latest releases of most J2EE (Java 2 Platform, Enterprise Edition) application servers support JCA.

Read the whole series on the JCA:

In this article, the second of two, I demonstrate how to implement a JCA adapter -- a set of classes with which a J2EE application server targets a particular enterprise system. A JCA adapter functions similarly to how a JDBC driver connects to databases. However, because developing a full-featured JCA adapter is a complex task, I can only scratch the surface in this article. Nevertheless, by the end, you will understand a basic JCA adapter's construction, and grasp the effort required to build your own.

To accomplish those goals, I first describe the sample adapter's capabilities, as well as how to deploy and run it. I then introduce the implementation classes, followed by what occurs when the adapter executes in the container. Finally, I discuss the lessons learned from creating the sample adapter.

Note: To download the myjca.rar source code that accompanies this article, see Resources.

What the sample JCA adapter implementation does (and doesn't) contain

First, it's important to frame the sample-adapter discussion by describing its functionality. This article's sample adapter doesn't actually hook up to an enterprise system; it merely implements the interfaces required to deploy the adapter and look up a connection.

Moreover, the sample adapter implements only those classes required for the JCA specification's connection management section. Further, most of the adapter's method implementations contain print statements that let you see the method calls' order, without hooking up a debugger. The sample adapter does not, however, address transaction and security contacts.

This article's sample adapter does not use the CCI (Common Client Interface) interfaces. It strictly demonstrates the classes necessary to connect to an enterprise system.

What you need to deploy the sample adapter

To use the adapter (or any JCA adapter for that matter), you need a J2EE application server with JCA 1.0 specification support. I used BEA's WebLogic 6.1 server to test the sample adapter; however, other application servers should work.

To deploy the sample adapter with BEA's WebLogic 6.1, you must:

  1. Navigate to the connector tree in the Administration Console
  2. Click on the "Install new connector" link
  3. Navigate to the myjca.rar file, then upload it

In a later section, you'll find instructions on how to compile and build myjca.rar.

The sample class files

Next, let's delve into the Java classes required to implement the sample JCA adapter. The adapter includes two class categories:

  • Managed classes: The application server calls managed classes to perform the connection management. They're needed only if the application server is managing the connection (via a connection pool), a likely situation.
  • Physical connection classes: These required classes, which the aforementioned managed classes may call, establish the connection to the EIS (enterprise information systems).

MyManagedConnectionFactory

With the MyManagedConnectionFactory class, which implements the ManagedConnectionFacytory interface, you create the MyConnectionFactory and MyManagedConnection classes. The MyManagedConnectionFactory class acts as the main entry point for the application server to call into the adapter:

 
package myjca;
import java.io.PrintWriter;
import java.io.Serializable;
import java.sql.DriverManager;
import java.util.Iterator;
import java.util.Set;
import javax.resource.ResourceException;
import javax.resource.spi.*;
import javax.security.auth.Subject;
public class MyManagedConnectionFactory
    implements ManagedConnectionFactory, Serializable
{
    public MyManagedConnectionFactory() {
        System.out.println("In MyManagedConnectionFactory.constructor");
    }
    public Object createConnectionFactory(ConnectionManager cxManager) throws ResourceException {
        System.out.println("In MyManagedConnectionFactory.createConnectionFactory,1");
        return new MyDataSource(this, cxManager);
    }
    public Object createConnectionFactory() throws ResourceException {
              System.out.println("In MyManagedConnectionFactory.createManagedFactory,2");
        return new MyDataSource(this, null);
    }
    public ManagedConnection createManagedConnection(Subject subject, ConnectionRequestInfo info) {
        System.out.println("In MyManagedConnectionFactory.createManagedConnection");
        return new MyManagedConnection(this, "test");
    }
    public ManagedConnection matchManagedConnections(Set connectionSet, Subject subject, ConnectionRequestInfo info)
        throws ResourceException
    {
        System.out.println("In MyManagedConnectionFactory.matchManagedConnections");
        return null;
    }
    public void setLogWriter(PrintWriter out) throws ResourceException {
        System.out.println("In MyManagedConnectionFactory.setLogWriter");
    }
    public PrintWriter getLogWriter() throws ResourceException {
        System.out.println("In MyManagedConnectionFactory.getLogWriter");
        return DriverManager.getLogWriter();
    }
    public boolean equals(Object obj) {
        if(obj == null)
            return false;
        if(obj instanceof MyManagedConnectionFactory)
        {
            int hash1 = ((MyManagedConnectionFactory)obj).hashCode();
            int hash2 = hashCode();
            return hash1 == hash2;
        }
        else
        {
            return false;
        }
    }
    public int hashCode()
    {
              return 1;
    }
}

MyManagedConnection

The MyManagedConnection class implements the ManagedConnection interface. MyManagedConnection encapsulates the adapter's physical connection, in this case the MyConnection class:

package myjca;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.*;
import javax.resource.NotSupportedException;
import javax.resource.ResourceException;
import javax.resource.spi.*;
import javax.security.auth.Subject;
import javax.transaction.xa.XAResource;
public class MyManagedConnection
  implements ManagedConnection
{
     private MyConnectionEventListener myListener;
     private String user;
     private ManagedConnectionFactory mcf;
     private PrintWriter logWriter;
     private boolean destroyed;
     private Set connectionSet;
  MyManagedConnection(ManagedConnectionFactory mcf, String user)
  {
    System.out.println("In MyManagedConnection");
    this.mcf = mcf;
    this.user = user;
    connectionSet = new HashSet();
    myListener = new MyConnectionEventListener(this);
  }
  private void throwResourceException(SQLException ex)
    throws ResourceException
  {
    ResourceException re = new ResourceException("SQLException: " +
ex.getMessage());
    re.setLinkedException(ex);
    throw re;
  }
  public Object getConnection(Subject subject, ConnectionRequestInfo
connectionRequestInfo)
    throws ResourceException
  {
    System.out.println("In MyManagedConnection.getConnection");
    MyConnection myCon = new MyConnection(this);
    addMyConnection(myCon);
    return myCon;
  }
  public void destroy()
  {
        System.out.println("In MyManagedConnection.destroy");
        destroyed = true;
  }
  public void cleanup()
   {
        System.out.println("In MyManagedConnection.cleanup");
  }
  public void associateConnection(Object connection)
   {
        System.out.println("In MyManagedConnection.associateConnection");
  }
  public void addConnectionEventListener(ConnectionEventListener listener)
  {
        System.out.println("In MyManagedConnection.addConnectionEventListener");
    myListener.addConnectorListener(listener);
  }
  public void removeConnectionEventListener(ConnectionEventListener listener)
  {
        System.out.println("In MyManagedConnection.removeConnectionEventListener");
    myListener.removeConnectorListener(listener);
  }
  public XAResource getXAResource()
    throws ResourceException
  {
        System.out.println("In MyManagedConnection.getXAResource");
    return null;
  }
  public LocalTransaction getLocalTransaction()
  {
           System.out.println("In MyManagedConnection.getLocalTransaction");
      return null;
  }
  public ManagedConnectionMetaData getMetaData()
    throws ResourceException
  {
    System.out.println("In MyManagedConnection.getMetaData");
    return new MyConnectionMetaData(this);
  }
  public void setLogWriter(PrintWriter out)
    throws ResourceException
  {
        System.out.println("In MyManagedConnection.setLogWriter");
    logWriter = out;
  }
  public PrintWriter getLogWriter()
    throws ResourceException
  {
        System.out.println("In MyManagedConnection.getLogWriter");
    return logWriter;
  }
  Connection getMyConnection()
    throws ResourceException
  {
        System.out.println("In MyManagedConnection.getMyConnection");
    return null;
  }
  boolean isDestroyed()
  {
        System.out.println("In MyManagedConnection.isDestroyed");
    return destroyed;
  }
  String getUserName()
  {
        System.out.println("In MyManagedConnection.getUserName");
    return user;
  }
  void sendEvent(int eventType, Exception ex)
  {
        System.out.println("In MyManagedConnection.sendEvent,1");
    myListener.sendEvent(eventType, ex, null);
  }
  void sendEvent(int eventType, Exception ex, Object connectionHandle)
  {
        System.out.println("In MyManagedConnection.sendEvent,2 ");
    myListener.sendEvent(eventType, ex, connectionHandle);
  }
  void removeMyConnection(MyConnection myCon)
  {
        System.out.println("In MyManagedConnection.removeMyConnection");
    connectionSet.remove(myCon);
  }
  void addMyConnection(MyConnection myCon)
  {
        System.out.println("In MyManagedConnection.addMyConnection");
    connectionSet.add(myCon);
  }
  ManagedConnectionFactory getManagedConnectionFactory()
  {
        System.out.println("In MyManagedConnection.getManagedConnectionFactory");
    return mcf;
  }
}

MyConnectionEventListener

For its part, the MyConnectionEventListener class allows the application server to register callbacks for the adapter. The application server can then perform operations, connection-pool maintenance, for example, based on the connection state:

package myjca;
import java.util.Vector;
import javax.resource.spi.ConnectionEvent;
import javax.resource.spi.ConnectionEventListener;
import javax.resource.spi.ManagedConnection;
public class MyConnectionEventListener
    implements javax.sql.ConnectionEventListener
{
    private Vector listeners;
    private ManagedConnection mcon;
    public MyConnectionEventListener(ManagedConnection mcon)
    {
        System.out.println("In MyConnectionEventListener");
        this.mcon = mcon;
    }
    public void sendEvent(int eventType, Exception ex, Object connectionHandle)
    {
        System.out.println("In MyConnectionEventListener.sendEvent");
    }
    public void addConnectorListener(ConnectionEventListener l)
    {
        System.out.println("In MyConnectionEventListener.addConnectorListener");
    }
    public void removeConnectorListener(ConnectionEventListener l)
    {
        System.out.println("In MyConnectionEventListener.removeConnectorListener");
    }
    public void connectionClosed(javax.sql.ConnectionEvent connectionevent)
    {
        System.out.println("In MyConnectionEventListener.connectorClosed");
    }
    public void connectionErrorOccurred(javax.sql.ConnectionEvent event)
    {
        System.out.println("In MyConnectionEventListener.connectorErrorOccurred");
    }
}

MyConnectionMetaData

The MyConnectionMetaData class provides meta information -- product name, the maximum number of connections allowed, and so on -- regarding the managed connection and the underlying physical connection class:

package myjca;
import javax.resource.ResourceException;
import javax.resource.spi.*;
public class MyConnectionMetaData
    implements ManagedConnectionMetaData
{
    private MyManagedConnection mc;
    public MyConnectionMetaData(MyManagedConnection mc)
    {
        System.out.println("In MyConnectionMetaData.constructor");
        this.mc = mc;
    }
    public String getEISProductName()
        throws ResourceException
    {
        System.out.println("In MyConnectionMetaData.getEISProductName");
        return "myJCA";
    }
    public String getEISProductVersion()
        throws ResourceException
    {
        System.out.println("In MyConnectionMetaData.getEISProductVersion");
        return "1.0";
    }
    public int getMaxConnections()
        throws ResourceException
    {
            System.out.println("In MyConnectionMetaData.getMaxConnections");
            return 5;
    }
    public String getUserName()
        throws ResourceException
    {
           return mc.getUserName();
    }
}
1 2 Page 1