JCA 1 - Outbound adapters

Content:

  1. Introduction
  2. JCA standard
  3. JCA outbound adapters
  4. Examples
  5. JCA mechanics
  6. Multi TCP
  7. Multi TCP via CCI
  8. TCP
  9. Permanent TCP
  10. UDP
  11. Native multi TCP
  12. Deployment structure

Introduction:

Java EE consists of many standards: JSP, servlet, EJB, JCA, JSF etc..

Most Java EE developers know all of the standards listed above with one exception: JCA. Only a small fraction of Java EE developers know about JCA.

Which is sort of a pity since JCA can be a good solution for some special cases. And it is therefor useful if a Java EE developer has a basic understanding of JCA.

My article Modern Java EE also only covers JSP, servlet, EJB and JSF.

This article should help fix that.

JCA standard:

JCA (Java Connector Architecture) is the Java EE standard for how Java EE applications can interact with other systems in a non-standard way.

JCA 1.0 was introduced in J2EE 1.4 back in 2003.

JCA adapters come in two flavors:

outbound adapters
the Java EE application connects to another system
inbound adapters
another system connects to the Java EE application

Outbound adapter:

JCA outbound adapter

Inbound adapter:

JCA inbound adapter

(EIS = Enterprise Information System)

JCA 1.0 only had outbound adapters. Inbound adapters was added to JCA 1.5 that came with Java EE 5 in 2006.

This article covers JCA inbound adapters.

For JCA inbound adapters see article JCA 2 - Inbound adapters.

JCA outbound adapters:

Examples:

All examples will have the following logic:

The example is of course absurd trivial, but that just mean that the business logic will not distract from the topic of this article.

The examples has been tested on WildFly 10 (JBoss).

6 different outbound adapters will be shown:

Mockup server classes:

package jcademo.mockup;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;

public class MulTCP {
    public static void main(String[] args) throws IOException {
        // listen port
        ServerSocket ss = new ServerSocket(20001);
        while(true) {
            // accept connections
            Socket s = ss.accept();
            // start handler thread
            new Thread() {
                public void run() {
                    try {
                        // open
                        BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream(), "UTF-8"));
                        PrintStream ps = new PrintStream(s.getOutputStream(), true, "UTF-8");
                        // read and parse line with numbers
                        String line;
                        while((line = br.readLine()) != null) {
                            String[] parts = line.split(" ");
                            int a = Integer.parseInt(parts[0]);
                            int b = Integer.parseInt(parts[1]);
                            // calculate and write result
                            int c = a + b;
                            ps.println(c);
                            ps.flush();
                        }
                        // close
                        ps.close();
                        br.close();
                        s.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }.start();
        }
        //ss.close();
    }
}
package jcademo.mockup;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;

public class TCP {
    public static void main(String[] args) throws IOException {
        // listen port
        ServerSocket ss = new ServerSocket(20002);
        while(true) {
            // accept connections
            Socket s = ss.accept();
            // start handler thread
            new Thread() {
                public void run() {
                    try {
                        // open
                        BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream(), "UTF-8"));
                        PrintStream ps = new PrintStream(s.getOutputStream(), true, "UTF-8");
                        // read and parse line with numbers
                        String line = br.readLine();
                        String[] parts = line.split(" ");
                        int a = Integer.parseInt(parts[0]);
                        int b = Integer.parseInt(parts[1]);
                        // calculate and write result
                        int c = a + b;
                        ps.println(c);
                        ps.flush();
                        // close
                        ps.close();
                        br.close();
                        s.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }.start();
        }
        //ss.close();
    }
}
package jcademo.mockup;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;

public class PermTCP {
    public static void main(String[] args) throws IOException {
        // listen port
        ServerSocket ss = new ServerSocket(20003);
        while(true) {
            // accept connections
            Socket s = ss.accept();
            try {
                // open
                BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream(), "UTF-8"));
                PrintStream ps = new PrintStream(s.getOutputStream(), true, "UTF-8");
                while(true) {
                    // read id, read and parse line with numbers
                    String id = br.readLine();
                    if(id == null) break;
                    String line = br.readLine();
                    if(line == null) break;
                    String[] parts = line.split(" ");
                    int a = Integer.parseInt(parts[0]);
                    int b = Integer.parseInt(parts[1]);
                    // calculate and write result
                    int c = a + b;
                    ps.println(id);
                    ps.println(c);
                    ps.flush();
                }
                // close
                ps.close();
                br.close();
                s.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //ss.close();
    }
}
package jcademo.mockup;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class UDP {
    public static void main(String[] args) throws IOException {
        // open datagram socket
        DatagramSocket dgs = new DatagramSocket(2004);
        // receive datagram with id and line with numbers
        byte[] b1 = new byte[1000];
        DatagramPacket dp1 = new DatagramPacket(b1, b1.length);
        dgs.receive(dp1);
        String text = new String(dp1.getData(), "UTF-8");
        // parse id and numbers
        String[] textparts = text.split("\r\n");
        String id = textparts[0];
        String line = textparts[1];
        String[] lineparts = line.split(" ");
        int a = Integer.parseInt(lineparts[0]);
        int b = Integer.parseInt(lineparts[1]);
        // calculate result
        int c = a + b;
        // send datagram with id and result
        String s2 = id + "\r\n" + c;
        byte[] b2 = s2.getBytes("UTF-8");
        DatagramPacket dp2 = new DatagramPacket(b2, b2.length);
        dp2.setAddress(dp1.getAddress());
        dp2.setPort(dp1.getPort());
        dgs.send(dp2);
        // close
        dgs.close();
    }
}

Examples with:

will not be shown.

HTTP can be done using Apache HTTP Client.

For JMS see Java examples in this article.

For remote EJB call examples see:

JCA mechanics:

JCA outbound adapters are really a generalization of database connections and database connection pool.

A database connection pool works with pooled database connections that forward to managed database connections, where the managed database connections are allocated and deallocated from the pool and temporarily associated with a pooled connection.

Database connection pool

A JCA outbound adapter with a connection manager works with pooled connections that forward to managed connections, where the managed database connections are allocated and deallocated from the pool and temporarily associated with a pooled connection.

JCA outbound connection pool

One obvious question is: why use a JCA outbound adapter instead of just having the business logic establish a direct connection when needed and close it again when done?

There are at least 4 potential reasons for that:

For a quick intro to database connection pools see here.

Note that an outbound adapter can be accessed via two interfaces:

The CCI interface seemed smart 15 years ago. But today it just seems like an unnecessary complication. So I will recommend not to use CCI.

To create a resource adapter you need to:

  1. Create a connection interface
  2. Create a connection implementation class
  3. Create a connection factory interface
  4. Create a connection factory implementation class
  5. Create a managed connection class
  6. Create a managed connection factory class
  7. Create a resource adapter descriptor describing how it all fits together

The connection interface is just a simple interface defining business action methods.

The connection implementation class implements the connection interface but just forward all business methods to the managaed connection.

The connection factory interface is just a simple interface defining how to get a connection.

The connection factory implementation class implements the connection factory interface and use the connection manager to get a connection with associated managed connection.

The managed connection class implemeneds ManagedConnection and does the actual work.

The managed connection factory class implemeneds ManagedConnectionFactory and has methods to create managed connections and connection factories.

To use a resource adapter you need to:

  1. Bind the managed connection factory to a JNDI name in an application server specific way
  2. Get a connection factory via JNDI lookup (done via annotation based injection today)
  3. Get a connection from the connection factory
  4. Call method on connection

Starting the adapter works like:

  1. the container instantiate the connection manager
  2. the connection manager fill the pool of managed connections using managed connection factory

Class diagram:

JCA outbound adapter class diagram

Sequence diagram:

JCA outbound adapter sequence diagram

The JCA standard defines a framework for transactions and security as well. None of my examples will use any of these. But be aware that they do exist.

It is usually possible to define pool behavior in an application server specific way.

In the examples (deployed on WildFly) I will define pool behavior in ironjacamar.xml.

Snippet:

            <pool>
                <min-pool-size>5</min-pool-size>
                <max-pool-size>10</max-pool-size>
                <prefill>true</prefill>  
                <use-strict-min>true</use-strict-min>  
                <capacity>
                    <decrementer class-name="org.jboss.jca.core.connectionmanager.pool.capacity.SizeDecrementer">
                        <config-property name="size">1</config-property>
                    </decrementer>
                </capacity>
            </pool>

This instruct the connection manager to:

Snippet:

            <timeout>
                <blocking-timeout-millis>5000</blocking-timeout-millis>
                <idle-timeout-minutes>4</idle-timeout-minutes>
                <allocation-retry>2</allocation-retry>
                <allocation-retry-wait-millis>3000</allocation-retry-wait-millis>
            </timeout>

This instruct the connection manager to:

JCA adapters are packaged in RAR (Resource ARchive) files, which is really a special flavor of JAR files.

The structure of a RAR file is:

META-INF/ra.xml
META-INF/application-server-specific-deployment-descriptors.xml
java-code.jar
native-code.dll
native=code.so

The RAR file can either be deployed directly or be packaged inside an EAR file, where application.xml has:

    <module>
        <connector>my-connector.rar</connector>
    </module>

Hint: with WildFly to make the classes in the RAR file available to a WAR file then add a jboss-deployment-structure.xml to the WAR's WEB-INF with:

<?xml version="1.0" encoding="UTF-8"?>
<jboss-deployment-structure>
    <deployment>
        <dependencies>
            <module name="deployment.my-connector.rar" export="true"/>
        </dependencies>
    </deployment>
</jboss-deployment-structure>

Multi TCP:

Example sending requests via TCP and receiving response via TCP (multiple connections, multiple requests per connection).

For intro to TCP in Java see Java examples here.

Connection interface:

package jcademo.raoutbound.multcp;

import javax.resource.ResourceException;
import javax.resource.cci.Connection;

public interface TestConnection extends Connection {
    public int add(int a, int b) throws ResourceException;
}

Connection implementation class:

package jcademo.raoutbound.multcp;

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

public class TestConnectionImpl implements TestConnection {
    private MulTCPManagedConnection mc;
    private boolean debug;
    // create
    public TestConnectionImpl(MulTCPManagedConnection mc, boolean debug) {
        this.mc = mc;
        this.debug = debug;
        if(debug) {
            System.out.println("Connection created");
        }
    }
    // business logic
    @Override
    public int add(int a, int b) throws ResourceException {
        return mc.add(a, b);
    }
    // close
    @Override
    public void close() {
        if(debug) {
            System.out.println("Connection closed");
        }
        mc.close();
        mc = null;
    }
    // CCI support
    @Override
    public Interaction createInteraction() throws ResourceException {
        throw new ResourceException("CCI not supported");
    }
    // transaction support
    @Override
    public LocalTransaction getLocalTransaction() throws ResourceException {
        throw new ResourceException("Transaction not supported");
    }
    // meta data support
    @Override
    public ConnectionMetaData getMetaData() throws ResourceException {
        throw new ResourceException("Meta data not supported");
    }
    // result set info support
    @Override
    public ResultSetInfo getResultSetInfo() throws ResourceException {
        throw new ResourceException("Result set info not supported");
    }
}

Connection factory interface:

package jcademo.raoutbound.multcp;

import javax.resource.ResourceException;
import javax.resource.cci.ConnectionFactory;
import javax.resource.cci.ConnectionSpec;

public interface TestConnectionFactory extends ConnectionFactory {
    public TestConnection getConnection() throws ResourceException;
    public TestConnection getConnection(ConnectionSpec cs) throws ResourceException;
}

Connection factory implementation class:

package jcademo.raoutbound.multcp;

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

public class TestConnectionFactoryImpl implements TestConnectionFactory {
    private static final long serialVersionUID = 1L;
    private MulTCPManagedConnectionFactory mcf;
    private ConnectionManager cm;
    // instantiate
    public TestConnectionFactoryImpl() {
        this(null, null);
    }
    public TestConnectionFactoryImpl(MulTCPManagedConnectionFactory mcf, ConnectionManager cm) {
        this.mcf = mcf;
        this.cm = cm;
    }
    // get connection from connection manager
    @Override
    public TestConnection getConnection() throws ResourceException {
        return (TestConnection)cm.allocateConnection(mcf, null);
    }
    @Override
    public TestConnection getConnection(ConnectionSpec cs) throws ResourceException {
        return (TestConnection)cm.allocateConnection(mcf, null);
    }
    // CCI support
    @Override
    public RecordFactory getRecordFactory() throws ResourceException {
        throw new ResourceException("CCI not supported");
    }
    // reference
    private Reference ref;
    @Override
    public Reference getReference() throws NamingException {
        return ref;
    }
    @Override
    public void setReference(Reference ref) {
        this.ref = ref;
    }
    // meta data support
    @Override
    public ResourceAdapterMetaData getMetaData() throws ResourceException {
        throw new ResourceException("Meta data not supported");
    }
}

Managed connection class:

package jcademo.raoutbound.multcp;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

import javax.resource.ResourceException;
import javax.resource.spi.ConnectionEvent;
import javax.resource.spi.ConnectionEventListener;
import javax.resource.spi.ConnectionRequestInfo;
import javax.resource.spi.LocalTransaction;
import javax.resource.spi.ManagedConnection;
import javax.resource.spi.ManagedConnectionMetaData;
import javax.security.auth.Subject;
import javax.transaction.xa.XAResource;

