J2EE Connector Architecture

Objectives

We will show you how to develop an deploy a JCA 1.5 compliant connector in jBoss 4.

Introduction

JCA (also known as J2EE Connector Architecture) is a specification that defines a standard API to develop components that are used to access Enterprise Information Systems such as ERP, mainframes, TP monitors, databases, .. and a standard way to deploy these components. jBoss 4 supports JCA 1.5.

Project

The project you are working on needs connectivity to a server. That server can only be accessed by socket. J2EE 1.4 has been choosen as the middleware for the whole project. No J2EE application server has been selected yet, and the architect decides to develop a JCA 1.5 compliant component to access this server, component that could be deployed in any J2EE 1.4 compliant application server.




This EIS Server will be simulated by a simple java.net.ServerSocket that will send heart beat messages to the JCA component that will send them to a MDB. A stateless session bean will be used to publish information to the server via the JCA component (and the use of CCI interfaces).

"Inbound" deployment

We will know develop in asynchronous part of the system: when messages are sent from the EIS server to the J2EE application server.

ra.xml: The JCA configuration file


<?xml version="1.0" encoding="UTF-8"?>

<!-- (C) 2004 - Jyperion Lt - @author Thierry Janaudy -->

<connector xmlns="http://java.sun.com/xml/ns/j2ee"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
           http://java.sun.com/xml/ns/j2ee/connector_1_5.xsd"
           version="1.5">

   <description>Serveur EIS Resource Adapter</description>
   <display-name>EISJCAAdapter</display-name>

   <vendor-name>Jyperion Ltd</vendor-name>
   <eis-type>Serveur EIS</eis-type>
   <resourceadapter-version>4.0</resourceadapter-version>

   <license>
      <description>Copyright (c) 2004 Jyperion Ltd</description>
      <license-required>false</license-required>
   </license>

   <resourceadapter>
     <resourceadapter-class>org.jyperion.eis.jca.spi.EISResourceAdapter</resourceadapter-class>
     
     <config-property>
       <config-property-name>eisHost</config-property-name>
       <config-property-type>java.lang.String</config-property-type>
       <config-property-value>localhost</config-property-value>
     </config-property>
     <config-property>
       <config-property-name>eisPort</config-property-name>
       <config-property-type>java.lang.Integer</config-property-type>
       <config-property-value>23067</config-property-value>
     </config-property>
           
      <inbound-resourceadapter>
         <messageadapter>        
            <messagelistener>
               <messagelistener-type>org.jyperion.eis.jca.inbound.EISListener</messagelistener-type>
               <activationspec>
                  <activationspec-class>org.jyperion.eis.jca.spi.EISActivationSpec</activationspec-class>
               </activationspec>
            </messagelistener>
         </messageadapter>
      </inbound-resourceadapter>
       
   </resourceadapter>
</connector>

UML View: SPI (Service Provider Interface)

EISResourceAdapter

When endpointActivation will be invoked, an instance of EISActivationSpec will be created to manage the connection with the EIS Server. The EISXAResource instance will publish the received message using the method defined in the EISListener interface.

package org.jyperion.eis.jca.spi;

import javax.resource.ResourceException;
import javax.resource.spi.ActivationSpec;
import javax.resource.spi.BootstrapContext;
import javax.resource.spi.ResourceAdapter;
import javax.resource.spi.ResourceAdapterInternalException;
import javax.resource.spi.endpoint.MessageEndpoint;
import javax.resource.spi.endpoint.MessageEndpointFactory;
import javax.transaction.xa.XAResource;

/**
 * EISResourceAdapter
 * 
 * (C) 2004 - Jyperion Ltd
 * @author Thierry Janaudy janaudy@jyperion.com
 */
public class EISResourceAdapter implements ResourceAdapter {
    /**
     * BootstrapContext
     */
    private BootstrapContext bootstrapContext = null;
    
    /**
     * eisHost
     */
    private String eisHost = null;
    
    /**
     * eisPort
     */
    private Integer eisPort = null;
    
    /**
     * EISActivationSpec
     */
    private EISActivationSpec eisActivationSpec = null;
    
    /**
     * EISXAResource
     */
    private EISXAResource eisXAResource = null;
    
    /**
     * @return Returns the eisHost.
     */
    public String getEisHost() {
        return eisHost;
    }
    
    /**
     * @param eisHost The eisHost to set.
     */
    public void seteisHost(String eisHost) {
        System.out.println("EISResourceAdapter/seteisHost to " + eisHost);
        this.eisHost = eisHost;
    }
    
    /**
     * @return Returns the eisPort.
     */
    public Integer getEisPort() {
        return eisPort;
    }
    
    /**
     * @param eisPort The eisPort to set.
     */
    public void seteisPort(Integer eisPort) {
        System.out.println("EISResourceAdapter/seteisPort to " + eisPort);
        this.eisPort = eisPort;
    }
    
    /**
     * @return Returns the eisActivationSpec.
     */
    public EISActivationSpec getEisActivationSpec() {
        return eisActivationSpec;
    }
    
    /**
     * @param eisActivationSpec The eisActivationSpec to set.
     */
    public void setEisActivationSpec(EISActivationSpec eisActivationSpec) {
        this.eisActivationSpec = eisActivationSpec;
    }
    
    /**
     * @return Returns the eisXAResource.
     */
    public EISXAResource getEisXAResource() {
        return eisXAResource;
    }
    
    /**
     * @param eisXAResource The eisXAResource to set.
     */
    public void setEisXAResource(EISXAResource eisXAResource) {
        this.eisXAResource = eisXAResource;
    }
    
    /**
     * @see javax.resource.spi.ResourceAdapter#start(javax.resource.spi.BootstrapContext)
     */
    public void start(BootstrapContext bootstrapContext) throws ResourceAdapterInternalException {
        System.out.println("EISResourceAdapter/start " + bootstrapContext);
        this.bootstrapContext = bootstrapContext;
    }