public class MulTCPManagedConnection implements ManagedConnection {
    private boolean debug;
    private TestConnection con;
    private Socket s;
    private PrintStream ps;
    private BufferedReader br;
    // create
    public MulTCPManagedConnection(String host, int port, boolean debug) throws ResourceException {
        this.debug = debug;
        try {
            s = new Socket(host, port);
            ps = new PrintStream(s.getOutputStream(), true, "UTF-8");
            br = new BufferedReader(new InputStreamReader(s.getInputStream(), "UTF-8"));
        } catch (IOException e) {
            throw new ResourceException(e);
        }
        if(debug) {
            System.out.printf("Managed connection created (%s:%d)\n", host, port);
        }
    }
    // business logic
    public int add(int a, int b) throws ResourceException {
        try {
            ps.println(a + " " + b);
            ps.flush();
            String line = br.readLine();
            int c = Integer.parseInt(line);
            return c;
        } catch (IOException e) {
            throw new ResourceException(e);
        }
    }
    // called by Connection.close => informs connection manager that connection is closed
    public void close() {
        for(ConnectionEventListener cel : cellst) {
            ConnectionEvent ce = new ConnectionEvent(this, ConnectionEvent.CONNECTION_CLOSED);
            ce.setConnectionHandle(con);
            cel.connectionClosed(ce);
        }
    }
    // called by connection manager to destroy this instance when adapter is shutting down or pool size is decreased
    @Override
    public void destroy() throws ResourceException {
        try {
            br.close();
            ps.close();
            s.close();
        } catch (IOException e) {
            throw new ResourceException(e);
        }
        if(debug) {
            System.out.println("Managed connection destroyed");
        }
    }
    // called by connection manager when it is informed that connection is closed
    @Override
    public void cleanup() {
        con = null;
    }
    // get connection and save connection reference
    @Override
    public TestConnection getConnection(Subject subj, ConnectionRequestInfo cri) {
        con = new TestConnectionImpl(this, debug);
        return con;
    }
    // change connection reference
    @Override
    public void associateConnection(Object con) {
        this.con = (TestConnection)con;
    }
    // manage listeners (used by connection manager)
    private List<ConnectionEventListener> cellst = new ArrayList<>();
    @Override
    public void addConnectionEventListener(ConnectionEventListener cel) {
        cellst.add(cel);
    }
    @Override
    public void removeConnectionEventListener(ConnectionEventListener cel) {
        cellst.remove(cel);
    }
    // log support:
    private PrintWriter logWriter;
    @Override
    public PrintWriter getLogWriter() throws ResourceException {
        return logWriter;
    }
    @Override
    public void setLogWriter(PrintWriter logWriter) {
        this.logWriter = logWriter;
    }
    // transaction support
    @Override
    public LocalTransaction getLocalTransaction() throws ResourceException {
        throw new ResourceException("Transaction not supported");
    }
    @Override
    public XAResource getXAResource() throws ResourceException {
        throw new ResourceException("Transaction not supported");
    }
    // meta data support
    @Override
    public ManagedConnectionMetaData getMetaData() throws ResourceException {
        throw new ResourceException("Meta data not supported");
    }
}

Managed connection factory class:

package jcademo.raoutbound.multcp;

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

import javax.resource.ResourceException;
import javax.resource.cci.ConnectionFactory;
import javax.resource.spi.ConnectionManager;
import javax.resource.spi.ConnectionRequestInfo;
import javax.resource.spi.ManagedConnection;
import javax.resource.spi.ManagedConnectionFactory;
import javax.security.auth.Subject;

public class MulTCPManagedConnectionFactory implements ManagedConnectionFactory {
    private static final long serialVersionUID = 1L;
    // config properties
    private String host;
    private Integer port;
    private Boolean debug;
    public String getHost() {
        return host;
    }
    public void setHost(String host) {
        this.host = host;
    }
    public Integer getPort() {
        return port;
    }
    public void setPort(Integer port) {
        this.port = port;
    }
    public Boolean getDebug() {
        return debug;
    }
    public void setDebug(Boolean debug) {
        this.debug = debug;
    }
    // get connection factory
    @Override
    public ConnectionFactory createConnectionFactory(ConnectionManager cm) {
        return new TestConnectionFactoryImpl(this, cm);
    }
    @Override
    public ConnectionFactory createConnectionFactory() throws ResourceException {
        throw new ResourceException("Non-managed not supported");
    }
    // get managed connection
    @Override
    public ManagedConnection createManagedConnection(Subject subj, ConnectionRequestInfo cri) throws ResourceException {
        return new MulTCPManagedConnection(host, port, debug);
    }
    // pick a connection from set when asked by connection manager
    @Override
    public ManagedConnection matchManagedConnections(@SuppressWarnings("rawtypes") Set pool, Subject subj, ConnectionRequestInfo cri) {
        @SuppressWarnings("rawtypes") Iterator it =  pool.iterator();
        while(it.hasNext()) {
            ManagedConnection mc = (ManagedConnection)it.next();
            if(mc instanceof MulTCPManagedConnection) {
                return mc;
            }
        }
        return null;
    }
    // required by some specification
    @Override
    public int hashCode() {
        return super.hashCode();
    }
    @Override
    public boolean equals(Object o) {
        return this == o;
    }
    // log support
    private PrintWriter logWriter;
    @Override
    public PrintWriter getLogWriter() {
        return logWriter;
    }
    @Override
    public void setLogWriter(PrintWriter logWriter) {
        this.logWriter = logWriter;
    }
}

Resource adapter class:

package jcademo.raoutbound.multcp;

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

// JCA Adapter
public class MulTCPAdapter implements ResourceAdapter {
    @Override
    public void start(BootstrapContext bctx) {
    }
    @Override
    public void endpointActivation(MessageEndpointFactory mepf, ActivationSpec as) {
    }
    @Override
    public void endpointDeactivation(MessageEndpointFactory mepf, ActivationSpec as) {
    }
    @Override
    public void stop() {
    }
    @Override
    public XAResource[] getXAResources(ActivationSpec[] as) throws ResourceException {
        throw new ResourceException("no XA support");
    }
    @Override
    public int hashCode() {
        return super.hashCode();
    }
    @Override
    public boolean equals(Object o) {
        return this == o;
    }
}

Resource adapter descriptor (ra.xml):

<?xml version="1.0" encoding="UTF-8"?>
<connector xmlns="http://java.sun.com/xml/ns/javaee"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/connector_1_6.xsd"
           version="1.6">
    <!--
    JCA Adaptor: Outbound multi TCP
        ManagedConnectionFactory class: jcademo.raoutbound.multcp.MulTCPManagedConnectionFactory
            property host = localhost
            property port = 20001
            property debug = true
        Connectionfactory interface: jcademo.raoutbound.multcp.TestConnectionFactory
        Connectionfactory class: jcademo.raoutbound.multcp.TestConnectionFactoryImpl
        Connection interface: jcademo.raoutbound.multcp.TestConnection
        Connection class: jcademo.raoutbound.multcp.TestConnectionImpl
     -->
    <display-name>Outbound multi TCP</display-name>
    <vendor-name>AV</vendor-name>
    <eis-type>Demo</eis-type>
    <resourceadapter-version>1.0</resourceadapter-version>
    <resourceadapter>
        <resourceadapter-class>jcademo.raoutbound.multcp.MulTCPAdapter</resourceadapter-class>
        <outbound-resourceadapter>
            <connection-definition>
                <managedconnectionfactory-class>jcademo.raoutbound.multcp.MulTCPManagedConnectionFactory</managedconnectionfactory-class>
                <config-property>
                    <config-property-name>host</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>port</config-property-name>
                    <config-property-type>java.lang.Integer</config-property-type>
                    <config-property-value>20001</config-property-value>
                </config-property>
                <config-property>
                    <config-property-name>debug</config-property-name>
                    <config-property-type>java.lang.Boolean</config-property-type>
                    <config-property-value>true</config-property-value>
                </config-property>
                <connectionfactory-interface>jcademo.raoutbound.multcp.TestConnectionFactory</connectionfactory-interface>
                <connectionfactory-impl-class>jcademo.raoutbound.multcp.TestConnectionFactoryImpl</connectionfactory-impl-class>
                <connection-interface>jcademo.raoutbound.multcp.TestConnection</connection-interface>
                <connection-impl-class>jcademo.raoutbound.multcp.TestConnectionImpl</connection-impl-class>
            </connection-definition>
            <transaction-support>NoTransaction</transaction-support>
        </outbound-resourceadapter>
    </resourceadapter>
</connector>

Binding for WildFly/JBoss (ironjacamar.xml):

<?xml version="1.0" encoding="UTF-8"?>
<ironjacamar>
    <connection-definitions>
        <connection-definition class-name="jcademo.raoutbound.multcp.MulTCPManagedConnectionFactory" jndi-name="java:/eis/MulTCP">
            <pool>
                <min-pool-size>5</min-pool-size>
                <max-pool-size>10</max-pool-size>
                <prefill>true</prefill>  
                <use-strict-min>true</use-strict-min>  
                <capacity>
                    <decrementer class-name="org.jboss.jca.core.connectionmanager.pool.capacity.SizeDecrementer">
                        <config-property name="size">1</config-property>
                    </decrementer>
                </capacity>
            </pool>
            <timeout>
                <blocking-timeout-millis>5000</blocking-timeout-millis>
                <idle-timeout-minutes>4</idle-timeout-minutes>
                <allocation-retry>2</allocation-retry>
                <allocation-retry-wait-millis>3000</allocation-retry-wait-millis>
            </timeout>
        </connection-definition>
    </connection-definitions>
</ironjacamar>

Test client:

package jcademo.client;

import java.io.IOException;

import javax.annotation.Resource;
import javax.resource.ResourceException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import jcademo.raoutbound.multcp.TestConnection;
import jcademo.raoutbound.multcp.TestConnectionFactory;

@WebServlet(urlPatterns={"/testmultcp"})
public class TestMulTCP extends HttpServlet {
    private static final long serialVersionUID = 1L;
    @Resource(mappedName = "java:/eis/MulTCP")
    private TestConnectionFactory cf;
    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        try {
            TestConnection con = cf.getConnection();
            int c = con.add(123, 456);
            con.close();
            resp.getWriter().println(c);
        } catch (ResourceException e) {
            e.printStackTrace();
        } 
    }
}

Multi TCP via CCI:

Example sending requests via TCP and receiving response via TCP (multiple connections, multiple requests per connection) using the generic CCI interface.

For intro to TCP in Java see Java examples here.

Connection interface:

package jcademo.raoutbound.multcp_cci;

import javax.resource.ResourceException;
import javax.resource.cci.Connection;

public interface TestConnection extends Connection {
    public int add(int a, int b) throws ResourceException;
}

Connection implementation class:

package jcademo.raoutbound.multcp_cci;

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

public class TestConnectionImpl implements TestConnection {
    private MulTCPManagedConnection mc;
    private boolean debug;
    // create
    public TestConnectionImpl(MulTCPManagedConnection mc, boolean debug) {
        this.mc = mc;
        this.debug = debug;
        if(debug) {
            System.out.println("Connection created");
        }
    }
    // business logic
    @Override
    public int add(int a, int b) throws ResourceException {
        return mc.add(a, b);
    }
    // close
    @Override
    public void close() {
        if(debug) {
            System.out.println("Connection closed");
        }
        mc.close();
        mc = null;
    }
    // CCI support
    @Override
    public Interaction createInteraction() {
        return new CCI_Interaction(this);
    }
    // transaction support
    @Override
    public LocalTransaction getLocalTransaction() throws ResourceException {
        throw new ResourceException("Transaction not supported");
    }
    // meta data support
    @Override
    public ConnectionMetaData getMetaData() throws ResourceException {
        throw new ResourceException("Meta data not supported");
    }
    // result set info support
    @Override
    public ResultSetInfo getResultSetInfo() throws ResourceException {
        throw new ResourceException("Result set info not supported");
    }
}

Connection factory interface:

package jcademo.raoutbound.multcp_cci;

import javax.resource.ResourceException;
import javax.resource.cci.ConnectionFactory;
import javax.resource.cci.ConnectionSpec;

public interface TestConnectionFactory extends ConnectionFactory {
    public TestConnection getConnection() throws ResourceException;
    public TestConnection getConnection(ConnectionSpec cs) throws ResourceException;
}

Connection factory implementation class:

package jcademo.raoutbound.multcp_cci;

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

public class TestConnectionFactoryImpl implements TestConnectionFactory {
    private static final long serialVersionUID = 1L;
    private MulTCPManagedConnectionFactory mcf;
    private ConnectionManager cm;
    // instantiate
    public TestConnectionFactoryImpl() {
        this(null, null);
    }
    public TestConnectionFactoryImpl(MulTCPManagedConnectionFactory mcf, ConnectionManager cm) {
        this.mcf = mcf;
        this.cm = cm;
    }
    // get connection from connection manager
    @Override
    public TestConnection getConnection() throws ResourceException {
        return (TestConnection)cm.allocateConnection(mcf, null);
    }
    @Override
    public TestConnection getConnection(ConnectionSpec cs) throws ResourceException {
        return (TestConnection)cm.allocateConnection(mcf, null);
    }
    // CCI support
    @Override
    public RecordFactory getRecordFactory() {
        return new CCI_RecordFactory();
    }
    // reference
    private Reference ref;
    @Override
    public Reference getReference() throws NamingException {
        return ref;
    }
    @Override
    public void setReference(Reference ref) {
        this.ref = ref;
    }
    // meta data support
    @Override
    public ResourceAdapterMetaData getMetaData() throws ResourceException {
        throw new ResourceException("Meta data not supported");
    }
}

Managed connection class:

package jcademo.raoutbound.multcp_cci;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

import javax.resource.ResourceException;
import javax.resource.spi.ConnectionEvent;
import javax.resource.spi.ConnectionEventListener;
import javax.resource.spi.ConnectionRequestInfo;
import javax.resource.spi.LocalTransaction;
import javax.resource.spi.ManagedConnection;
import javax.resource.spi.ManagedConnectionMetaData;
import javax.security.auth.Subject;
import javax.transaction.xa.XAResource;

public class MulTCPManagedConnection implements ManagedConnection {
    private boolean debug;
    private TestConnection con;
    private Socket s;
    private PrintStream ps;
    private BufferedReader br;
    // create
    public MulTCPManagedConnection(String host, int port, boolean debug) throws ResourceException {
        this.debug = debug;
        try {
            s = new Socket(host, port);
            ps = new PrintStream(s.getOutputStream(), true, "UTF-8");
            br = new BufferedReader(new InputStreamReader(s.getInputStream(), "UTF-8"));
        } catch (IOException e) {
            throw new ResourceException(e);
        }
        if(debug) {
            System.out.printf("Managed connection created (%s:%d)\n", host, port);
        }
    }
    // business logic
    public int add(int a, int b) throws ResourceException {
        try {
            ps.println(a + " " + b);
            ps.flush();
            String line = br.readLine();
            int c = Integer.parseInt(line);
            return c;
        } catch (IOException e) {
            throw new ResourceException(e);
        }
    }
    // called by Connection.close => informs connection manager that connection is closed
    public void close() {
        for(ConnectionEventListener cel : cellst) {
            ConnectionEvent ce = new ConnectionEvent(this, ConnectionEvent.CONNECTION_CLOSED);
            ce.setConnectionHandle(con);
            cel.connectionClosed(ce);
        }
    }
    // called by connection manager to destroy this instance when adapter is shutting down or pool size is decreased
    @Override
    public void destroy() throws ResourceException {
        try {
            br.close();
            ps.close();
            s.close();
        } catch (IOException e) {
            throw new ResourceException(e);
        }
        if(debug) {
            System.out.println("Managed connection destroyed");
        }
    }
    // called by connection manager when it is informed that connection is closed
    @Override
    public void cleanup() {
        con = null;
    }
    // get connection and save connection reference
    @Override
    public TestConnection getConnection(Subject subj, ConnectionRequestInfo cri) {
        con = new TestConnectionImpl(this, debug);
        return con;
    }
    // change connection reference
    @Override
    public void associateConnection(Object con) {
        this.con = (TestConnection)con;
    }
    // manage listeners (used by connection manager)
    private List<ConnectionEventListener> cellst = new ArrayList<>();
    @Override
    public void addConnectionEventListener(ConnectionEventListener cel) {
        cellst.add(cel);
    }
    @Override
    public void removeConnectionEventListener(ConnectionEventListener cel) {
        cellst.remove(cel);
    }
    // log support:
    private PrintWriter logWriter;
    @Override
    public PrintWriter getLogWriter() throws ResourceException {
        return logWriter;
    }
    @Override
    public void setLogWriter(PrintWriter logWriter) {
        this.logWriter = logWriter;
    }
    // transaction support
    @Override
    public LocalTransaction getLocalTransaction() throws ResourceException {
        throw new ResourceException("Transaction not supported");
    }
    @Override
    public XAResource getXAResource() throws ResourceException {
        throw new ResourceException("Transaction not supported");
    }
    // meta data support
    @Override
    public ManagedConnectionMetaData getMetaData() throws ResourceException {
        throw new ResourceException("Meta data not supported");
    }
}

Managed connection factory class:

package jcademo.raoutbound.multcp_cci;

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

import javax.resource.ResourceException;
import javax.resource.cci.ConnectionFactory;
import javax.resource.spi.ConnectionManager;
import javax.resource.spi.ConnectionRequestInfo;
import javax.resource.spi.ManagedConnection;
import javax.resource.spi.ManagedConnectionFactory;
import javax.security.auth.Subject;

public class MulTCPManagedConnectionFactory implements ManagedConnectionFactory {
    private static final long serialVersionUID = 1L;
    // config properties
    private String host;
    private Integer port;
    private Boolean debug;
    public String getHost() {
        return host;
    }
    public void setHost(String host) {
        this.host = host;
    }
    public Integer getPort() {
        return port;
    }
    public void setPort(Integer port) {
        this.port = port;
    }
    public Boolean getDebug() {
        return debug;
    }
    public void setDebug(Boolean debug) {
        this.debug = debug;
    }
    // get connection factory
    @Override
    public ConnectionFactory createConnectionFactory(ConnectionManager cm) {
        return new TestConnectionFactoryImpl(this, cm);
    }
    @Override
    public ConnectionFactory createConnectionFactory() throws ResourceException {
        throw new ResourceException("Non-managed not supported");
    }
    // get managed connection
    @Override
    public ManagedConnection createManagedConnection(Subject subj, ConnectionRequestInfo cri) throws ResourceException {
        return new MulTCPManagedConnection(host, port, debug);
    }
    // pick a connection from set when asked by connection manager
    @Override
    public ManagedConnection matchManagedConnections(@SuppressWarnings("rawtypes") Set pool, Subject subj, ConnectionRequestInfo cri) {
        @SuppressWarnings("rawtypes") Iterator it =  pool.iterator();
        while(it.hasNext()) {
            ManagedConnection mc = (ManagedConnection)it.next();
            if(mc instanceof MulTCPManagedConnection) {
                return mc;
            }
        }
        return null;
    }
    // required by some specification
    @Override
    public int hashCode() {
        return super.hashCode();
    }
    @Override
    public boolean equals(Object o) {
        return this == o;
    }
    // log support
    private PrintWriter logWriter;
    @Override
    public PrintWriter getLogWriter() {
        return logWriter;
    }
    @Override
    public void setLogWriter(PrintWriter logWriter) {
        this.logWriter = logWriter;
    }
}

Resource adapter class:

package jcademo.raoutbound.multcp_cci;

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

// JCA Adapter
public class MulTCPAdapter implements ResourceAdapter {
    @Override
    public void start(BootstrapContext bctx) {
    }
    @Override
    public void endpointActivation(MessageEndpointFactory mepf, ActivationSpec as) {
    }
    @Override
    public void endpointDeactivation(MessageEndpointFactory mepf, ActivationSpec as) {
    }
    @Override
    public void stop() {
    }
    @Override
    public XAResource[] getXAResources(ActivationSpec[] as) throws ResourceException {
        throw new ResourceException("no XA support");
    }
    @Override
    public int hashCode() {
        return super.hashCode();
    }
    @Override
    public boolean equals(Object o) {
        return this == o;
    }
}

CCI interface classes:

package jcademo.raoutbound.multcp_cci;

import java.util.AbstractList;

import javax.resource.cci.IndexedRecord;