    /**
     * @see javax.resource.spi.ResourceAdapter#stop()
     */
    public void stop() {
        System.out.println("EISResourceAdapter/stop");
    }

    /**
     * @see javax.resource.spi.ResourceAdapter#endpointActivation(javax.resource.spi.endpoint.MessageEndpointFactory, javax.resource.spi.ActivationSpec)
     */
    public void endpointActivation(MessageEndpointFactory messageEndpointFactory, 
            ActivationSpec activationSpec) throws ResourceException {
        System.out.println("EISResourceAdapter/endpointActivation " + messageEndpointFactory + 
                ", " + activationSpec);
        
        this.eisActivationSpec = (EISActivationSpec)activationSpec;
        this.eisXAResource = new EISXAResource();
        MessageEndpoint messageEndpoint = messageEndpointFactory.createEndpoint(this.eisXAResource);
        this.eisXAResource.setEndpoint(messageEndpoint);
        
        try {
            eisActivationSpec.connectToEIS();
        } catch (Exception e) {
            throw new ResourceException("EIS Server down, or wrong host, or wrong port: " + e.getMessage());
        }
    }

    /**
     * @see javax.resource.spi.ResourceAdapter#endpointDeactivation(javax.resource.spi.endpoint.MessageEndpointFactory, javax.resource.spi.ActivationSpec)
     */
    public void endpointDeactivation(MessageEndpointFactory messageEndpointFactory, 
            ActivationSpec activationSpec) {
        System.out.println("EISResourceAdapter/endpointDeactivation " + messageEndpointFactory + 
                ", " + activationSpec);
        
        try {
            if (this.eisActivationSpec != null) {
                this.eisActivationSpec.closeConnectionToEIS();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * @see javax.resource.spi.ResourceAdapter#getXAResources(javax.resource.spi.ActivationSpec[])
     */
    public XAResource[] getXAResources(ActivationSpec[] activationSpec) throws ResourceException {
        System.out.println("EISResourceAdapter/getXAResources " + activationSpec);
        return null;
    }

}

EISActivationSpec


package org.jyperion.eis.jca.spi;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;

import javax.resource.ResourceException;
import javax.resource.spi.ActivationSpec;
import javax.resource.spi.InvalidPropertyException;
import javax.resource.spi.ResourceAdapter;

/**
 * EISActivationSpec
 * 
 * (C) 2004 - Jyperion Ltd
 * @author Thierry Janaudy janaudy@jyperion.com
 */
public class EISActivationSpec implements ActivationSpec {
    /**
     * ResourceAdapter
     */
    private ResourceAdapter resourceAdapter = null;
    
    /**
     * Socket
     */
    private Socket socket = null;
    
    
    /**
     * connectToEIS
     * @throws Exception
     */
    public void connectToEIS() throws Exception {
        String host = ((EISResourceAdapter)this.resourceAdapter).getEisHost();
        Integer port = ((EISResourceAdapter)this.resourceAdapter).getEisPort();
        System.out.println("EISActivationSpec/connectToEIS " + host + ":" + port);
        this.socket = new Socket(host, port.intValue());
        new MessageListenerThread();
    }
    
    /**
     * getOutputStream
     * @return
     * @throws Exception
     */
    public OutputStream getOutputStream() throws Exception {
        return this.socket.getOutputStream();
    }
    
    /**
     * MessageListenerThread
     * @author janaudyt
     */
    class MessageListenerThread extends Thread {
        /**
         * MessageListenerThread
         */
        public MessageListenerThread() {
            start();
        }

        /**
         * @see java.lang.Runnable#run()
         */
        public void run() {
            try {
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                EISXAResource eisXAResource = ((EISResourceAdapter)resourceAdapter).getEisXAResource();
                System.out.println("MessageListenerThread/run " + eisXAResource);
                String message = null;
                while((message = bufferedReader.readLine()) != null) {
                    eisXAResource.sendReceivedMessageToMDB(message);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    /**
     * closeConnectionToEIS
     * @throws Exception
     */
    public void closeConnectionToEIS() throws Exception {
        if (this.socket != null && this.socket.isConnected()) {
            this.socket.close();
        }
    }
    
    /**
     * @see javax.resource.spi.ActivationSpec#validate()
     */
    public void validate() throws InvalidPropertyException {
        System.out.println("EISActivationSpec/validate");
    }

    /**
     * @see javax.resource.spi.ResourceAdapterAssociation#getResourceAdapter()
     */
    public ResourceAdapter getResourceAdapter() {
        System.out.println("EISActivationSpec/getResourceAdapter");
        return this.resourceAdapter;
    }

    /**
     * @see javax.resource.spi.ResourceAdapterAssociation#setResourceAdapter(javax.resource.spi.ResourceAdapter)
     */
    public void setResourceAdapter(ResourceAdapter resourceAdapter) throws ResourceException {
        this.resourceAdapter = resourceAdapter;
        System.out.println("EISActivationSpec/setResourceAdapter " + resourceAdapter);
    }
}

EISXAResource


package org.jyperion.eis.jca.spi;

import java.lang.reflect.Method;

import javax.resource.spi.endpoint.MessageEndpoint;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;

import org.jyperion.eis.jca.inbound.EISListener;

/**
 * EISXAResource
 * 
 * (C) 2004 - Jyperion Ltd
 * @author Thierry Janaudy janaudy@jyperion.com
 */
public class EISXAResource implements XAResource {
    /**
     * Method defined in the EISListener interface
     * @see org.jyperion.eis.jca.inbound.EISListener
     */
    public static final String METHOD_NAME = "processMessage";
    
    /**
     * METHOD
     */
    private static Method processMessageMethod = null;
    
    /**
     * Looking up the method
     */
    static {
        try {
            processMessageMethod = EISListener.class.getMethod(METHOD_NAME, new Class[]{Object.class});
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
    
    /**
     * MessageEndpoint
     */
    private MessageEndpoint messageEndpoint = null;
    
    /**
     * setEndpoint
     * @param messageEndpoint
     */
    public void setEndpoint(MessageEndpoint messageEndpoint) {
        this.messageEndpoint = messageEndpoint;
        System.out.println("EISXAResource/setEndpoint " + messageEndpoint); 
    }
    
    /**
     * sendReceivedMessageToMDB
     * @param message
     */
    public void sendReceivedMessageToMDB(Object message) {
        System.out.println("EISXAResource/sendReceivedMessageToMDB " + message);
        try {
            this.messageEndpoint.beforeDelivery( processMessageMethod );
            ((EISListener)this.messageEndpoint).processMessage( message );
            this.messageEndpoint.afterDelivery();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    /**
     * @see javax.transaction.xa.XAResource#getTransactionTimeout()
     */
    public int getTransactionTimeout() throws XAException {
        return 10;
    }

    /**
     * @see javax.transaction.xa.XAResource#setTransactionTimeout(int)
     */
    public boolean setTransactionTimeout(int transactionTimeout) throws XAException {
        return false;
    }

    /**
     * @see javax.transaction.xa.XAResource#isSameRM(javax.transaction.xa.XAResource)
     */
    public boolean isSameRM(XAResource xaResource) throws XAException {
        return true;
    }

    /**
     * @see javax.transaction.xa.XAResource#recover(int)
     */
    public Xid[] recover(int arg0) throws XAException {
        return null;
    }

    /**
     * @see javax.transaction.xa.XAResource#prepare(javax.transaction.xa.Xid)
     */
    public int prepare(Xid arg0) throws XAException {
        return 0;
    }

    /**
     * @see javax.transaction.xa.XAResource#forget(javax.transaction.xa.Xid)
     */
    public void forget(Xid arg0) throws XAException {
    }

    /**
     * @see javax.transaction.xa.XAResource#rollback(javax.transaction.xa.Xid)
     */
    public void rollback(Xid arg0) throws XAException {
    }

    /**
     * @see javax.transaction.xa.XAResource#end(javax.transaction.xa.Xid, int)
     */
    public void end(Xid arg0, int arg1) throws XAException {
    }

    /**
     * @see javax.transaction.xa.XAResource#start(javax.transaction.xa.Xid, int)
     */
    public void start(Xid arg0, int arg1) throws XAException {
    }

    /**
     * @see javax.transaction.xa.XAResource#commit(javax.transaction.xa.Xid, boolean)
     */
    public void commit(Xid arg0, boolean arg1) throws XAException {
    }
}

Deployment

The deployment is identical to a EJB jar file except that instead of a ejb-jar.xml file you haave a ra.xml file (decribed earlier).

Just start the EIS Server (See labs in src-server) and deploy our classes and ra.xml packaged into a eisjca.rar file into the deploy folder of jBoss.

11:08:38,746 INFO [STDOUT] EISResourceAdapter/seteisPort to 23067 
11:08:38,760 INFO [STDOUT] EISResourceAdapter/seteisHost to localhost 
11:08:38,761 INFO [STDOUT] EISResourceAdapter/start org.jboss.resource.deployment.RARDeployment@3106b6 

Message-Driven Bean

UML View: Message-Driven Bean


Messages received by the JCA component will be processed by a MDB.

package org.jyperion.eis.mdb;

import javax.ejb.EJBException;
import javax.ejb.MessageDrivenBean;
import javax.ejb.MessageDrivenContext;

import org.jyperion.eis.jca.inbound.EISListener;

/**
 * EISMDBListener
 * 
 * (C) 2004 - Jyperion Ltd
 * @author Thierry Janaudy janaudy@jyperion.com
 */
public class EISMDBListener implements MessageDrivenBean, EISListener {
    /**
     * MessageDrivenContext
     */
    private MessageDrivenContext messageDrivenContext = null;
    
    /**
     * ejbCreate
     */
    public void ejbCreate() {        
    }
    
    /**
     * @see javax.ejb.MessageDrivenBean#setMessageDrivenContext(javax.ejb.MessageDrivenContext)
     */
    public void setMessageDrivenContext(MessageDrivenContext messageDrivenContext) throws EJBException {
        System.out.println("EISMDBListener/setMessageDrivenContext " + messageDrivenContext);
        this.messageDrivenContext = messageDrivenContext;
    }

    /**
     * @see javax.ejb.MessageDrivenBean#ejbRemove()
     */
    public void ejbRemove() throws EJBException {
        System.out.println("EISMDBListener/ejbRemove");
    }

    /**
     * @see org.jyperion.eis.jca.inbound.EISListener#processMessage(java.lang.Object)
     */
    public void processMessage(Object msg) {
        System.out.println("EISMDBListener/processMessage " + msg);
    }
}
ejb-jar.xml

<?xml version="1.0" encoding="UTF-8"?>

<ejb-jar xmlns="http://java.sun.com/xml/ns/j2ee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
         http://java.sun.com/xml/ns/j2ee/ejb-jar_2_1.xsd"
         version="2.1">

   <enterprise-beans>
      <message-driven>
         <ejb-name>EISMDB</ejb-name>
         <ejb-class>org.jyperion.eis.mdb.EISMDBListener</ejb-class>
         <messaging-type>org.jyperion.eis.jca.inbound.EISListener</messaging-type>
        <transaction-type>Container</transaction-type>
      </message-driven>
   </enterprise-beans>

   <assembly-descriptor>
      <container-transaction>
         <method>
            <ejb-name>EISMDB</ejb-name>
            <method-name>*</method-name>
         </method>
         <trans-attribute>Required</trans-attribute>
      </container-transaction>
   </assembly-descriptor>

</ejb-jar>
jboss.xml

<?xml version="1.0" encoding="UTF-8"?>

<jboss>
   <enterprise-beans>
      <message-driven>
         <ejb-name>EISMDB</ejb-name>
         <destination-jndi-name>EISMDB</destination-jndi-name>
         <resource-adapter-name>eisjca.rar</resource-adapter-name>
      </message-driven>
   </enterprise-beans>
</jboss>

"Outbound" deployment

A standalone client will invoke a method on a stateless session bean that will use the CCI interfaces to send a message to the EIS server.

ra.xml

Here is the full ra.xml. Now notice the outbound tag (in red):

<?xml version="1.0" encoding="UTF-8"?>

<!-- (C) 2004 - Jyperion Lt - @author Thierry Janaudy -->

<connector xmlns="http://java.sun.com/xml/ns/j2ee"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
           http://java.sun.com/xml/ns/j2ee/connector_1_5.xsd"
           version="1.5">

   <description>Serveur EIS Resource Adapter</description>
   <display-name>EISJCAAdapter</display-name>

   <vendor-name>Jyperion Ltd</vendor-name>
   <eis-type>Serveur EIS</eis-type>
   <resourceadapter-version>4.0</resourceadapter-version>

   <license>
      <description>Copyright (c) 2004 Jyperion Ltd</description>
      <license-required>false</license-required>
   </license>

   <resourceadapter>
     <resourceadapter-class>org.jyperion.eis.jca.spi.EISResourceAdapter</resourceadapter-class>
     
     <config-property>
       <config-property-name>eisHost</config-property-name>
       <config-property-type>java.lang.String</config-property-type>
       <config-property-value>localhost</config-property-value>
     </config-property>
     <config-property>
       <config-property-name>eisPort</config-property-name>
       <config-property-type>java.lang.Integer</config-property-type>
       <config-property-value>23067</config-property-value>
     </config-property>
     
     <outbound-resourceadapter>
       <connection-definition>
         <managedconnectionfactory-class>org.jyperion.eis.jca.spi.EISManagedConnectionFactory</managedconnectionfactory-class>
            
         <connectionfactory-interface>javax.resource.cci.ConnectionFactory</connectionfactory-interface>
         <connectionfactory-impl-class>org.jyperion.eis.jca.cci.EISConnectionFactory</connectionfactory-impl-class>
         
         <connection-interface>javax.resource.cci.Connection</connection-interface>
         <connection-impl-class>org.jyperion.eis.jca.cci.EISConnection</connection-impl-class>
       </connection-definition>
         
       <transaction-support>NoTransaction</transaction-support>
         
       <reauthentication-support>false</reauthentication-support>
     </outbound-resourceadapter>
           
      <inbound-resourceadapter>
         <messageadapter>        
            <messagelistener>
               <messagelistener-type>org.jyperion.eis.jca.inbound.EISListener</messagelistener-type>
               <activationspec>
                  <activationspec-class>org.jyperion.eis.jca.spi.EISActivationSpec</activationspec-class>
               </activationspec>
            </messagelistener>
         </messageadapter>
      </inbound-resourceadapter>
       
   </resourceadapter>
</connector>

eisjca-ds.xml

To deploy in the jBoss deploy folder.

<?xml version="1.0" encoding="UTF-8"?>

<connection-factories>
   <no-tx-connection-factory>
      <jndi-name>ra/EISJCA</jndi-name>
      <rar-name>eisjca.rar</rar-name>
      <connection-definition>javax.resource.cci.ConnectionFactory</connection-definition>
      <adapter-display-name>EISJCAAdapter</adapter-display-name>
   </no-tx-connection-factory>
</connection-factories>

EISManagedConnectionFactory

EISManagedConnectionFactory is a class that implements the javax.resource.spi.ManagedConnectionFactory interface. The methodcreateConnectionFactory will be called by the container to get an instance of the class EISConnectionFactory that implements the javax.resource.cci.ConnectionFactory interface.

package org.jyperion.eis.jca.spi;

import java.io.PrintWriter;
import java.util.Set;

import javax.resource.ResourceException;
import javax.resource.spi.ConnectionManager;
import javax.resource.spi.ConnectionRequestInfo;
import javax.resource.spi.ManagedConnection;
import javax.resource.spi.ManagedConnectionFactory;
import javax.resource.spi.ResourceAdapter;
import javax.resource.spi.ResourceAdapterAssociation;
import javax.security.auth.Subject;

import org.jyperion.eis.jca.cci.EISConnectionFactory;

/**
 * EISManagedConnectionFactory
 * 
 * (C) 2004 - Jyperion Ltd
 * @author Thierry Janaudy janaudy@jyperion.com
 */
public class EISManagedConnectionFactory implements ManagedConnectionFactory, ResourceAdapterAssociation {
    /**
     * ResourceAdapter
     */
    private ResourceAdapter resourceAdapter = null;
    
    /**
     * @see javax.resource.spi.ManagedConnectionFactory#createConnectionFactory(javax.resource.spi.ConnectionManager)
     */
    public Object createConnectionFactory(ConnectionManager connectionManager) throws ResourceException {
        System.out.println("EISManagedConnectionFactory/createConnectionFactory " + connectionManager);
        return new EISConnectionFactory(this, connectionManager);
    }

    /**
     * @see javax.resource.spi.ManagedConnectionFactory#createConnectionFactory()
     */
    public Object createConnectionFactory() throws ResourceException {
        System.out.println("EISManagedConnectionFactory/createConnectionFactory");
        return null;
    }

    /**
     * @see javax.resource.spi.ManagedConnectionFactory#createManagedConnection(javax.security.auth.Subject, javax.resource.spi.ConnectionRequestInfo)
     */
    public ManagedConnection createManagedConnection(Subject subject, 
            ConnectionRequestInfo connectionRequestInfo) throws ResourceException {
        System.out.println("EISManagedConnectionFactory/createManagedConnection " + subject + ", " +
                connectionRequestInfo);
        return null;
    }

    /**
     * @see javax.resource.spi.ManagedConnectionFactory#matchManagedConnections(java.util.Set, javax.security.auth.Subject, javax.resource.spi.ConnectionRequestInfo)
     */
    public ManagedConnection matchManagedConnections(Set set, Subject subject,
            ConnectionRequestInfo connectionRequestInfo) throws ResourceException {
        System.out.println("EISManagedConnectionFactory/matchManagedConnections " + set + ", " + subject + ", " +
                connectionRequestInfo);
        return null;
    }

    /**
     * @see javax.resource.spi.ManagedConnectionFactory#setLogWriter(java.io.PrintWriter)
     */
    public void setLogWriter(PrintWriter printWriter) throws ResourceException {
        System.out.println("EISManagedConnectionFactory/setLogWriter " + printWriter);
    }

    /**
     * @see javax.resource.spi.ManagedConnectionFactory#getLogWriter()
     */
    public PrintWriter getLogWriter() throws ResourceException {
        System.out.println("EISManagedConnectionFactory/getLogWriter");
        return null;
    }

    /**
     * @see javax.resource.spi.ResourceAdapterAssociation#getResourceAdapter()
     */
    public ResourceAdapter getResourceAdapter() {
        System.out.println("EISManagedConnectionFactory/getResourceAdapter");
        return this.resourceAdapter;
    }

    /**
     * @see javax.resource.spi.ResourceAdapterAssociation#setResourceAdapter(javax.resource.spi.ResourceAdapter)
     */
    public void setResourceAdapter(ResourceAdapter resourceAdapter) throws ResourceException {
        System.out.println("EISManagedConnectionFactory/setResourceAdapter " + resourceAdapter);
        this.resourceAdapter = resourceAdapter;
    }
}

EISBean: A stateless session bean

UML View: Stateless Session Bean


This bean will be called by a standalone client to send messages to the EIS Server.

package org.jyperion.eis.jca.ejb;

import java.rmi.RemoteException;

import javax.ejb.EJBException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
import javax.naming.InitialContext;
import javax.resource.cci.Connection;
import javax.resource.cci.ConnectionFactory;
import javax.resource.cci.Interaction;
import javax.resource.cci.MappedRecord;
import javax.resource.cci.Record;
import javax.resource.cci.RecordFactory;

/**
 * PoC for a JCA 1.5 compliant adapter for an EIS Server
 * 
 * @ejb.bean
 *   name = "EIS"
 *   type = "Stateless"
 *   jndi-name = "ejb/EISBean"
 *   view-type = "remote"
 * 
 * (C) 2004 - Jyperion Ltd
 * @author Thierry Janaudy janaudy@jyperion.com
 */
public class EISBean implements SessionBean {
    /**
     * ejbCreate
     */
    public void ejbCreate() {
        System.out.println("EISBean/ejbCreate");
    }
    
    /**
     * @ejb.interface-method
     */
    public void execJCATest(String raJndiName) {
        System.out.println("EISBean/execJCATest " + raJndiName);
        
        try {
            InitialContext ic = new InitialContext();
            Object obj = ic.lookup(raJndiName);
            System.out.println("EISBean/execJCATest obj = " + obj);
            ConnectionFactory connectionFactory = (ConnectionFactory)obj;
            Connection connection = connectionFactory.getConnection();
            System.out.println("EISBean/execJCATest connection = " + connection);
            Interaction interaction = connection.createInteraction();
            System.out.println("EISBean/execJCATest interaction = " + interaction);
            RecordFactory recordFactory = connectionFactory.getRecordFactory();
            System.out.println("EISBean/execJCATest recordFactory = " + recordFactory);
            MappedRecord mappedRecord = recordFactory.createMappedRecord("EISTestRecord");
            System.out.println("EISBean/execJCATest mappedRecord = " + mappedRecord);
            Record resultRecord = interaction.execute(null, mappedRecord);
            System.out.println("EISBean/execJCATest resultRecord = " + resultRecord);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    /**
     * @see javax.ejb.SessionBean#setSessionContext(javax.ejb.SessionContext)
     */
    public void setSessionContext(SessionContext sessionContext) throws EJBException, RemoteException {
        System.out.println("EISBean/setSessionContext " + sessionContext);
    }

    /**
     * @see javax.ejb.SessionBean#ejbRemove()
     */
    public void ejbRemove() throws EJBException, RemoteException {
        System.out.println("EISBean/ejbRemove");
    }

    /**
     * @see javax.ejb.SessionBean#ejbActivate()
     */
    public void ejbActivate() throws EJBException, RemoteException {
        System.out.println("EISBean/ejbActivate");
    }

    /**
     * @see javax.ejb.SessionBean#ejbPassivate()
     */
    public void ejbPassivate() throws EJBException, RemoteException {
        System.out.println("EISBean/ejbPassivate");
    }
}

EISConnectionFactory

UML View: CCI (Common Client Interface)



package org.jyperion.eis.jca.cci;

import javax.naming.NamingException;
import javax.naming.Reference;
import javax.resource.ResourceException;
import javax.resource.cci.Connection;
import javax.resource.cci.ConnectionFactory;
import javax.resource.cci.ConnectionSpec;
import javax.resource.cci.RecordFactory;
import javax.resource.cci.ResourceAdapterMetaData;
import javax.resource.spi.ConnectionManager;
import javax.resource.spi.ResourceAdapter;

import org.jyperion.eis.jca.spi.EISManagedConnectionFactory;
import org.jyperion.eis.jca.spi.EISResourceAdapter;

/**
 * EISConnectionFactory
 * 
 * (C) 2004 - Jyperion Ltd
 * @author Thierry Janaudy janaudy@jyperion.com
 */
public class EISConnectionFactory implements ConnectionFactory {
    /**
     * EISManagedConnectionFactory
     */
    private EISManagedConnectionFactory eisManagedConnectionFactory = null;
    
    /**
     * ConnectionManager
     */
    private ConnectionManager connectionManager = null;
    
    /**
     * EISConnection
     */
    private EISConnection eisConnection = null;
    
    /**
     * EISConnectionFactory
     * @param eisManagedConnectionFactory
     * @param connectionManager
     */
    public EISConnectionFactory(EISManagedConnectionFactory eisManagedConnectionFactory,
            ConnectionManager connectionManager) {
        System.out.println("EISConnectionFactory/EISConnectionFactory " + eisManagedConnectionFactory + 
                ", " + connectionManager);
        this.eisManagedConnectionFactory = eisManagedConnectionFactory;
        this.connectionManager = connectionManager;
        this.eisConnection = new EISConnection();
    }
    
    /**
     * @see javax.resource.cci.ConnectionFactory#getConnection()
     */
    public Connection getConnection() throws ResourceException {
        System.out.println("EISConnectionFactory/getConnection");
        ResourceAdapter ra = this.eisManagedConnectionFactory.getResourceAdapter();
        EISResourceAdapter eisRA = (EISResourceAdapter)ra;
        try {
            this.eisConnection.setOutputStream(eisRA.getEisActivationSpec().getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return this.eisConnection;
    }

    /**
     * @see javax.resource.cci.ConnectionFactory#getConnection(javax.resource.cci.ConnectionSpec)
     */
    public Connection getConnection(ConnectionSpec connectionSpec) throws ResourceException {
        System.out.println("EISConnectionFactory/getConnection " + connectionSpec);
        return null;
    }

    /**
     * @see javax.resource.cci.ConnectionFactory#getRecordFactory()
     */
    public RecordFactory getRecordFactory() throws ResourceException {
        System.out.println("EISConnectionFactory/getRecordFactory");
        return new EISRecordFactory();
    }

    /**
     * @see javax.resource.cci.ConnectionFactory#getMetaData()
     */
    public ResourceAdapterMetaData getMetaData() throws ResourceException {
        System.out.println("EISConnectionFactory/getMetaData");
        return null;
    }

    /**
     * @see javax.resource.Referenceable#setReference(javax.naming.Reference)
     */
    public void setReference(Reference reference) {
        System.out.println("EISConnectionFactory/setReference " + reference);
    }

    /**
     * @see javax.naming.Referenceable#getReference()
     */
    public Reference getReference() throws NamingException {
        System.out.println("EISConnectionFactory/getMetaData");
        return null;
    }
}

EISConnection


package org.jyperion.eis.jca.cci;

import java.io.OutputStream;

import javax.resource.ResourceException;
import javax.resource.cci.Connection;
import javax.resource.cci.ConnectionMetaData;
import javax.resource.cci.Interaction;
import javax.resource.cci.LocalTransaction;
import javax.resource.cci.ResultSetInfo;

/**
 * EISConnection
 * 
 * (C) 2004 - Jyperion Ltd
 * @author Thierry Janaudy janaudy@jyperion.com
 */
public class EISConnection implements Connection {
    /**
     * OutputStream
     */
    private OutputStream outputStream = null;
    
    /**
     * EISConnection
     * @param os
     */
    public EISConnection() {
    }
    
    /**
     * setOutputStream
     * @param os
     */
    public void setOutputStream(OutputStream os) {
        this.outputStream = os;
    }
    
    /**
     * @see javax.resource.cci.Connection#createInteraction()
     */
    public Interaction createInteraction() throws ResourceException {
        System.out.println("EISConnection/createInteraction");
        return new EISInteraction(this.outputStream);
    }

    /**
     * @see javax.resource.cci.Connection#getLocalTransaction()
     */
    public LocalTransaction getLocalTransaction() throws ResourceException {
        System.out.println("EISConnection/getLocalTransaction");
        return null;
    }

    /**
     * @see javax.resource.cci.Connection#getMetaData()
     */
    public ConnectionMetaData getMetaData() throws ResourceException {
        System.out.println("EISConnection/getMetaData");
        return null;
    }

    /**
     * @see javax.resource.cci.Connection#getResultSetInfo()
     */
    public ResultSetInfo getResultSetInfo() throws ResourceException {
        System.out.println("EISConnection/getResultSetInfo");
        return null;
    }

    /**
     * @see javax.resource.cci.Connection#close()
     */
    public void close() throws ResourceException {
        System.out.println("EISConnection/close");
    }
}

EISInteraction


package org.jyperion.eis.jca.cci;

import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Date;

import javax.resource.ResourceException;
import javax.resource.cci.Connection;
import javax.resource.cci.Interaction;
import javax.resource.cci.InteractionSpec;
import javax.resource.cci.Record;
import javax.resource.cci.ResourceWarning;

/**
 * EISInteraction
 * 
 * (C) 2004 - Jyperion Ltd
 * @author Thierry Janaudy janaudy@jyperion.com
 */
public class EISInteraction implements Interaction {
    /**
     * PrintWriter
     */
    private PrintWriter printWriter = null;
    
    /**
     * EISInteraction
     * @param os
     */
    public EISInteraction(OutputStream os) {
        try {
            this.printWriter = new PrintWriter(os);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    /**
     * @see javax.resource.cci.Interaction#close()
     */
    public void close() throws ResourceException {
        System.out.println("EISInteraction/close");
    }

    /**
     * @see javax.resource.cci.Interaction#getConnection()
     */
    public Connection getConnection() {
        System.out.println("EISInteraction/getConnection");
        return null;
    }

    /**
     * @see javax.resource.cci.Interaction#execute(javax.resource.cci.InteractionSpec, javax.resource.cci.Record, javax.resource.cci.Record)
     */
    public boolean execute(InteractionSpec interactionSpec, Record record1, Record record2)
            throws ResourceException {
        System.out.println("EISInteraction/getConnection " + interactionSpec + ", " +
                record1 + ", " + record2);
        return false;
    }

    /**
     * @see javax.resource.cci.Interaction#execute(javax.resource.cci.InteractionSpec, javax.resource.cci.Record)
     */
    public Record execute(InteractionSpec interactionSpec, Record record) throws ResourceException {
        System.out.println("EISInteraction/getConnection " + interactionSpec + 
                ", " + record);
        
        EISRecord eisRecord = new EISRecord();
        
        this.printWriter.println(record.getRecordName() + " @ " + new Date());
        this.printWriter.flush();
        
        return eisRecord;
    }

    /**
     * @see javax.resource.cci.Interaction#getWarnings()
     */
    public ResourceWarning getWarnings() throws ResourceException {
        System.out.println("EISInteraction/getWarnings");
        return null;
    }

    /**
     * @see javax.resource.cci.Interaction#clearWarnings()
     */
    public void clearWarnings() throws ResourceException {
        System.out.println("EISInteraction/clearWarnings");
    }
}

EISRecordFactory


package org.jyperion.eis.jca.cci;

import javax.resource.ResourceException;
import javax.resource.cci.IndexedRecord;
import javax.resource.cci.MappedRecord;
import javax.resource.cci.RecordFactory;

/**
 * EISRecordFactory
 * 
 * (C) 2004 - Jyperion Ltd
 * @author Thierry Janaudy janaudy@jyperion.com
 */
public class EISRecordFactory implements RecordFactory {
    /**
     * @see javax.resource.cci.RecordFactory#createMappedRecord(java.lang.String)
     */
    public MappedRecord createMappedRecord(String recordName)
            throws ResourceException {
        System.out.println("EISRecordFactory/createMappedRecord " + recordName);
        EISMappedRecord mr = new EISMappedRecord();
        mr.setRecordName(recordName);
        return mr;
    }

    /**
     * @see javax.resource.cci.RecordFactory#createIndexedRecord(java.lang.String)
     */
    public IndexedRecord createIndexedRecord(String recordName)
            throws ResourceException {
        System.out.println("EISRecordFactory/createIndexedRecord " + recordName);
        return null;
    }
}

EISMappedRecord


package org.jyperion.eis.jca.cci;

import java.util.Collection;
import java.util.Map;
import java.util.Set;

import javax.resource.cci.MappedRecord;

/**
 * EISMappedRecord
 * 
 * (C) 2004 - Jyperion Ltd
 * @author Thierry Janaudy janaudy@jyperion.com
 */
public class EISMappedRecord implements MappedRecord {
    /**
     * recordName
     */
    private String recordName = null;
    
    /**
     * @see javax.resource.cci.Record#getRecordName()
     */
    public String getRecordName() {
        return this.recordName;
    }

    /**
     * @see javax.resource.cci.Record#setRecordName(java.lang.String)
     */
    public void setRecordName(String recordName) {
        this.recordName = recordName;
    }

    /**
     * @see javax.resource.cci.Record#setRecordShortDescription(java.lang.String)
     */
    public void setRecordShortDescription(String arg0) {
    }

    /**
     * @see javax.resource.cci.Record#getRecordShortDescription()
     */
    public String getRecordShortDescription() {
        return null;
    }

    /**
     * @see java.util.Map#size()
     */
    public int size() {
        return 0;
    }

    /**
     * @see java.util.Map#clear()
     */
    public void clear() {
    }

    /**
     * @see java.util.Map#isEmpty()
     */
    public boolean isEmpty() {
        return false;
    }

    /**
     * @see java.util.Map#containsKey(java.lang.Object)
     */
    public boolean containsKey(Object arg0) {
        return false;
    }

    /**
     * @see java.util.Map#containsValue(java.lang.Object)
     */
    public boolean containsValue(Object arg0) {
        return false;
    }

    /**
     * @see java.util.Map#values()
     */
    public Collection values() {
        return null;
    }

    /**
     * @see java.util.Map#putAll(java.util.Map)
     */
    public void putAll(Map arg0) {
    }

    /**
     * @see java.util.Map#entrySet()
     */
    public Set entrySet() {
        return null;
    }

    /**
     * @see java.util.Map#keySet()
     */
    public Set keySet() {
        return null;
    }

    /**
     * @see java.util.Map#get(java.lang.Object)
     */
    public Object get(Object arg0) {
        return null;
    }

    /**
     * @see java.util.Map#remove(java.lang.Object)
     */
    public Object remove(Object arg0) {
        return null;
    }

    /**
     * @see java.util.Map#put(java.lang.Object, java.lang.Object)
     */
    public Object put(Object arg0, Object arg1) {
        return null;
    }
    
    /**
     * @see java.lang.Object#clone()
     */
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

EISRecord


package org.jyperion.eis.jca.cci;

import javax.resource.cci.Record;

/**
 * EISRecord
 * 
 * (C) 2004 - Jyperion Ltd
 * @author Thierry Janaudy janaudy@jyperion.com
 */
public class EISRecord implements Record {
    /**
     * @see javax.resource.cci.Record#getRecordName()
     */
    public String getRecordName() {
        return null;
    }

    /**
     * @see javax.resource.cci.Record#setRecordName(java.lang.String)
     */
    public void setRecordName(String arg0) {
    }

    /**
     * @see javax.resource.cci.Record#setRecordShortDescription(java.lang.String)
     */
    public void setRecordShortDescription(String arg0) {
    }

    /**
     * @see javax.resource.cci.Record#getRecordShortDescription()
     */
    public String getRecordShortDescription() {
        return null;
    }

    /**
     * @see java.lang.Object#clone()
     */
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

Deloyment in jBoss

Console EIS Server

[Thierry-Janaudys-Computer:thierry/book-jca/eclipse-output] janaudy% java org.jyperion.eis.server.EISServer 23067 
Waiting for incoming connection on port 23067 
ClientThread started Thread[Thread-1,5,main] 
Client list size is 1 
Got connection Socket[addr=/127.0.0.1,port=60347,localport=23067] 
Waiting for incoming connection on port 23067 
outputWriter is java.io.PrintWriter@911f71 
inputReader is java.io.BufferedReader@a73d3c 
ReaderThread Thread[Thread-2,5,main] started 
Sending heartbeat from Thread[Thread-1,5,main] 
Sending heartbeat from Thread[Thread-1,5,main] 
Console jBoss

17:36:43,754 INFO [STDOUT] EISResourceAdapter/seteisPort to 23067 
17:36:43,776 INFO [STDOUT] EISResourceAdapter/seteisHost to localhost 
17:36:43,863 INFO [STDOUT] EISResourceAdapter/start org.jboss.resource.deployment.RARDeployment@524c19 
17:36:45,341 INFO [STDOUT] EISManagedConnectionFactory/setResourceAdapter org.jyperion.eis.jca.spi.EISResourceAdapter@bbafc7 
17:36:45,352 INFO [STDOUT] EISManagedConnectionFactory/setLogWriter org.jboss.logging.util.LoggerPluginWriter@a037bc 
17:36:45,367 INFO [STDOUT] EISManagedConnectionFactory/createConnectionFactory org.jboss.resource.connectionmanager.BaseConnectionManager2$ConnectionManagerProxy@ab32a2 
17:36:45,460 INFO [STDOUT] EISConnectionFactory/EISConnectionFactory org.jyperion.eis.jca.spi.EISManagedConnectionFactory@34d2f7, org.jboss.resource.connectionmanager. BaseConnectionManager2$ConnectionManagerProxy@ab32a2
17:36:45,509 INFO [ConnectionFactoryBindingService] Bound connection factory for resource adapter for ConnectionManager 'jboss.jca:name=ra/EISJCA,service=ConnectionFactoryBinding to JNDI name 'java:ra/EISJCA' 
17:36:46,209 INFO [EjbModule] Deploying EISMDB 
17:36:47,082 INFO [STDOUT] EISActivationSpec/validate 
17:36:47,153 INFO [STDOUT] EISActivationSpec/setResourceAdapter org.jyperion.eis.jca.spi.EISResourceAdapter@bbafc7 
17:36:47,155 INFO [STDOUT] EISResourceAdapter/endpointActivation org.jboss.ejb.plugins.inflow.JBossMessageEndpointFactory@aa4bf8{ resourceAdapter=jboss.jca:name='eisjca.rar',service=RARDeployment, messagingType=org.jyperion.eis.jca.inbound.EISListener, activationConfig=[], activationSpec=org.jyperion.eis.jca.spi.EISActivationSpec@619eb7}, org.jyperion.eis.jca.spi.EISActivationSpec@619eb7 
17:36:47,230 INFO [STDOUT] EISXAResource/setEndpoint local/EISMDB@1 
17:36:47,232 INFO [STDOUT] EISActivationSpec/connectToEIS localhost:23067 
17:36:47,280 INFO [STDOUT] MessageListenerThread/run org.jyperion.eis.jca.spi.EISXAResource@8f75bd 
17:36:47,285 INFO [EJBDeployer] Deployed: file:/Users/janaudy/Softwares/jboss-4.0.0RC2/server/default/deploy/eismdb.jar 
17:36:52,339 INFO [STDOUT] EISMDBListener/setMessageDrivenContext org.jboss.ejb.MessageDrivenEnterpriseContext$MessageDrivenContextImpl@6f63a0 
17:37:01,885 INFO [EjbModule] Deploying EIS 
17:37:02,218 INFO [EJBDeployer] Deployed: file:/Users/janaudy/Softwares/jboss-4.0.0RC2/server/default/deploy/eisejb.jar 

JcaEjbClientTest


package org.jyperion.eis.jca.ejb.client;

import javax.naming.InitialContext;

import org.jyperion.eis.jca.ejb.EIS;
import org.jyperion.eis.jca.ejb.EISHome;

/**
 * JcaEjbClientTest
 * 
 * (C) 2004 - Jyperion Ltd
 * @author Thierry Janaudy janaudy@jyperion.com
 */
public class JcaEjbClientTest {
    public static void main(String[] args) throws Exception {
        InitialContext lContext = new InitialContext();
		System.out.println("Got initial context: " + lContext);
		
		EISHome home = (EISHome)lContext.lookup("ejb/EISBean");
		System.out.println("home = " + home);
		EIS ion = home.create();
		System.out.println("eis = " + ion);
		
		ion.execJCATest("java:ra/EISJCA");
		
		System.out.println("Bye.");
		ion.remove();
		
		System.exit(0);
    }
}
Console jBoss

17:41:03,703 INFO [STDOUT] EISBean/setSessionContext org.jboss.ejb.StatelessSessionEnterpriseContext$SessionContextImpl@86acfb 
17:41:03,706 INFO [STDOUT] EISBean/ejbCreate 
17:41:03,708 INFO [STDOUT] EISBean/execJCATest java:ra/EISJCA 
17:41:03,710 INFO [STDOUT] EISBean/execJCATest obj = org.jyperion.eis.jca.cci.EISConnectionFactory@e4df82 
17:41:03,711 INFO [STDOUT] EISConnectionFactory/getConnection 
17:41:03,712 INFO [STDOUT] EISManagedConnectionFactory/getResourceAdapter 
17:41:03,714 INFO [STDOUT] EISBean/execJCATest connection = org.jyperion.eis.jca.cci.EISConnection@4c076b 
17:41:03,715 INFO [STDOUT] EISConnection/createInteraction 
17:41:03,722 INFO [STDOUT] EISBean/execJCATest interaction = org.jyperion.eis.jca.cci.EISInteraction@41cf5c 
17:41:03,724 INFO [STDOUT] EISConnectionFactory/getRecordFactory 
17:41:03,731 INFO [STDOUT] EISBean/execJCATest recordFactory = org.jyperion.eis.jca.cci.EISRecordFactory@f41467 
17:41:03,733 INFO [STDOUT] EISRecordFactory/createMappedRecord EISTestRecord 
17:41:03,738 INFO [STDOUT] EISBean/execJCATest mappedRecord = org.jyperion.eis.jca.cci.EISMappedRecord@3c0dda 
17:41:03,739 INFO [STDOUT] EISInteraction/getConnection null, org.jyperion.eis.jca.cci.EISMappedRecord@3c0dda 
17:41:03,755 INFO [STDOUT] EISBean/execJCATest resultRecord = org.jyperion.eis.jca.cci.EISRecord@926a10 

Exercise

Do It Yourself.