@SuppressWarnings("rawtypes")
public class CCI_AB_IndexedRecord extends AbstractList implements IndexedRecord {
    private static final long serialVersionUID = 1L;
    private int a;
    private int b;
    // record name and description
    @Override
    public String getRecordName() {
        return "AB";
    }
    @Override
    public void setRecordName(String name) {
        // name is fixed
    }
    @Override
    public String getRecordShortDescription() {
        return "A and B (input)";
    }
    @Override
    public void setRecordShortDescription(String description) {
        // description is fixed
    }
    // core methods
    @Override
    public Object get(int index) {
        switch(index) {
            case 0:
                return a;
            case 1:
                return b;
            default:
                throw new IndexOutOfBoundsException(Integer.toString(index));
        }
    }
    @Override
    public Object set(int index, Object element) {
        switch(index) {
            case 0:
                int olda = a;
                a = (Integer)element;
                return olda;
            case 1:
                int oldb = b;
                b = (Integer)element;
                return oldb;
            default:
                throw new IndexOutOfBoundsException(Integer.toString(index));
        }
    }
    @Override
    public int size() {
        return 2;
    }
    // required
    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
package jcademo.raoutbound.multcp_cci;

import java.util.AbstractMap;
import java.util.HashSet;
import java.util.Set;

import javax.resource.cci.MappedRecord;

@SuppressWarnings("rawtypes")
public class CCI_AB_MappedRecord extends AbstractMap implements MappedRecord {
    private static final long serialVersionUID = 1L;
    private int a;
    private int b;
    // record name and description
    @Override
    public String getRecordName() {
        return "AB";
    }
    @Override
    public void setRecordName(String name) {
        // name is fixed
    }
    @Override
    public String getRecordShortDescription() {
        return "A and B (input)";
    }
    @Override
    public void setRecordShortDescription(String description) {
        // description is fixed
    }
    // core methods
    @Override
    public Set entrySet() {
        Set res = new HashSet();
        res.add(new AbstractMap.SimpleEntry("A", (Integer)a));
        res.add(new AbstractMap.SimpleEntry("B", (Integer)b));
        return res;
    }
    @Override
    public Object put(Object key, Object value) {
        if(key.equals("A")) {
            int olda = a;
            a = (Integer) value;
            return olda;
        } else if(key.equals("B")) {
            int oldb = b;
            b = (Integer) value;
            return oldb;
        } else {
            throw new IllegalArgumentException("Bad key: " + key);
        }
    }
    // required
    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
package jcademo.raoutbound.multcp_cci;

import java.util.AbstractList;

import javax.resource.cci.IndexedRecord;

@SuppressWarnings("rawtypes")
public class CCI_C_IndexedRecord extends AbstractList implements IndexedRecord {
    private static final long serialVersionUID = 1L;
    private int c;
    // record name and description
    @Override
    public String getRecordName() {
        return "C";
    }
    @Override
    public void setRecordName(String name) {
        // name is fixed
    }
    @Override
    public String getRecordShortDescription() {
        return "C (output)";
    }
    @Override
    public void setRecordShortDescription(String description) {
        // description is fixed
    }
    // core methods
    @Override
    public Object get(int index) {
        switch(index) {
            case 0:
                return c;
            default:
                throw new IndexOutOfBoundsException(Integer.toString(index));
        }
    }
    @Override
    public Object set(int index, Object element) {
        switch(index) {
            case 0:
                int oldc = c;
                c = (Integer)element;
                return oldc;
            default:
                throw new IndexOutOfBoundsException(Integer.toString(index));
        }
    }
    @Override
    public int size() {
        return 1;
    }
    // required
    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
package jcademo.raoutbound.multcp_cci;

import java.util.AbstractMap;
import java.util.HashSet;
import java.util.Set;

import javax.resource.cci.MappedRecord;

@SuppressWarnings("rawtypes")
public class CCI_C_MappedRecord extends AbstractMap implements MappedRecord {
    private static final long serialVersionUID = 1L;
    private int c;
    // record name and description
    @Override
    public String getRecordName() {
        return "C";
    }
    @Override
    public void setRecordName(String name) {
        // name is fixed
    }
    @Override
    public String getRecordShortDescription() {
        return "C (output)";
    }
    @Override
    public void setRecordShortDescription(String description) {
        // description is fixed
    }
    // core methods
    @Override
    public Set entrySet() {
        Set res = new HashSet();
        res.add(new AbstractMap.SimpleEntry("C", (Integer)c));
        return res;
    }
    @Override
    public Object put(Object key, Object value) {
        if(key.equals("C")) {
            int oldc = c;
            c = (Integer) value;
            return oldc;
        } else {
            throw new IllegalArgumentException("Bad key: " + key);
        }
    }
    // required
    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
package jcademo.raoutbound.multcp_cci;

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

public class CCI_Interaction implements Interaction {
    // connection
    private TestConnection con;
    public CCI_Interaction(TestConnection con) {
        this.con = con;
    }
    @Override
    public Connection getConnection() {
        return con;
    }
    // execute
    @Override
    public Record execute(InteractionSpec ispec, Record input) throws ResourceException {
        Record output;
        if(input instanceof IndexedRecord) {
            output = new CCI_C_IndexedRecord();
        } else if(input instanceof MappedRecord) {
            output = new CCI_C_MappedRecord();
        } else {
            throw new ResourceException("Unknown record type " + input.getClass().getName());
        }
        return execute(ispec, input, output) ? output : null;
    }
    @Override
    public boolean execute(InteractionSpec ispec, Record input, Record output) throws ResourceException {
        int a;
        int b;
        if(input instanceof IndexedRecord) {
            IndexedRecord input2 = (IndexedRecord)input;
            a = (Integer)input2.get(0);
            b = (Integer)input2.get(1);
        } else if(input instanceof MappedRecord) {
            MappedRecord input2 = (MappedRecord)input;
            a = (Integer)input2.get("A");
            b = (Integer)input2.get("B");
        } else {
            throw new ResourceException("Unknown record type " + input.getClass().getName());
        }
        CCI_InteractionSpec ispec2 = (CCI_InteractionSpec)ispec;
        int c;
        if(ispec2.getFunctionName().equals("ADD")) {
            c = con.add(a, b);
        } else {
            throw new ResourceException("Unknown function name " + ispec2.getFunctionName());
        }
        if(output instanceof IndexedRecord) {
            IndexedRecord output2 = (IndexedRecord)output;
            output2.set(0, c);
        } else if(output instanceof MappedRecord) {
            MappedRecord output2 = (MappedRecord)output;
            output2.put("C", c);
        } else {
            throw new ResourceException("Unknown record type " + output.getClass().getName());
        }
        return true;
    }
    // close
    @Override
    public void close() throws ResourceException {
        // nothing
    }
    // warnings
    @Override
    public ResourceWarning getWarnings() throws ResourceException {
        throw new ResourceException("Warnings not supported");
    }
    @Override
    public void clearWarnings() throws ResourceException {
        throw new ResourceException("Warnings not supported");
    }
}
package jcademo.raoutbound.multcp_cci;

import javax.resource.cci.InteractionSpec;

public interface CCI_InteractionSpec extends InteractionSpec {
    public String getFunctionName();
    public void setFunctionName(String functionName);
    public int getInteractionVerb();
    public void setInteractionVerb(int interactionVerb);
}
package jcademo.raoutbound.multcp_cci;

public class CCI_InteractionSpecImpl implements CCI_InteractionSpec {
    private static final long serialVersionUID = 1L;
    private String functionName = "ADD";
    private int interactionVerb = SYNC_SEND_RECEIVE;
    @Override
    public String getFunctionName() {
        return functionName;
    }
    @Override
    public void setFunctionName(String functionName) {
        this.functionName = functionName;
    }
    @Override
    public int getInteractionVerb() {
        return interactionVerb;
    }
    @Override
    public void setInteractionVerb(int interactionVerb) {
        this.interactionVerb = interactionVerb;
    }
}
package jcademo.raoutbound.multcp_cci;

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

public class CCI_RecordFactory implements RecordFactory {
    @Override
    public IndexedRecord createIndexedRecord(String recordName) throws ResourceException {
        if(recordName.equals("AB")) {
            return new CCI_AB_IndexedRecord();
        } else if(recordName.equals("C")) {
            return new CCI_C_IndexedRecord();
        } else {
            throw new ResourceException("Unknown record type " + recordName);
        }
    }
    @Override
    public MappedRecord createMappedRecord(String recordName) throws ResourceException {
        if(recordName.equals("AB")) {
            return new CCI_AB_MappedRecord();
        } else if(recordName.equals("C")) {
            return new CCI_C_MappedRecord();
        } else {
            throw new ResourceException("Unknown record type " + recordName);
        }
    }
}

Resource adapter descriptor (ra.xml):

<?xml version="1.0" encoding="UTF-8"?>
<connector xmlns="http://java.sun.com/xml/ns/javaee"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/connector_1_6.xsd"
           version="1.6">
    <!--
    JCA Adaptor: Outbound multi TCP with CCI
        ManagedConnectionFactory class: jcademo.raoutbound.multcp_cci.MulTCPManagedConnectionFactory
            property host = localhost
            property port = 20001
            property debug = true
        Connectionfactory interface: jcademo.raoutbound.multcp_cci.TestConnectionFactory
        Connectionfactory class: jcademo.raoutbound.multcp_cci.TestConnectionFactoryImpl
        Connection interface: jcademo.raoutbound.multcp_cci.TestConnection
        Connection class: jcademo.raoutbound.multcp_cci.TestConnectionImpl
     -->
    <display-name>Outbound multi TCP with CCI</display-name>
    <vendor-name>AV</vendor-name>
    <eis-type>Demo</eis-type>
    <resourceadapter-version>1.0</resourceadapter-version>
    <resourceadapter>
        <resourceadapter-class>jcademo.raoutbound.multcp_cci.MulTCPAdapter</resourceadapter-class>
        <outbound-resourceadapter>
            <connection-definition>
                <managedconnectionfactory-class>jcademo.raoutbound.multcp_cci.MulTCPManagedConnectionFactory</managedconnectionfactory-class>
                <config-property>
                    <config-property-name>host</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>port</config-property-name>
                    <config-property-type>java.lang.Integer</config-property-type>
                    <config-property-value>20001</config-property-value>
                </config-property>
                <config-property>
                    <config-property-name>debug</config-property-name>
                    <config-property-type>java.lang.Boolean</config-property-type>
                    <config-property-value>true</config-property-value>
                </config-property>
                <connectionfactory-interface>jcademo.raoutbound.multcp_cci.TestConnectionFactory</connectionfactory-interface>
                <connectionfactory-impl-class>jcademo.raoutbound.multcp_cci.TestConnectionFactoryImpl</connectionfactory-impl-class>
                <connection-interface>jcademo.raoutbound.multcp_cci.TestConnection</connection-interface>
                <connection-impl-class>jcademo.raoutbound.multcp_cci.TestConnectionImpl</connection-impl-class>
            </connection-definition>
            <transaction-support>NoTransaction</transaction-support>
        </outbound-resourceadapter>
        <adminobject>
            <adminobject-interface>jcademo.raoutbound.multcp_cci.CCI_InteractionSpec</adminobject-interface>
            <adminobject-class>jcademo.raoutbound.multcp_cci.CCI_InteractionSpecImpl</adminobject-class>
        </adminobject>
    </resourceadapter>
</connector>

Binding for WildFly/JBoss (ironjacamar.xml):

<?xml version="1.0" encoding="UTF-8"?>
<ironjacamar>
    <connection-definitions>
        <connection-definition class-name="jcademo.raoutbound.multcp_cci.MulTCPManagedConnectionFactory" jndi-name="java:/eis/MulTCP_CCI">
            <pool>
                <min-pool-size>5</min-pool-size>
                <max-pool-size>10</max-pool-size>
                <prefill>true</prefill>  
                <use-strict-min>true</use-strict-min>  
                <capacity>
                    <decrementer class-name="org.jboss.jca.core.connectionmanager.pool.capacity.SizeDecrementer">
                        <config-property name="size">1</config-property>
                    </decrementer>
                </capacity>
            </pool>
            <timeout>
                <blocking-timeout-millis>5000</blocking-timeout-millis>
                <idle-timeout-minutes>4</idle-timeout-minutes>
                <allocation-retry>2</allocation-retry>
                <allocation-retry-wait-millis>3000</allocation-retry-wait-millis>
            </timeout>
        </connection-definition>
    </connection-definitions>
    <admin-objects>
        <admin-object class-name="jcademo.raoutbound.multcp_cci.CCI_InteractionSpecImpl" jndi-name="java:/eis/MulTCP_IS"/>
    </admin-objects>
</ironjacamar>

Test client:

package jcademo.client;

import java.io.IOException;

import javax.annotation.Resource;
import javax.resource.ResourceException;
import javax.resource.cci.Connection;
import javax.resource.cci.ConnectionFactory;
import javax.resource.cci.IndexedRecord;
import javax.resource.cci.Interaction;
import javax.resource.cci.InteractionSpec;
import javax.resource.cci.MappedRecord;
import javax.resource.cci.RecordFactory;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet(urlPatterns={"/testmultcp_cci"})
public class TestMulTCP_CCI extends HttpServlet {
    private static final long serialVersionUID = 1L;
    @Resource(mappedName = "java:/eis/MulTCP_CCI")
    private ConnectionFactory cf;
    @Resource(mappedName = "java:/eis/MulTCP_IS")
    private InteractionSpec is;
    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        try {
            int c;
            // open
            Connection con = cf.getConnection();
            Interaction i = con.createInteraction();
            RecordFactory rf = cf.getRecordFactory();
            // test index records
            IndexedRecord ir = rf.createIndexedRecord("AB");
            ir.set(0, 123);
            ir.set(1, 456);
            IndexedRecord oir = (IndexedRecord)i.execute(is, ir);
            c = (Integer)oir.get(0);
            resp.getWriter().println(c);
            // test mapped record
            MappedRecord mr = rf.createMappedRecord("AB");
            mr.put("A", 123);
            mr.put("B", 456);
            MappedRecord omr = (MappedRecord)i.execute(is, mr);
            c = (Integer)omr.get("C");
            resp.getWriter().println(c);
            // close
            con.close();
        } catch (ResourceException e) {
            e.printStackTrace();
        } 
    }
}

And I really don't get the CCI interface. Yes - it seems smart that one can write the client code referencing only general Java EE types without referencing any adapter specific types. But then one realize that the code instead contains a large number of adapter specific values - and then the point is sort of lost. I recommend avoiding CCI interface - it just obfuscates the client code unnecessary.

TCP:

Example sending requests via TCP and receiving response via TCP (multiple connections, one request per connection).

For intro to TCP in Java see Java examples here.

Connection interface:

package jcademo.raoutbound.tcp;

import javax.resource.ResourceException;
import javax.resource.cci.Connection;

public interface TestConnection extends Connection {
    public int add(int a, int b) throws ResourceException;
}

Connection implementation class:

package jcademo.raoutbound.tcp;

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

public class TestConnectionImpl implements TestConnection {
    private TCPManagedConnection mc;
    private boolean debug;
    // create
    public TestConnectionImpl(TCPManagedConnection mc, boolean debug) {
        this.mc = mc;
        this.debug = debug;
        if(debug) {
            System.out.println("Connection created");
        }
    }
    // business logic
    @Override
    public int add(int a, int b) throws ResourceException {
        return mc.add(a, b);
    }
    // close
    @Override
    public void close() {
        if(debug) {
            System.out.println("Connection closed");
        }
        mc.close();
        mc = null;
    }
    // CCI support
    @Override
    public Interaction createInteraction() throws ResourceException {
        throw new ResourceException("CCI not supported");
    }
    // transaction support
    @Override
    public LocalTransaction getLocalTransaction() throws ResourceException {
        throw new ResourceException("Transaction not supported");
    }
    // meta data support
    @Override
    public ConnectionMetaData getMetaData() throws ResourceException {
        throw new ResourceException("Meta data not supported");
    }
    // result set info support
    @Override
    public ResultSetInfo getResultSetInfo() throws ResourceException {
        throw new ResourceException("Result set info not supported");
    }
}

Connection factory interface:

package jcademo.raoutbound.tcp;

import javax.resource.ResourceException;
import javax.resource.cci.ConnectionFactory;
import javax.resource.cci.ConnectionSpec;

public interface TestConnectionFactory extends ConnectionFactory {
    public TestConnection getConnection() throws ResourceException;
    public TestConnection getConnection(ConnectionSpec cs) throws ResourceException;
}

Connection factory implementation class:

package jcademo.raoutbound.tcp;

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

public class TestConnectionFactoryImpl implements TestConnectionFactory {
    private static final long serialVersionUID = 1L;
    private TCPManagedConnectionFactory mcf;
    private ConnectionManager cm;
    // instantiate
    public TestConnectionFactoryImpl() {
        this(null, null);
    }
    public TestConnectionFactoryImpl(TCPManagedConnectionFactory mcf, ConnectionManager cm) {
        this.mcf = mcf;
        this.cm = cm;
    }
    // get connection from connection manager
    @Override
    public TestConnection getConnection() throws ResourceException {
        return (TestConnection)cm.allocateConnection(mcf, null);
    }
    @Override
    public TestConnection getConnection(ConnectionSpec cs) throws ResourceException {
        return (TestConnection)cm.allocateConnection(mcf, null);
    }
    // CCI support
    @Override
    public RecordFactory getRecordFactory() throws ResourceException {
        throw new ResourceException("CCI not supported");
    }
    // reference
    private Reference ref;
    @Override
    public Reference getReference() throws NamingException {
        return ref;
    }
    @Override
    public void setReference(Reference ref) {
        this.ref = ref;
    }
    // meta data support
    @Override
    public ResourceAdapterMetaData getMetaData() throws ResourceException {
        throw new ResourceException("Meta data not supported");
    }
}

Managed connection class:

package jcademo.raoutbound.tcp;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

import javax.resource.ResourceException;
import javax.resource.spi.ConnectionEvent;
import javax.resource.spi.ConnectionEventListener;
import javax.resource.spi.ConnectionRequestInfo;
import javax.resource.spi.LocalTransaction;
import javax.resource.spi.ManagedConnection;
import javax.resource.spi.ManagedConnectionMetaData;
import javax.security.auth.Subject;
import javax.transaction.xa.XAResource;

public class TCPManagedConnection implements ManagedConnection {
    private boolean debug;
    private TestConnection con;
    private String host;
    private int port;
    // create
    public TCPManagedConnection(String host, int port, boolean debug) throws ResourceException {
        this.debug = debug;
        this.host = host;
        this.port = port;
        if(debug) {
            System.out.printf("Managed connection created (%s:%d)\n", host, port);
        }
    }
    // business logic
    public int add(int a, int b) throws ResourceException {
        try {
            Socket s = new Socket(host, port);
            PrintStream ps = new PrintStream(s.getOutputStream(), true, "UTF-8");
            BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream(), "UTF-8"));
            ps.println(a + " " + b);
            ps.flush();
            String line = br.readLine();
            br.close();
            ps.close();
            s.close();
            int c = Integer.parseInt(line);
            return c;
        } catch (IOException e) {
            throw new ResourceException(e);
        }
    }
    // called by Connection.close => informs connection manager that connection is closed
    public void close() {
        for(ConnectionEventListener cel : cellst) {
            ConnectionEvent ce = new ConnectionEvent(this, ConnectionEvent.CONNECTION_CLOSED);
            ce.setConnectionHandle(con);
            cel.connectionClosed(ce);
        }
    }
    // called by connection manager to destroy this instance when adapter is shutting down or pool size is decreased
    @Override
    public void destroy() throws ResourceException {
        if(debug) {
            System.out.println("Managed connection destroyed");
        }
    }
    // called by connection manager when it is informed that connection is closed
    @Override
    public void cleanup() {
        con = null;
    }
    // get connection and save connection reference
    @Override
    public TestConnection getConnection(Subject subj, ConnectionRequestInfo cri) {
        con = new TestConnectionImpl(this, debug);
        return con;
    }
    // change connection reference
    @Override
    public void associateConnection(Object con) {
        this.con = (TestConnection)con;
    }
    // manage listeners (used by connection manager)
    private List<ConnectionEventListener> cellst = new ArrayList<>();
    @Override
    public void addConnectionEventListener(ConnectionEventListener cel) {
        cellst.add(cel);
    }
    @Override
    public void removeConnectionEventListener(ConnectionEventListener cel) {
        cellst.remove(cel);
    }
    // log support:
    private PrintWriter logWriter;
    @Override
    public PrintWriter getLogWriter() throws ResourceException {
        return logWriter;
    }
    @Override
    public void setLogWriter(PrintWriter logWriter) {
        this.logWriter = logWriter;
    }
    // transaction support
    @Override
    public LocalTransaction getLocalTransaction() throws ResourceException {
        throw new ResourceException("Transaction not supported");
    }
    @Override
    public XAResource getXAResource() throws ResourceException {
        throw new ResourceException("Transaction not supported");
    }
    // meta data support
    @Override
    public ManagedConnectionMetaData getMetaData() throws ResourceException {
        throw new ResourceException("Meta data not supported");
    }
}

Managed connection factory class:

package jcademo.raoutbound.tcp;

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

import javax.resource.ResourceException;
import javax.resource.cci.ConnectionFactory;
import javax.resource.spi.ConnectionManager;
import javax.resource.spi.ConnectionRequestInfo;
import javax.resource.spi.ManagedConnection;
import javax.resource.spi.ManagedConnectionFactory;
import javax.security.auth.Subject;

public class TCPManagedConnectionFactory implements ManagedConnectionFactory {
    private static final long serialVersionUID = 1L;
    // config properties
    private String host;
    private Integer port;
    private Boolean debug;
    public String getHost() {
        return host;
    }
    public void setHost(String host) {
        this.host = host;
    }
    public Integer getPort() {
        return port;
    }
    public void setPort(Integer port) {
        this.port = port;
    }
    public Boolean getDebug() {
        return debug;
    }
    public void setDebug(Boolean debug) {
        this.debug = debug;
    }
    // get connection factory
    @Override
    public ConnectionFactory createConnectionFactory(ConnectionManager cm) {
        return new TestConnectionFactoryImpl(this, cm);
    }
    @Override
    public ConnectionFactory createConnectionFactory() throws ResourceException {
        throw new ResourceException("Non-managed not supported");
    }
    // get managed connection
    @Override
    public ManagedConnection createManagedConnection(Subject subj, ConnectionRequestInfo cri) throws ResourceException {
        return new TCPManagedConnection(host, port, debug);
    }
    // pick a connection from set when asked by connection manager
    @Override
    public ManagedConnection matchManagedConnections(@SuppressWarnings("rawtypes") Set pool, Subject subj, ConnectionRequestInfo cri) {
        @SuppressWarnings("rawtypes") Iterator it =  pool.iterator();
        while(it.hasNext()) {
            ManagedConnection mc = (ManagedConnection)it.next();
            if(mc instanceof TCPManagedConnection) {
                return mc;
            }
        }
        return null;
    }
    // required by some specification
    @Override
    public int hashCode() {
        return super.hashCode();
    }
    @Override
    public boolean equals(Object o) {
        return this == o;
    }
    // log support
    private PrintWriter logWriter;
    @Override
    public PrintWriter getLogWriter() {
        return logWriter;
    }
    @Override
    public void setLogWriter(PrintWriter logWriter) {
        this.logWriter = logWriter;
    }
}

Resource adapter class:

package jcademo.raoutbound.tcp;

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

// JCA Adapter
public class TCPAdapter implements ResourceAdapter {
    @Override
    public void start(BootstrapContext bctx) {
    }
    @Override
    public void endpointActivation(MessageEndpointFactory mepf, ActivationSpec as) {
    }
    @Override
    public void endpointDeactivation(MessageEndpointFactory mepf, ActivationSpec as) {
    }
    @Override
    public void stop() {
    }
    @Override
    public XAResource[] getXAResources(ActivationSpec[] as) throws ResourceException {
        throw new ResourceException("no XA support");
    }
    @Override
    public int hashCode() {
        return super.hashCode();
    }
    @Override
    public boolean equals(Object o) {
        return this == o;
    }
}

Resource adapter descriptor (ra.xml):

<?xml version="1.0" encoding="UTF-8"?>
<connector xmlns="http://java.sun.com/xml/ns/javaee"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/connector_1_6.xsd"
           version="1.6">
    <!--
    JCA Adaptor: Outbound TCP
        ManagedConnectionFactory class: jcademo.raoutbound.tcp.TCPManagedConnectionFactory
            property host = localhost
            property port = 20001
            property debug = true
        Connectionfactory interface: jcademo.raoutbound.tcp.TestConnectionFactory
        Connectionfactory class: jcademo.raoutbound.tcp.TestConnectionFactoryImpl
        Connection interface: jcademo.raoutbound.tcp.TestConnection
        Connection class: jcademo.raoutbound.tcp.TestConnectionImpl
     -->
    <display-name>Outbound TCP</display-name>
    <vendor-name>AV</vendor-name>
    <eis-type>Demo</eis-type>
    <resourceadapter-version>1.0</resourceadapter-version>
    <resourceadapter>
        <resourceadapter-class>jcademo.raoutbound.tcp.TCPAdapter</resourceadapter-class>
        <outbound-resourceadapter>
            <connection-definition>
                <managedconnectionfactory-class>jcademo.raoutbound.tcp.TCPManagedConnectionFactory</managedconnectionfactory-class>
                <config-property>
                    <config-property-name>host</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>port</config-property-name>
                    <config-property-type>java.lang.Integer</config-property-type>
                    <config-property-value>20002</config-property-value>
                </config-property>
                <config-property>
                    <config-property-name>debug</config-property-name>
                    <config-property-type>java.lang.Boolean</config-property-type>
                    <config-property-value>true</config-property-value>
                </config-property>
                <connectionfactory-interface>jcademo.raoutbound.tcp.TestConnectionFactory</connectionfactory-interface>
                <connectionfactory-impl-class>jcademo.raoutbound.tcp.TestConnectionFactoryImpl</connectionfactory-impl-class>
                <connection-interface>jcademo.raoutbound.tcp.TestConnection</connection-interface>
                <connection-impl-class>jcademo.raoutbound.tcp.TestConnectionImpl</connection-impl-class>
            </connection-definition>
            <transaction-support>NoTransaction</transaction-support>
        </outbound-resourceadapter>
    </resourceadapter>
</connector>

Binding for WildFly/JBoss (ironjacamar.xml):

<?xml version="1.0" encoding="UTF-8"?>
<ironjacamar>
    <connection-definitions>
        <connection-definition class-name="jcademo.raoutbound.tcp.TCPManagedConnectionFactory" jndi-name="java:/eis/TCP">
            <pool>
                <min-pool-size>5</min-pool-size>
                <max-pool-size>10</max-pool-size>
                <prefill>true</prefill>  
                <use-strict-min>true</use-strict-min>  
                <capacity>
                    <decrementer class-name="org.jboss.jca.core.connectionmanager.pool.capacity.SizeDecrementer">
                        <config-property name="size">1</config-property>
                    </decrementer>
                </capacity>
            </pool>
            <timeout>
                <blocking-timeout-millis>5000</blocking-timeout-millis>
                <idle-timeout-minutes>4</idle-timeout-minutes>
                <allocation-retry>2</allocation-retry>
                <allocation-retry-wait-millis>3000</allocation-retry-wait-millis>
            </timeout>
        </connection-definition>
    </connection-definitions>
</ironjacamar>

Test client:

package jcademo.client;

import java.io.IOException;

import javax.annotation.Resource;
import javax.resource.ResourceException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import jcademo.raoutbound.tcp.TestConnection;
import jcademo.raoutbound.tcp.TestConnectionFactory;

@WebServlet(urlPatterns={"/testtcp"})
public class TestTCP extends HttpServlet {
    private static final long serialVersionUID = 1L;
    @Resource(mappedName = "java:/eis/TCP")
    private TestConnectionFactory cf;
    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        try {
            TestConnection con = cf.getConnection();
            int c = con.add(123, 456);
            con.close();
            resp.getWriter().println(c);
        } catch (ResourceException e) {
            e.printStackTrace();
        } 
    }
}

Permanent TCP:

Example sending requests via TCP and receiving response via TCP (single permanent connection, multiple requests).

The problem of having multiple managed connections and a single physical TCP connection is solved by introducing a TCP multi-plexer that send requests and return responses on a Java BlockingQueue to each managed connection.

JCA TCP multi-plexer

For intro to TCP in Java see Java examples here.

Connection interface:

package jcademo.raoutbound.permtcp;

import javax.resource.ResourceException;
import javax.resource.cci.Connection;

public interface TestConnection extends Connection {
    public int add(int a, int b) throws ResourceException;
}

Connection implementation class:

package jcademo.raoutbound.permtcp;

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

public class TestConnectionImpl implements TestConnection {
    private PermTCPManagedConnection mc;
    private boolean debug;
    // create
    public TestConnectionImpl(PermTCPManagedConnection mc, boolean debug) {
        this.mc = mc;
        this.debug = debug;
        if(debug) {
            System.out.println("Connection created");
        }
    }
    // business logic
    @Override
    public int add(int a, int b) throws ResourceException {
        return mc.add(a, b);
    }
    // close
    @Override
    public void close() {
        if(debug) {
            System.out.println("Connection closed");
        }
        mc.close();
        mc = null;
    }
    // CCI support
    @Override
    public Interaction createInteraction() throws ResourceException {
        throw new ResourceException("CCI not supported");
    }
    // transaction support
    @Override
    public LocalTransaction getLocalTransaction() throws ResourceException {
        throw new ResourceException("Transaction not supported");
    }
    // meta data support
    @Override
    public ConnectionMetaData getMetaData() throws ResourceException {
        throw new ResourceException("Meta data not supported");
    }
    // result set info support
    @Override
    public ResultSetInfo getResultSetInfo() throws ResourceException {
        throw new ResourceException("Result set info not supported");
    }
}

Connection factory interface:

package jcademo.raoutbound.permtcp;

import javax.resource.ResourceException;
import javax.resource.cci.ConnectionFactory;
import javax.resource.cci.ConnectionSpec;

public interface TestConnectionFactory extends ConnectionFactory {
    public TestConnection getConnection() throws ResourceException;
    public TestConnection getConnection(ConnectionSpec cs) throws ResourceException;
}

Connection factory implementation class:

package jcademo.raoutbound.permtcp;

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

public class TestConnectionFactoryImpl implements TestConnectionFactory {
    private static final long serialVersionUID = 1L;
    private PermTCPManagedConnectionFactory mcf;
    private ConnectionManager cm;
    // instantiate
    public TestConnectionFactoryImpl() {
        this(null, null);
    }
    public TestConnectionFactoryImpl(PermTCPManagedConnectionFactory mcf, ConnectionManager cm) {
        this.mcf = mcf;
        this.cm = cm;
    }
    // get connection from connection manager
    @Override
    public TestConnection getConnection() throws ResourceException {
        return (TestConnection)cm.allocateConnection(mcf, null);
    }
    @Override
    public TestConnection getConnection(ConnectionSpec cs) throws ResourceException {
        return (TestConnection)cm.allocateConnection(mcf, null);
    }
    // CCI support
    @Override
    public RecordFactory getRecordFactory() throws ResourceException {
        throw new ResourceException("CCI not supported");
    }
    // reference
    private Reference ref;
    @Override
    public Reference getReference() throws NamingException {
        return ref;
    }
    @Override
    public void setReference(Reference ref) {
        this.ref = ref;
    }
    // meta data support
    @Override
    public ResourceAdapterMetaData getMetaData() throws ResourceException {
        throw new ResourceException("Meta data not supported");
    }
}

Managed connection class:

package jcademo.raoutbound.permtcp;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

import javax.resource.ResourceException;
import javax.resource.spi.ConnectionEvent;
import javax.resource.spi.ConnectionEventListener;
import javax.resource.spi.ConnectionRequestInfo;
import javax.resource.spi.LocalTransaction;
import javax.resource.spi.ManagedConnection;
import javax.resource.spi.ManagedConnectionMetaData;
import javax.security.auth.Subject;
import javax.transaction.xa.XAResource;

public class PermTCPManagedConnection implements ManagedConnection {
    private boolean debug;
    private String id;
    private BlockingQueue<String> q;
    private TestConnection con;
    // create
    public PermTCPManagedConnection(boolean debug) throws ResourceException {
        this.debug = debug;
        id = UUID.randomUUID().toString();
        q = new ArrayBlockingQueue<String>(10);
        TCPMultiPlexer.getInstance().register(id, q);
        if(debug) {
            System.out.println("Managed connection created");
        }
    }
    // business logic
    public int add(int a, int b) throws ResourceException {
        try {
            TCPMultiPlexer.getInstance().send(id, a + " " + b);
            String line = q.take();
            int c = Integer.parseInt(line);
            return c;
        } catch (NumberFormatException e) {
            throw new ResourceException(e);
        } catch (InterruptedException e) {
            throw new ResourceException(e);
        }
    }
    // called by Connection.close => informs connection manager that connection is closed
    public void close() {
        for(ConnectionEventListener cel : cellst) {
            ConnectionEvent ce = new ConnectionEvent(this, ConnectionEvent.CONNECTION_CLOSED);
            ce.setConnectionHandle(con);
            cel.connectionClosed(ce);
        }
    }
    // called by connection manager to destroy this instance when adapter is shutting down or pool size is decreased
    @Override
    public void destroy() throws ResourceException {
        TCPMultiPlexer.getInstance().deregister(id);
        if(debug) {
            System.out.println("Managed connection destroyed");
        }
    }
    // called by connection manager when it is informed that connection is closed
    @Override
    public void cleanup() {
        con = null;
    }
    // get connection and save connection reference
    @Override
    public TestConnection getConnection(Subject subj, ConnectionRequestInfo cri) {
        con = new TestConnectionImpl(this, debug);
        return con;
    }
    // change connection reference
    @Override
    public void associateConnection(Object con) {
        this.con = (TestConnection)con;
    }
    // manage listeners (used by connection manager)
    private List<ConnectionEventListener> cellst = new ArrayList<>();
    @Override
    public void addConnectionEventListener(ConnectionEventListener cel) {
        cellst.add(cel);
    }
    @Override
    public void removeConnectionEventListener(ConnectionEventListener cel) {
        cellst.remove(cel);
    }
    // log support:
    private PrintWriter logWriter;
    @Override
    public PrintWriter getLogWriter() throws ResourceException {
        return logWriter;
    }
    @Override
    public void setLogWriter(PrintWriter logWriter) {
        this.logWriter = logWriter;
    }
    // transaction support
    @Override
    public LocalTransaction getLocalTransaction() throws ResourceException {
        throw new ResourceException("Transaction not supported");
    }
    @Override
    public XAResource getXAResource() throws ResourceException {
        throw new ResourceException("Transaction not supported");
    }
    // meta data support
    @Override
    public ManagedConnectionMetaData getMetaData() throws ResourceException {
        throw new ResourceException("Meta data not supported");
    }
}

Managed connection factory class:

package jcademo.raoutbound.permtcp;

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

import javax.resource.ResourceException;
import javax.resource.cci.ConnectionFactory;
import javax.resource.spi.ConnectionManager;
import javax.resource.spi.ConnectionRequestInfo;
import javax.resource.spi.ManagedConnection;
import javax.resource.spi.ManagedConnectionFactory;
import javax.security.auth.Subject;

public class PermTCPManagedConnectionFactory implements ManagedConnectionFactory {
    private static final long serialVersionUID = 1L;
    // config properties
    private Boolean debug;
    public Boolean getDebug() {
        return debug;
    }
    public void setDebug(Boolean debug) {
        this.debug = debug;
    }
    // get connection factory
    @Override
    public ConnectionFactory createConnectionFactory(ConnectionManager cm) {
        return new TestConnectionFactoryImpl(this, cm);
    }
    @Override
    public ConnectionFactory createConnectionFactory() throws ResourceException {
        throw new ResourceException("Non-managed not supported");
    }
    // get managed connection
    @Override
    public ManagedConnection createManagedConnection(Subject subj, ConnectionRequestInfo cri) throws ResourceException {
        return new PermTCPManagedConnection(debug);
    }
    // pick a connection from set when asked by connection manager
    @Override
    public ManagedConnection matchManagedConnections(@SuppressWarnings("rawtypes") Set pool, Subject subj, ConnectionRequestInfo cri) {
        @SuppressWarnings("rawtypes") Iterator it =  pool.iterator();
        while(it.hasNext()) {
            ManagedConnection mc = (ManagedConnection)it.next();
            if(mc instanceof PermTCPManagedConnection) {
                return mc;
            }
        }
        return null;
    }
    // required by some specification
    @Override
    public int hashCode() {
        return super.hashCode();
    }
    @Override
    public boolean equals(Object o) {
        return this == o;
    }
    // log support
    private PrintWriter logWriter;
    @Override
    public PrintWriter getLogWriter() {
        return logWriter;
    }
    @Override
    public void setLogWriter(PrintWriter logWriter) {
        this.logWriter = logWriter;
    }
}

Resource adapter class:

package jcademo.raoutbound.permtcp;

import javax.resource.ResourceException;
import javax.resource.spi.ActivationSpec;
import javax.resource.spi.BootstrapContext;
import javax.resource.spi.ResourceAdapter;
import javax.resource.spi.endpoint.MessageEndpointFactory;
import javax.resource.spi.work.Work;
import javax.resource.spi.work.WorkException;
import javax.resource.spi.work.WorkManager;
import javax.transaction.xa.XAResource;

// JCA Adapter
public class PermTCPAdapter implements ResourceAdapter {
    private String host;
    private int port;
    public String getHost() {
        return host;
    }
    public void setHost(String host) {
        this.host = host;
    }
    public int getPort() {
        return port;
    }
    public void setPort(int port) {
        this.port = port;
    }
    private Work w;
    @Override
    public void start(BootstrapContext bctx) {
        WorkManager wm = bctx.getWorkManager();
        try {
            w = new PermTCPWorker(host, port);
            wm.scheduleWork(w);
        } catch (WorkException e) {
            e.printStackTrace();
        }
    }
    @Override
    public void endpointActivation(MessageEndpointFactory mepf, ActivationSpec as) {
    }
    @Override
    public void endpointDeactivation(MessageEndpointFactory mepf, ActivationSpec as) {
    }
    @Override
    public void stop() {
        w.release();
    }
    @Override
    public XAResource[] getXAResources(ActivationSpec[] as) throws ResourceException {
        throw new ResourceException("no XA support");
    }
    @Override
    public int hashCode() {
        return super.hashCode();
    }
    @Override
    public boolean equals(Object o) {
        return this == o;
    }
}

Utility classes:

package jcademo.raoutbound.permtcp;

import java.io.IOException;

import javax.resource.spi.work.Work;

public class PermTCPWorker implements Work {
    private String host;
    private int port;
    private boolean done;
    public PermTCPWorker(String host, int port) {
        this.host = host;
        this.port = port;
    }
    @Override
    public void run() {
        done = false;
        while(!done) {
            try {
                Thread.sleep(1000);
                TCPMultiPlexer.getInstance().connect(host, port);
                TCPMultiPlexer.getInstance().receive();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    @Override
    public void release() {
        try {
            done = true;
            TCPMultiPlexer.getInstance().close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
package jcademo.raoutbound.permtcp;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.BlockingQueue;

public class TCPMultiPlexer {
    // singleton
    private static TCPMultiPlexer instance = new TCPMultiPlexer();
    public static TCPMultiPlexer getInstance() {
        return instance;
    }
    // data
    private Socket s;
    private PrintStream ps;
    private BufferedReader br;
    private Map<String, BlockingQueue<String>> qmap = new HashMap<String, BlockingQueue<String>>();
    // connect
    public void connect(String host, int port) throws IOException {
        s = new Socket(host, port);
        ps = new PrintStream(s.getOutputStream(), true, "UTF-8");
        br = new BufferedReader(new InputStreamReader(s.getInputStream(), "UTF-8"));
    }
    // register receive queue
    public void register(String id, BlockingQueue<String> q) {
        synchronized(qmap) {
            qmap.put(id, q);
        }
    }
    // send
    public void send(String id, String line) throws InterruptedException {
        synchronized(ps) {
            ps.println(id);
            ps.println(line);
            ps.flush();
        }
    }
    // deregister receive queue
    public void deregister(String id) {
        synchronized(qmap) {
            qmap.remove(id);
        }
    }
    // close
    public void close() throws IOException {
        br.close();
        ps.close();
        s.close();
    }
    // receive loop that put responses in correct queue
    public void receive() throws IOException, InterruptedException {
        while(true) {
            String id = br.readLine();
            if(id == null) break;
            String line = br.readLine();
            if(line == null) break;
            qmap.get(id).put(line);
        }
    }
}

Resource adapter descriptor (ra.xml):

<?xml version="1.0" encoding="UTF-8"?>
<connector xmlns="http://java.sun.com/xml/ns/javaee"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/connector_1_6.xsd"
           version="1.6">
    <!--
    JCA Adaptor: Outbound permament TCP
        ManagedConnectionFactory class: jcademo.raoutbound.permtcp.PermTCPManagedConnectionFactory
            property host = localhost
            property port = 20003
            property debug = true
        Connectionfactory interface: jcademo.raoutbound.tcp.TestConnectionFactory
        Connectionfactory class: jcademo.raoutbound.tcp.TestConnectionFactoryImpl
        Connection interface: jcademo.raoutbound.tcp.TestConnection
        Connection class: jcademo.raoutbound.tcp.TestConnectionImpl
     -->
    <display-name>Outbound permanent TCP</display-name>
    <vendor-name>AV</vendor-name>
    <eis-type>Demo</eis-type>
    <resourceadapter-version>1.0</resourceadapter-version>
    <resourceadapter>
        <resourceadapter-class>jcademo.raoutbound.permtcp.PermTCPAdapter</resourceadapter-class>
        <config-property>
            <config-property-name>host</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>port</config-property-name>
            <config-property-type>java.lang.Integer</config-property-type>
            <config-property-value>20003</config-property-value>
        </config-property>
        <outbound-resourceadapter>
            <connection-definition>
                <managedconnectionfactory-class>jcademo.raoutbound.permtcp.PermTCPManagedConnectionFactory</managedconnectionfactory-class>
                <config-property>
                    <config-property-name>debug</config-property-name>
                    <config-property-type>java.lang.Boolean</config-property-type>
                    <config-property-value>true</config-property-value>
                </config-property>
                <connectionfactory-interface>jcademo.raoutbound.permtcp.TestConnectionFactory</connectionfactory-interface>
                <connectionfactory-impl-class>jcademo.raoutbound.permtcp.TestConnectionFactoryImpl</connectionfactory-impl-class>
                <connection-interface>jcademo.raoutbound.permtcp.TestConnection</connection-interface>
                <connection-impl-class>jcademo.raoutbound.permtcp.TestConnectionImpl</connection-impl-class>
            </connection-definition>
            <transaction-support>NoTransaction</transaction-support>
        </outbound-resourceadapter>
    </resourceadapter>
</connector>

Binding for WildFly/JBoss (ironjacamar.xml):

<?xml version="1.0" encoding="UTF-8"?>
<ironjacamar>
    <connection-definitions>
        <connection-definition class-name="jcademo.raoutbound.permtcp.PermTCPManagedConnectionFactory" jndi-name="java:/eis/PermTCP">
            <pool>
                <min-pool-size>5</min-pool-size>
                <max-pool-size>10</max-pool-size>
                <prefill>true</prefill>  
                <use-strict-min>true</use-strict-min>  
                <capacity>
                    <decrementer class-name="org.jboss.jca.core.connectionmanager.pool.capacity.SizeDecrementer">
                        <config-property name="size">1</config-property>
                    </decrementer>
                </capacity>
            </pool>
            <timeout>
                <blocking-timeout-millis>5000</blocking-timeout-millis>
                <idle-timeout-minutes>4</idle-timeout-minutes>
                <allocation-retry>2</allocation-retry>
                <allocation-retry-wait-millis>3000</allocation-retry-wait-millis>
            </timeout>
        </connection-definition>
    </connection-definitions>
</ironjacamar>

Test client:

package jcademo.client;

import java.io.IOException;

import javax.annotation.Resource;
import javax.resource.ResourceException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import jcademo.raoutbound.permtcp.TestConnection;
import jcademo.raoutbound.permtcp.TestConnectionFactory;

@WebServlet(urlPatterns={"/testpermtcp"})
public class TestPermTCP extends HttpServlet {
    private static final long serialVersionUID = 1L;
    @Resource(mappedName = "java:/eis/PermTCP")
    private TestConnectionFactory cf;
    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        try {
            TestConnection con = cf.getConnection();
            int c = con.add(123, 456);
            con.close();
            resp.getWriter().println(c);
        } catch (ResourceException e) {
            e.printStackTrace();
        } 
    }
}

UDP:

Example sending requests via UDP and receiving response via UDP.

The problem of having multiple managed connections and a single UDP port is solved by introducing am UDP multi-plexer that send requests and return responses on a Java BlockingQueue to each managed connection.

JCA UDP multi-plexer

For intro to UDP in Java see Java examples here.

Connection interface:

package jcademo.raoutbound.udp;

import javax.resource.ResourceException;
import javax.resource.cci.Connection;

public interface TestConnection extends Connection {
    public int add(int a, int b) throws ResourceException;
}

Connection implementation class:

package jcademo.raoutbound.udp;

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

public class TestConnectionImpl implements TestConnection {
    private UDPManagedConnection mc;
    private boolean debug;
    // create
    public TestConnectionImpl(UDPManagedConnection mc, boolean debug) {
        this.mc = mc;
        this.debug = debug;
        if(debug) {
            System.out.println("Connection created");
        }
    }
    // business logic
    @Override
    public int add(int a, int b) throws ResourceException {
        return mc.add(a, b);
    }
    // close
    @Override
    public void close() {
        if(debug) {
            System.out.println("Connection closed");
        }
        mc.close();
        mc = null;
    }
    // CCI support
    @Override
    public Interaction createInteraction() throws ResourceException {
        throw new ResourceException("CCI not supported");
    }
    // transaction support
    @Override
    public LocalTransaction getLocalTransaction() throws ResourceException {
        throw new ResourceException("Transaction not supported");
    }
    // meta data support
    @Override
    public ConnectionMetaData getMetaData() throws ResourceException {
        throw new ResourceException("Meta data not supported");
    }
    // result set info support
    @Override
    public ResultSetInfo getResultSetInfo() throws ResourceException {
        throw new ResourceException("Result set info not supported");
    }
}

Connection factory interface:

package jcademo.raoutbound.udp;

import javax.resource.ResourceException;
import javax.resource.cci.ConnectionFactory;
import javax.resource.cci.ConnectionSpec;

public interface TestConnectionFactory extends ConnectionFactory {
    public TestConnection getConnection() throws ResourceException;
    public TestConnection getConnection(ConnectionSpec cs) throws ResourceException;
}

Connection factory implementation class:

package jcademo.raoutbound.udp;

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

public class TestConnectionFactoryImpl implements TestConnectionFactory {
    private static final long serialVersionUID = 1L;
    private UDPManagedConnectionFactory mcf;
    private ConnectionManager cm;
    // instantiate
    public TestConnectionFactoryImpl() {
        this(null, null);
    }
    public TestConnectionFactoryImpl(UDPManagedConnectionFactory mcf, ConnectionManager cm) {
        this.mcf = mcf;
        this.cm = cm;
    }
    // get connection from connection manager
    @Override
    public TestConnection getConnection() throws ResourceException {
        return (TestConnection)cm.allocateConnection(mcf, null);
    }
    @Override
    public TestConnection getConnection(ConnectionSpec cs) throws ResourceException {
        return (TestConnection)cm.allocateConnection(mcf, null);
    }
    // CCI support
    @Override
    public RecordFactory getRecordFactory() throws ResourceException {
        throw new ResourceException("CCI not supported");
    }
    // reference
    private Reference ref;
    @Override
    public Reference getReference() throws NamingException {
        return ref;
    }
    @Override
    public void setReference(Reference ref) {
        this.ref = ref;
    }
    // meta data support
    @Override
    public ResourceAdapterMetaData getMetaData() throws ResourceException {
        throw new ResourceException("Meta data not supported");
    }
}

Managed connection class:

package jcademo.raoutbound.udp;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

import javax.resource.ResourceException;
import javax.resource.spi.ConnectionEvent;
import javax.resource.spi.ConnectionEventListener;
import javax.resource.spi.ConnectionRequestInfo;
import javax.resource.spi.LocalTransaction;
import javax.resource.spi.ManagedConnection;
import javax.resource.spi.ManagedConnectionMetaData;
import javax.security.auth.Subject;
import javax.transaction.xa.XAResource;

public class UDPManagedConnection implements ManagedConnection {
    private boolean debug;
    private String id;
    private BlockingQueue<String> q;
    private TestConnection con;
    // create
    public UDPManagedConnection(boolean debug) throws ResourceException {
        this.debug = debug;
        id = UUID.randomUUID().toString();
        q = new ArrayBlockingQueue<String>(10);
        UDPMultiPlexer.getInstance().register(id, q);
        if(debug) {
            System.out.println("Managed connection created");
        }
    }
    // business logic
    public int add(int a, int b) throws ResourceException {
        try {
            UDPMultiPlexer.getInstance().send(id, a + " " + b);
            String line = q.take();
            int c = Integer.parseInt(line);
            return c;
        } catch (NumberFormatException e) {
            throw new ResourceException(e);
        } catch (InterruptedException e) {
            throw new ResourceException(e);
        } catch (IOException e) {
            throw new ResourceException(e);
        }
    }
    // called by Connection.close => informs connection manager that connection is closed
    public void close() {
        for(ConnectionEventListener cel : cellst) {
            ConnectionEvent ce = new ConnectionEvent(this, ConnectionEvent.CONNECTION_CLOSED);
            ce.setConnectionHandle(con);
            cel.connectionClosed(ce);
        }
    }
    // called by connection manager to destroy this instance when adapter is shutting down or pool size is decreased
    @Override
    public void destroy() throws ResourceException {
        UDPMultiPlexer.getInstance().deregister(id);
        if(debug) {
            System.out.println("Managed connection destroyed");
        }
    }
    // called by connection manager when it is informed that connection is closed
    @Override
    public void cleanup() {
        con = null;
    }
    // get connection and save connection reference
    @Override
    public TestConnection getConnection(Subject subj, ConnectionRequestInfo cri) {
        con = new TestConnectionImpl(this, debug);
        return con;
    }
    // change connection reference
    @Override
    public void associateConnection(Object con) {
        this.con = (TestConnection)con;
    }
    // manage listeners (used by connection manager)
    private List<ConnectionEventListener> cellst = new ArrayList<>();
    @Override
    public void addConnectionEventListener(ConnectionEventListener cel) {
        cellst.add(cel);
    }
    @Override
    public void removeConnectionEventListener(ConnectionEventListener cel) {
        cellst.remove(cel);
    }
    // log support:
    private PrintWriter logWriter;
    @Override
    public PrintWriter getLogWriter() throws ResourceException {
        return logWriter;
    }
    @Override
    public void setLogWriter(PrintWriter logWriter) {
        this.logWriter = logWriter;
    }
    // transaction support
    @Override
    public LocalTransaction getLocalTransaction() throws ResourceException {
        throw new ResourceException("Transaction not supported");
    }
    @Override
    public XAResource getXAResource() throws ResourceException {
        throw new ResourceException("Transaction not supported");
    }
    // meta data support
    @Override
    public ManagedConnectionMetaData getMetaData() throws ResourceException {
        throw new ResourceException("Meta data not supported");
    }
}

Managed connection factory class:

package jcademo.raoutbound.udp;

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

import javax.resource.ResourceException;
import javax.resource.cci.ConnectionFactory;
import javax.resource.spi.ConnectionManager;
import javax.resource.spi.ConnectionRequestInfo;
import javax.resource.spi.ManagedConnection;
import javax.resource.spi.ManagedConnectionFactory;
import javax.security.auth.Subject;

public class UDPManagedConnectionFactory implements ManagedConnectionFactory {
    private static final long serialVersionUID = 1L;
    // config properties
    private Boolean debug;
    public Boolean getDebug() {
        return debug;
    }
    public void setDebug(Boolean debug) {
        this.debug = debug;
    }
    // get connection factory
    @Override
    public ConnectionFactory createConnectionFactory(ConnectionManager cm) {
        return new TestConnectionFactoryImpl(this, cm);
    }
    @Override
    public ConnectionFactory createConnectionFactory() throws ResourceException {
        throw new ResourceException("Non-managed not supported");
    }
    // get managed connection
    @Override
    public ManagedConnection createManagedConnection(Subject subj, ConnectionRequestInfo cri) throws ResourceException {
        return new UDPManagedConnection(debug);
    }
    // pick a connection from set when asked by connection manager
    @Override
    public ManagedConnection matchManagedConnections(@SuppressWarnings("rawtypes") Set pool, Subject subj, ConnectionRequestInfo cri) {
        @SuppressWarnings("rawtypes") Iterator it =  pool.iterator();
        while(it.hasNext()) {
            ManagedConnection mc = (ManagedConnection)it.next();
            if(mc instanceof UDPManagedConnection) {
                return mc;
            }
        }
        return null;
    }
    // required by some specification
    @Override
    public int hashCode() {
        return super.hashCode();
    }
    @Override
    public boolean equals(Object o) {
        return this == o;
    }
    // log support
    private PrintWriter logWriter;
    @Override
    public PrintWriter getLogWriter() {
        return logWriter;
    }
    @Override
    public void setLogWriter(PrintWriter logWriter) {
        this.logWriter = logWriter;
    }
}

Resource adapter class:

package jcademo.raoutbound.udp;

import java.io.IOException;

import javax.resource.ResourceException;
import javax.resource.spi.ActivationSpec;
import javax.resource.spi.BootstrapContext;
import javax.resource.spi.ResourceAdapter;
import javax.resource.spi.endpoint.MessageEndpointFactory;
import javax.resource.spi.work.Work;
import javax.resource.spi.work.WorkException;
import javax.resource.spi.work.WorkManager;
import javax.transaction.xa.XAResource;

// JCA Adapter
public class UDPAdapter implements ResourceAdapter {
    private String host;
    private int port;
    public String getHost() {
        return host;
    }
    public void setHost(String host) {
        this.host = host;
    }
    public int getPort() {
        return port;
    }
    public void setPort(int port) {
        this.port = port;
    }
    private Work w;
    @Override
    public void start(BootstrapContext bctx) {
        WorkManager wm = bctx.getWorkManager();
        try {
            UDPMultiPlexer.getInstance().associate(host, port);
            w = new UDPWorker();
            wm.scheduleWork(w);
        } catch (WorkException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    @Override
    public void endpointActivation(MessageEndpointFactory mepf, ActivationSpec as) {
    }
    @Override
    public void endpointDeactivation(MessageEndpointFactory mepf, ActivationSpec as) {
    }
    @Override
    public void stop() {
        w.release();
    }
    @Override
    public XAResource[] getXAResources(ActivationSpec[] as) throws ResourceException {
        throw new ResourceException("no XA support");
    }
    @Override
    public int hashCode() {
        return super.hashCode();
    }
    @Override
    public boolean equals(Object o) {
        return this == o;
    }
}

Utility classes:

package jcademo.raoutbound.udp;

import java.io.IOException;

import javax.resource.spi.work.Work;

public class UDPWorker implements Work {
    private boolean done;
    @Override
    public void run() {
        done = false;
        while(!done) {
            try {
                UDPMultiPlexer.getInstance().receive();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    @Override
    public void release() {
        try {
            done = true;
            UDPMultiPlexer.getInstance().close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

package jcademo.raoutbound.udp;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

// utility class to send and receive a bundle of lines (id + numbers to add) + client address + client port
public class UDPUtil {
    public static class DataAndDestination {
        private String data;
        private InetAddress address;
        private int port;
        public DataAndDestination(String data, InetAddress address, int port) {
            this.data = data;
            this.address = address;
            this.port = port;
        }
        public String getData() {
            return data;
        }
        public InetAddress getAddress() {
            return address;
        }
        public int getPort() {
            return port;
        }
    }
    public static DataAndDestination receive(DatagramSocket dgs) throws IOException {
        byte[] b = new byte[1000];
        DatagramPacket dp = new DatagramPacket(b, b.length);
        synchronized(dgs) {
            dgs.receive(dp);
        }
        return new DataAndDestination(new String(dp.getData(), 0, dp.getLength(), "UTF-8"), dp.getAddress(), dp.getPort());
    }
    public static void send(DatagramSocket dgs, DataAndDestination dad) throws IOException {
        byte[] b = dad.getData().getBytes("UTF-8");
        DatagramPacket dp = new DatagramPacket(b, b.length);
        dp.setAddress(dad.getAddress());
        dp.setPort(dad.getPort());
        synchronized(dgs) {
            dgs.send(dp);
        }
    }
}
package jcademo.raoutbound.udp;

import java.io.IOException;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.BlockingQueue;

public class UDPMultiPlexer {
    // singleton
    private static UDPMultiPlexer instance = new UDPMultiPlexer();
    public static UDPMultiPlexer getInstance() {
        return instance;
    }
    // data
    private InetAddress addr;
    private int port;
    private DatagramSocket dgs;
    private Map<String, BlockingQueue<String>> qmap = new HashMap<String, BlockingQueue<String>>();
    // connect
    public void associate(String host, int port) throws IOException {
        addr = InetAddress.getByName(host);
        this.port = port;
        dgs = new DatagramSocket();
    }
    // register receive queue
    public void register(String id, BlockingQueue<String> q) {
        synchronized(qmap) {
            qmap.put(id, q);
        }
    }
    // send
    public void send(String id, String line) throws IOException {
        UDPUtil.send(dgs, new UDPUtil.DataAndDestination(id + "\r\n" + line, addr, port));
    }
    // deregister receive queue
    public void deregister(String id) {
        synchronized(qmap) {
            qmap.remove(id);
        }
    }
    // close
    public void close() throws IOException {
        dgs.close();
    }
    // receive loop that put responses in correct queue
    public void receive() throws IOException, InterruptedException {
        while(true) {
            UDPUtil.DataAndDestination dad = UDPUtil.receive(dgs);
            String[] parts = dad.getData().split("\r\n");
            String id = parts[0];
            String line = parts[1];
            qmap.get(id).put(line);
        }
    }
}

Resource adapter descriptor (ra.xml):

<?xml version="1.0" encoding="UTF-8"?>
<connector xmlns="http://java.sun.com/xml/ns/javaee"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/connector_1_6.xsd"
           version="1.6">
    <!--
    JCA Adaptor: Outbound UDP
        ManagedConnectionFactory class: jcademo.raoutbound.udp.UDPManagedConnectionFactory
            property host = localhost
            property port = 20004
            property debug = true
        Connectionfactory interface: jcademo.raoutbound.udp.TestConnectionFactory
        Connectionfactory class: jcademo.raoutbound.udp.TestConnectionFactoryImpl
        Connection interface: jcademo.raoutbound.udp.TestConnection
        Connection class: jcademo.raoutbound.udp.TestConnectionImpl
     -->
    <display-name>Outbound UDP</display-name>
    <vendor-name>AV</vendor-name>
    <eis-type>Demo</eis-type>
    <resourceadapter-version>1.0</resourceadapter-version>
    <resourceadapter>
        <resourceadapter-class>jcademo.raoutbound.udp.UDPAdapter</resourceadapter-class>
        <config-property>
            <config-property-name>host</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>port</config-property-name>
            <config-property-type>java.lang.Integer</config-property-type>
            <config-property-value>20004</config-property-value>
        </config-property>
        <outbound-resourceadapter>
            <connection-definition>
                <managedconnectionfactory-class>jcademo.raoutbound.udp.UDPManagedConnectionFactory</managedconnectionfactory-class>
                <config-property>
                    <config-property-name>debug</config-property-name>
                    <config-property-type>java.lang.Boolean</config-property-type>
                    <config-property-value>true</config-property-value>
                </config-property>
                <connectionfactory-interface>jcademo.raoutbound.udp.TestConnectionFactory</connectionfactory-interface>
                <connectionfactory-impl-class>jcademo.raoutbound.udp.TestConnectionFactoryImpl</connectionfactory-impl-class>
                <connection-interface>jcademo.raoutbound.udp.TestConnection</connection-interface>
                <connection-impl-class>jcademo.raoutbound.udp.TestConnectionImpl</connection-impl-class>
            </connection-definition>
            <transaction-support>NoTransaction</transaction-support>
        </outbound-resourceadapter>
    </resourceadapter>
</connector>

Binding for WildFly/JBoss (ironjacamar.xml):

<?xml version="1.0" encoding="UTF-8"?>
<ironjacamar>
    <connection-definitions>
        <connection-definition class-name="jcademo.raoutbound.udp.UDPManagedConnectionFactory" jndi-name="java:/eis/UDP">
            <pool>
                <min-pool-size>5</min-pool-size>
                <max-pool-size>10</max-pool-size>
                <prefill>true</prefill>  
                <use-strict-min>true</use-strict-min>  
                <capacity>
                    <decrementer class-name="org.jboss.jca.core.connectionmanager.pool.capacity.SizeDecrementer">
                        <config-property name="size">1</config-property>
                    </decrementer>
                </capacity>
            </pool>
            <timeout>
                <blocking-timeout-millis>5000</blocking-timeout-millis>
                <idle-timeout-minutes>4</idle-timeout-minutes>
                <allocation-retry>2</allocation-retry>
                <allocation-retry-wait-millis>3000</allocation-retry-wait-millis>
            </timeout>
        </connection-definition>
    </connection-definitions>
</ironjacamar>

Test client:

package jcademo.client;

import java.io.IOException;

import javax.annotation.Resource;
import javax.resource.ResourceException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import jcademo.raoutbound.udp.TestConnection;
import jcademo.raoutbound.udp.TestConnectionFactory;

@WebServlet(urlPatterns={"/testudp"})
public class TestUDP extends HttpServlet {
    private static final long serialVersionUID = 1L;
    @Resource(mappedName = "java:/eis/UDP")
    private TestConnectionFactory cf;
    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        try {
            TestConnection con = cf.getConnection();
            int c = con.add(123, 456);
            con.close();
            resp.getWriter().println(c);
        } catch (ResourceException e) {
            e.printStackTrace();
        } 
    }
}

Native multi TCP:

Example sending requests via TCP and receiving response via TCP (multiple connections, multiple requests per connection) with the actual TCP communication done in C.

For intro to TCP in C see C examples here.

Connection interface:

package jcademo.raoutbound.natmultcp;

import javax.resource.ResourceException;
import javax.resource.cci.Connection;

public interface TestConnection extends Connection {
    public int add(int a, int b) throws ResourceException;
}

Connection implementation class:

package jcademo.raoutbound.natmultcp;

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

public class TestConnectionImpl implements TestConnection {
    private NatMulTCPManagedConnection mc;
    private boolean debug;
    // create
    public TestConnectionImpl(NatMulTCPManagedConnection mc, boolean debug) {
        this.mc = mc;
        this.debug = debug;
        if(debug) {
            System.out.println("Connection created");
        }
    }
    // business logic
    @Override
    public int add(int a, int b) throws ResourceException {
        return mc.add(a, b);
    }
    // close
    @Override
    public void close() {
        if(debug) {
            System.out.println("Connection closed");
        }
        mc.close();
        mc = null;
    }
    // CCI support
    @Override
    public Interaction createInteraction() throws ResourceException {
        throw new ResourceException("CCI not supported");
    }
    // transaction support
    @Override
    public LocalTransaction getLocalTransaction() throws ResourceException {
        throw new ResourceException("Transaction not supported");
    }
    // meta data support
    @Override
    public ConnectionMetaData getMetaData() throws ResourceException {
        throw new ResourceException("Meta data not supported");
    }
    // result set info support
    @Override
    public ResultSetInfo getResultSetInfo() throws ResourceException {
        throw new ResourceException("Result set info not supported");
    }
}

Connection factory interface:

package jcademo.raoutbound.natmultcp;

import javax.resource.ResourceException;
import javax.resource.cci.ConnectionFactory;
import javax.resource.cci.ConnectionSpec;

public interface TestConnectionFactory extends ConnectionFactory {
    public TestConnection getConnection() throws ResourceException;
    public TestConnection getConnection(ConnectionSpec cs) throws ResourceException;
}

Connection factory implementation class:

package jcademo.raoutbound.natmultcp;

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

public class TestConnectionFactoryImpl implements TestConnectionFactory {
    private static final long serialVersionUID = 1L;
    private NatMulTCPManagedConnectionFactory mcf;
    private ConnectionManager cm;
    // instantiate
    public TestConnectionFactoryImpl() {
        this(null, null);
    }
    public TestConnectionFactoryImpl(NatMulTCPManagedConnectionFactory mcf, ConnectionManager cm) {
        this.mcf = mcf;
        this.cm = cm;
    }
    // get connection from connection manager
    @Override
    public TestConnection getConnection() throws ResourceException {
        return (TestConnection)cm.allocateConnection(mcf, null);
    }
    @Override
    public TestConnection getConnection(ConnectionSpec cs) throws ResourceException {
        return (TestConnection)cm.allocateConnection(mcf, null);
    }
    // CCI support
    @Override
    public RecordFactory getRecordFactory() throws ResourceException {
        throw new ResourceException("CCI not supported");
    }
    // reference
    private Reference ref;
    @Override
    public Reference getReference() throws NamingException {
        return ref;
    }
    @Override
    public void setReference(Reference ref) {
        this.ref = ref;
    }
    // meta data support
    @Override
    public ResourceAdapterMetaData getMetaData() throws ResourceException {
        throw new ResourceException("Meta data not supported");
    }
}

Managed connection class:

package jcademo.raoutbound.natmultcp;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

import javax.resource.ResourceException;
import javax.resource.spi.ConnectionEvent;
import javax.resource.spi.ConnectionEventListener;
import javax.resource.spi.ConnectionRequestInfo;
import javax.resource.spi.LocalTransaction;
import javax.resource.spi.ManagedConnection;
import javax.resource.spi.ManagedConnectionMetaData;
import javax.security.auth.Subject;
import javax.transaction.xa.XAResource;

public class NatMulTCPManagedConnection implements ManagedConnection {
    private boolean debug;
    private TestConnection con;
    private long sd;
    // create
    public NatMulTCPManagedConnection(String host, int port, boolean debug) throws ResourceException {
        this.debug = debug;
        sd = NatMulTCP.open(host, port);
        if(sd == 0) {
            throw new ResourceException("NatMulTCP.open returned 0");
        }
        if(debug) {
            System.out.printf("Managed connection created (%s:%d)\n", host, port);
        }
    }
    // business logic
    public int add(int a, int b) throws ResourceException {
        if(!NatMulTCP.send(sd, a + " " + b + "\r\n")) { // add \r\n
            throw new ResourceException("NatMulTCP.send returned false");
        }
        String line = NatMulTCP.receive(sd);
        if(line == null) {
            throw new ResourceException("NatMulTCP.receive returned null");
        }
        line = line.substring(0, line.length() - 2); // drop \r\n
        int c = Integer.parseInt(line);
        return c;
    }
    // called by Connection.close => informs connection manager that connection is closed
    public void close() {
        for(ConnectionEventListener cel : cellst) {
            ConnectionEvent ce = new ConnectionEvent(this, ConnectionEvent.CONNECTION_CLOSED);
            ce.setConnectionHandle(con);
            cel.connectionClosed(ce);
        }
    }
    // called by connection manager to destroy this instance when adapter is shutting down or pool size is decreased
    @Override
    public void destroy() throws ResourceException {
        NatMulTCP.close(sd);
        if(debug) {
            System.out.println("Managed connection destroyed");
        }
    }
    // called by connection manager when it is informed that connection is closed
    @Override
    public void cleanup() {
        con = null;
    }
    // get connection and save connection reference
    @Override
    public TestConnection getConnection(Subject subj, ConnectionRequestInfo cri) {
        con = new TestConnectionImpl(this, debug);
        return con;
    }
    // change connection reference
    @Override
    public void associateConnection(Object con) {
        this.con = (TestConnection)con;
    }
    // manage listeners (used by connection manager)
    private List<ConnectionEventListener> cellst = new ArrayList<>();
    @Override
    public void addConnectionEventListener(ConnectionEventListener cel) {
        cellst.add(cel);
    }
    @Override
    public void removeConnectionEventListener(ConnectionEventListener cel) {
        cellst.remove(cel);
    }
    // log support:
    private PrintWriter logWriter;
    @Override
    public PrintWriter getLogWriter() throws ResourceException {
        return logWriter;
    }
    @Override
    public void setLogWriter(PrintWriter logWriter) {
        this.logWriter = logWriter;
    }
    // transaction support
    @Override
    public LocalTransaction getLocalTransaction() throws ResourceException {
        throw new ResourceException("Transaction not supported");
    }
    @Override
    public XAResource getXAResource() throws ResourceException {
        throw new ResourceException("Transaction not supported");
    }
    // meta data support
    @Override
    public ManagedConnectionMetaData getMetaData() throws ResourceException {
        throw new ResourceException("Meta data not supported");
    }
}

Managed connection factory class:

package jcademo.raoutbound.natmultcp;

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

import javax.resource.ResourceException;
import javax.resource.cci.ConnectionFactory;
import javax.resource.spi.ConnectionManager;
import javax.resource.spi.ConnectionRequestInfo;
import javax.resource.spi.ManagedConnection;
import javax.resource.spi.ManagedConnectionFactory;
import javax.security.auth.Subject;

public class NatMulTCPManagedConnectionFactory implements ManagedConnectionFactory {
    private static final long serialVersionUID = 1L;
    // config properties
    private String host;
    private Integer port;
    private Boolean debug;
    public String getHost() {
        return host;
    }
    public void setHost(String host) {
        this.host = host;
    }
    public Integer getPort() {
        return port;
    }
    public void setPort(Integer port) {
        this.port = port;
    }
    public Boolean getDebug() {
        return debug;
    }
    public void setDebug(Boolean debug) {
        this.debug = debug;
    }
    // get connection factory
    @Override
    public ConnectionFactory createConnectionFactory(ConnectionManager cm) {
        return new TestConnectionFactoryImpl(this, cm);
    }
    @Override
    public ConnectionFactory createConnectionFactory() throws ResourceException {
        throw new ResourceException("Non-managed not supported");
    }
    // get managed connection
    @Override
    public ManagedConnection createManagedConnection(Subject subj, ConnectionRequestInfo cri) throws ResourceException {
        return new NatMulTCPManagedConnection(host, port, debug);
    }
    // pick a connection from set when asked by connection manager
    @Override
    public ManagedConnection matchManagedConnections(@SuppressWarnings("rawtypes") Set pool, Subject subj, ConnectionRequestInfo cri) {
        @SuppressWarnings("rawtypes") Iterator it =  pool.iterator();
        while(it.hasNext()) {
            ManagedConnection mc = (ManagedConnection)it.next();
            if(mc instanceof NatMulTCPManagedConnection) {
                return mc;
            }
        }
        return null;
    }
    // required by some specification
    @Override
    public int hashCode() {
        return super.hashCode();
    }
    @Override
    public boolean equals(Object o) {
        return this == o;
    }
    // log support
    private PrintWriter logWriter;
    @Override
    public PrintWriter getLogWriter() {
        return logWriter;
    }
    @Override
    public void setLogWriter(PrintWriter logWriter) {
        this.logWriter = logWriter;
    }
}

Resource adapter class:

package jcademo.raoutbound.natmultcp;

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

// JCA Adapter
public class NatMulTCPAdapter implements ResourceAdapter {
    @Override
    public void start(BootstrapContext bctx) {
    }
    @Override
    public void endpointActivation(MessageEndpointFactory mepf, ActivationSpec as) {
    }
    @Override
    public void endpointDeactivation(MessageEndpointFactory mepf, ActivationSpec as) {
    }
    @Override
    public void stop() {
    }
    @Override
    public XAResource[] getXAResources(ActivationSpec[] as) throws ResourceException {
        throw new ResourceException("no XA support");
    }
    @Override
    public int hashCode() {
        return super.hashCode();
    }
    @Override
    public boolean equals(Object o) {
        return this == o;
    }
}

Native Java class:

package jcademo.raoutbound.natmultcp;

public class NatMulTCP {
    static {
        try {
            System.loadLibrary("NatMulTCP");
        } catch(Throwable t) {
            t.printStackTrace();
        }
    }
    native public static long open(String host, int port);
    native public static boolean send(long sd, String msg);
    native public static String receive(long sd);
    native public static void close(long sd);
}

Native C code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <sys/socket.h>
#include <netdb.h>
#endif

#include <errno.h>

#include "jcademo_raoutbound_natmultcp_NatMulTCP.h"

JNIEXPORT jlong JNICALL Java_jcademo_raoutbound_natmultcp_NatMulTCP_open(JNIEnv *cntx, jclass jc, jstring host, jint port)
{
    int sd, status;
    const char *chost;
    char cport[6];
    struct addrinfo hints, *res;
#ifdef WIN32
    WSADATA WSAData;
    WSAStartup(0x0101, &WSAData);
#endif
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags = 0;
    chost = (*cntx)->GetStringUTFChars(cntx, host, 0);
    itoa(port, cport, 10);
    status = getaddrinfo(chost, cport, &hints, &res);
    (*cntx)->ReleaseStringUTFChars(cntx, host, chost);
    if(status != 0) return -1;
    sd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    if(sd < 0) return -1;
    status = connect(sd, res->ai_addr, res->ai_addrlen);
    if(status != 0) return -1;
    freeaddrinfo(res);
    return sd;
}

static char buf[1000];

JNIEXPORT jboolean JNICALL Java_jcademo_raoutbound_natmultcp_NatMulTCP_send(JNIEnv *cntx, jclass jc, jlong sd, jstring msg)
{
    int status;
    const char *cmsg;
    cmsg = (*cntx)->GetStringUTFChars(cntx, msg, 0);
    strcpy(buf, cmsg); // move message to non-JNI memory
    status = send(sd, (char *)&buf, strlen(buf), 0);
    (*cntx)->ReleaseStringUTFChars(cntx, msg, cmsg);
    if(status < 0) return 0;
    return 1;
}

JNIEXPORT jstring JNICALL Java_jcademo_raoutbound_natmultcp_NatMulTCP_receive(JNIEnv *cntx, jclass jc, jlong sd)
{
    int ix;
    char res[100];
    strcpy(res, "");
    ix = 0;
    while(strstr(res, "\r\n") == NULL && ix < sizeof(res)) {
        ix = ix + recv(sd, res + ix, sizeof(res) - ix, 0);
        res[ix] = '\0';
    }
    return (*cntx)->NewStringUTF(cntx, res);
}

JNIEXPORT void JNICALL Java_jcademo_raoutbound_natmultcp_NatMulTCP_close(JNIEnv *cntx, jclass jc, jlong sd)
{
#ifdef WIN32
    closesocket(sd);
    WSACleanup();
#else
    close(sd);
#endif
}

Resource adapter descriptor (ra.xml):

<?xml version="1.0" encoding="UTF-8"?>
<connector xmlns="http://java.sun.com/xml/ns/javaee"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/connector_1_6.xsd"
           version="1.6">
    <!--
    JCA Adaptor: Outbound native multi TCP
        ManagedConnectionFactory class: jcademo.raoutbound.natmultcp.NatMulTCPManagedConnectionFactory
            property host = localhost
            property port = 20001
            property debug = true
        Connectionfactory interface: jcademo.raoutbound.natmultcp.TestConnectionFactory
        Connectionfactory class: jcademo.raoutbound.natmultcp.TestConnectionFactoryImpl
        Connection interface: jcademo.raoutbound.natmultcp.TestConnection
        Connection class: jcademo.raoutbound.natmultcp.TestConnectionImpl
     -->
    <display-name>Outbound native multi TCP</display-name>
    <vendor-name>AV</vendor-name>
    <eis-type>Demo</eis-type>
    <resourceadapter-version>1.0</resourceadapter-version>
    <resourceadapter>
        <resourceadapter-class>jcademo.raoutbound.natmultcp.NatMulTCPAdapter</resourceadapter-class>
        <outbound-resourceadapter>
            <connection-definition>
                <managedconnectionfactory-class>jcademo.raoutbound.natmultcp.NatMulTCPManagedConnectionFactory</managedconnectionfactory-class>
                <config-property>
                    <config-property-name>host</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>port</config-property-name>
                    <config-property-type>java.lang.Integer</config-property-type>
                    <config-property-value>20001</config-property-value>
                </config-property>
                <config-property>
                    <config-property-name>debug</config-property-name>
                    <config-property-type>java.lang.Boolean</config-property-type>
                    <config-property-value>true</config-property-value>
                </config-property>
                <connectionfactory-interface>jcademo.raoutbound.natmultcp.TestConnectionFactory</connectionfactory-interface>
                <connectionfactory-impl-class>jcademo.raoutbound.natmultcp.TestConnectionFactoryImpl</connectionfactory-impl-class>
                <connection-interface>jcademo.raoutbound.natmultcp.TestConnection</connection-interface>
                <connection-impl-class>jcademo.raoutbound.natmultcp.TestConnectionImpl</connection-impl-class>
            </connection-definition>
            <transaction-support>NoTransaction</transaction-support>
        </outbound-resourceadapter>
    </resourceadapter>
</connector>

Binding for WildFly/JBoss (ironjacamar.xml):

<?xml version="1.0" encoding="UTF-8"?>
<ironjacamar>
    <connection-definitions>
        <connection-definition class-name="jcademo.raoutbound.natmultcp.NatMulTCPManagedConnectionFactory" jndi-name="java:/eis/NatMulTCP">
            <pool>
                <min-pool-size>5</min-pool-size>
                <max-pool-size>10</max-pool-size>
                <prefill>true</prefill>  
                <use-strict-min>true</use-strict-min>  
                <capacity>
                    <decrementer class-name="org.jboss.jca.core.connectionmanager.pool.capacity.SizeDecrementer">
                        <config-property name="size">1</config-property>
                    </decrementer>
                </capacity>
            </pool>
            <timeout>
                <blocking-timeout-millis>5000</blocking-timeout-millis>
                <idle-timeout-minutes>4</idle-timeout-minutes>
                <allocation-retry>2</allocation-retry>
                <allocation-retry-wait-millis>3000</allocation-retry-wait-millis>
            </timeout>
        </connection-definition>
    </connection-definitions>
</ironjacamar>

Test client:

package jcademo.client;

import java.io.IOException;

import javax.annotation.Resource;
import javax.resource.ResourceException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import jcademo.raoutbound.natmultcp.TestConnection;
import jcademo.raoutbound.natmultcp.TestConnectionFactory;

@WebServlet(urlPatterns={"/testnatmultcp"})
public class TestNatMulTCP extends HttpServlet {
    private static final long serialVersionUID = 1L;
    @Resource(mappedName = "java:/eis/NatMulTCP")
    private TestConnectionFactory cf;
    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        try {
            TestConnection con = cf.getConnection();
            int c = con.add(123, 456);
            con.close();
            resp.getWriter().println(c);
        } catch (ResourceException e) {
            e.printStackTrace();
        } 
    }
}

Obviously there is no reason to use C just to use TCP - the socket API in Java is much more convenient than in C. But using this example allows this code to be functional equivalent to previous examples.

Deployment structure:

Here is an example (Multi TCP).

multcp.jar:

     0 Sun Jun 23 21:24:26 EDT 2019 META-INF/
   105 Sun Jun 23 21:24:24 EDT 2019 META-INF/MANIFEST.MF
     0 Fri Jun 21 20:38:42 EDT 2019 jcademo/
     0 Fri Jun 21 20:38:42 EDT 2019 jcademo/raoutbound/
     0 Fri Jun 21 20:38:44 EDT 2019 jcademo/raoutbound/multcp/
  1550 Fri Jun 21 20:38:44 EDT 2019 jcademo/raoutbound/multcp/MulTCPAdapter.class
  5418 Fri Jun 21 20:38:44 EDT 2019 jcademo/raoutbound/multcp/MulTCPManagedConnection.class
  3741 Fri Jun 21 20:38:44 EDT 2019 jcademo/raoutbound/multcp/MulTCPManagedConnectionFactory.class
   255 Fri Jun 21 20:38:44 EDT 2019 jcademo/raoutbound/multcp/TestConnection.class
   725 Fri Jun 21 20:38:44 EDT 2019 jcademo/raoutbound/multcp/TestConnectionFactory.class
  2417 Fri Jun 21 20:38:44 EDT 2019 jcademo/raoutbound/multcp/TestConnectionFactoryImpl.class
  1856 Fri Jun 21 20:38:44 EDT 2019 jcademo/raoutbound/multcp/TestConnectionImpl.class

test-multcp.rar:

     0 Sun Jun 23 21:24:26 EDT 2019 META-INF/
   105 Sun Jun 23 21:24:24 EDT 2019 META-INF/MANIFEST.MF
  8677 Sun Jun 23 21:24:26 EDT 2019 multcp.jar
  1145 Fri May 10 22:19:16 EDT 2019 META-INF/ironjacamar.xml
  2948 Fri May 10 19:35:52 EDT 2019 META-INF/ra.xml

test.war:

     0 Sun Jun 23 21:24:26 EDT 2019 META-INF/
   105 Sun Jun 23 21:24:24 EDT 2019 META-INF/MANIFEST.MF
     0 Sun Jun 23 21:24:26 EDT 2019 WEB-INF/
   268 Sun Apr 21 20:55:40 EDT 2019 WEB-INF/web.xml
     0 Sun Jun 23 21:24:26 EDT 2019 WEB-INF/classes/
     0 Fri Jun 21 20:38:42 EDT 2019 WEB-INF/classes/jcademo/
     0 Fri Jun 21 20:38:44 EDT 2019 WEB-INF/classes/jcademo/client/
  1612 Fri Jun 21 20:38:44 EDT 2019 WEB-INF/classes/jcademo/client/TestMulTCP.class
   613 Fri May 17 21:23:22 EDT 2019 WEB-INF/jboss-deployment-structure.xml

test.ear:

     0 Sun Jun 23 21:24:28 EDT 2019 META-INF/
   105 Sun Jun 23 21:24:26 EDT 2019 META-INF/MANIFEST.MF
   898 Sun May 19 20:50:50 EDT 2019 META-INF/application.xml
  9121 Sun Jun 23 21:24:26 EDT 2019 test-multcp.rar
  7899 Sun Jun 23 21:24:26 EDT 2019 test.war

Article history:

Version Date Description
1.0 May 22nd 2019 Initial version

Other articles:

See list of all articles here

Comments:

Please send comments to Arne Vajhøj