Configuring a Resource Adapter for JBoss AS7: OpenMQ

This tutorial shows how to configure a Resource Adapter on JBoss AS 7. The purpose of it, is connecting your MDB on another JMS provider (OpenMQ).

What is a Resource Adapter ? to keep it simple, imagine a resource adapter something analogous to a JDBC driver. Both provide a standard API through which an application can access a resource that is outside the JEE server. For a resource adapter, the outside resource is an EIS (Enterprise Information system) and allows a standard  way for EIS vendor’s software to be integrated with JEE applications; on the other hand for a JDBC driver, it is a DBMS(Database Management System).

A resource adapter is stored in a RAR (Resource Adapter Archive) file and may be deployed on any JEE server, much like the EAR file of a JEE application.

 In this tutorial we will show how to configure a resource adapter for connecting with glassfish’s OpenMQ messaging system. So the first step is getting the OpenMQ RA. This one can be downloaded from http://mq.java.net/downloads.html  or you can get it bundled into Glassfish distribution (under the folder “GLASSFISH_HOME\mq\lib” ).

So , once you have got the file imqjmsra.rar unzip it with a common unzip tool.
jboss as 7 resource adapter jca
As you can see, in the root folder you can find all the JARs which are used by OpenMQ and the Glassfish’s naming context interfaces. In the META-INF folder there’s the ra.xml file which contains all the information required to configure the Resource Adapter.

Now we need to configure the resource on JBoss AS 7: how can we do that ? JBoss AS 7 uses IronJacamar as JCA implementation.
The subsystem is contained into the AS 7 configuration file:

  <subsystem xmlns="urn:jboss:domain:jca:1.1">
  . . . . .
  </subsystem>

Now, the simplest way to create your resource adapter configuration is via the rar-info tool contained into the ironjacamar-1.x\doc\as folder. ( The official IronJacamar project page is  http://www.jboss.org/ironjacamar/ where you can download the software) .

The information about the resource adapter is generated using the following command:

./rar-info.sh imqjmsra.rar

This will generate a report file named imqjmsra-report.txt.  This is the most important part of the report:

<resource-adapters>
  <resource-adapter>
    <archive>imqjmsra.rar</archive>
    <config-property name="UserName">guest</config-property>
    <config-property name="ConnectionURL">mq://localhost:7676/</config-property>
    <config-property name="Password">guest</config-property>
    <transaction-support>XATransaction</transaction-support>
    <connection-definitions>
      <connection-definition class-name="com.sun.messaging.jms.ra.ManagedConnectionFactory" enabled="true" jndi-name="java:jboss/eis/QueueConnection" pool-name="QueueConnection" use-ccm="true" use-java-context="true">
        <config-property name="UserName">guest</config-property>
        <config-property name="AddressList">localhost</config-property>
        <config-property name="Password">guest</config-property>
        <xa-pool>
          <min-pool-size>0</min-pool-size>
          <max-pool-size>10</max-pool-size>
          <prefill>false</prefill>
          <use-strict-min>false</use-strict-min>
          <flush-strategy>FailingConnectionOnly</flush-strategy>
          <pad-xid>false</pad-xid>
          <wrap-xa-resource>true</wrap-xa-resource>
        </xa-pool>
        <security>
          <application/>
        </security>
      </connection-definition>
      <connection-definition class-name="com.sun.messaging.jms.ra.ManagedConnectionFactory" enabled="true" jndi-name="java:jboss/eis/QueueConnection" pool-name="QueueConnection" use-ccm="true" use-java-context="true">
        <config-property name="UserName">guest</config-property>
        <config-property name="AddressList">localhost</config-property>
        <config-property name="Password">guest</config-property>
        <xa-pool>
          <min-pool-size>0</min-pool-size>
          <max-pool-size>10</max-pool-size>
          <prefill>false</prefill>
          <use-strict-min>false</use-strict-min>
          <flush-strategy>FailingConnectionOnly</flush-strategy>
          <pad-xid>false</pad-xid>
          <wrap-xa-resource>true</wrap-xa-resource>
        </xa-pool>
        <security>
          <application/>
        </security>
      </connection-definition>
      <connection-definition class-name="com.sun.messaging.jms.ra.ManagedConnectionFactory" enabled="true" jndi-name="java:jboss/eis/TopicConnection" pool-name="TopicConnection" use-ccm="true" use-java-context="true">
        <config-property name="UserName">guest</config-property>
        <config-property name="AddressList">localhost</config-property>
        <config-property name="Password">guest</config-property>
        <xa-pool>
          <min-pool-size>0</min-pool-size>
          <max-pool-size>10</max-pool-size>
          <prefill>false</prefill>
          <use-strict-min>false</use-strict-min>
          <flush-strategy>FailingConnectionOnly</flush-strategy>
          <pad-xid>false</pad-xid>
          <wrap-xa-resource>true</wrap-xa-resource>
        </xa-pool>
        <security>
          <application/>
        </security>
      </connection-definition>
    </connection-definitions>
    <admin-objects>
      <admin-object class-name="com.sun.messaging.Queue" enabled="true" jndi-name="java:jboss/eis/ao/Queue" pool-name="Queue" use-java-context="true">

      </admin-object>
      <admin-object class-name="com.sun.messaging.Topic" enabled="true" jndi-name="java:jboss/eis/ao/Topic" pool-name="Topic" use-java-context="true">

      </admin-object>
    </admin-objects>
  </resource-adapter>
</resource-adapters>

We need applying just a little change to this XML: get rid of the resource-adapters and resource-adapter and archive root elements and replace them with the <ironjacamar> element as follows:

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

<ironjacamar>

    <config-property name="UserName">guest</config-property>
    <config-property name="ConnectionURL">mq://localhost:7676/</config-property>
    <config-property name="Password">guest</config-property>
    <transaction-support>XATransaction</transaction-support>
    <connection-definitions>
      <connection-definition class-name="com.sun.messaging.jms.ra.ManagedConnectionFactory" enabled="true" jndi-name="java:jboss/eis/QueueConnection" pool-name="QueueConnection" use-ccm="true" use-java-context="true">
        <config-property name="UserName">guest</config-property>
        <config-property name="AddressList">localhost</config-property>
        <config-property name="Password">guest</config-property>
        <xa-pool>
          <min-pool-size>0</min-pool-size>
          <max-pool-size>10</max-pool-size>
          <prefill>false</prefill>
          <use-strict-min>false</use-strict-min>
          <flush-strategy>FailingConnectionOnly</flush-strategy>
          <pad-xid>false</pad-xid>
          <wrap-xa-resource>true</wrap-xa-resource>
        </xa-pool>
        <security>
          <application/>
        </security>
      </connection-definition>

     <connection-definition class-name="com.sun.messaging.jms.ra.ManagedConnectionFactory" enabled="true" jndi-name="java:jboss/eis/QueueConnection" pool-name="QueueConnection" use-ccm="true" use-java-context="true">
        <config-property name="UserName">guest</config-property>
        <config-property name="AddressList">localhost</config-property>
        <config-property name="Password">guest</config-property>
        <xa-pool>
          <min-pool-size>0</min-pool-size>
          <max-pool-size>10</max-pool-size>
          <prefill>false</prefill>
          <use-strict-min>false</use-strict-min>
          <flush-strategy>FailingConnectionOnly</flush-strategy>
          <pad-xid>false</pad-xid>
          <wrap-xa-resource>true</wrap-xa-resource>
        </xa-pool>
        <security>
          <application/>
        </security>
      </connection-definition>

      <connection-definition class-name="com.sun.messaging.jms.ra.ManagedConnectionFactory" enabled="true" jndi-name="java:jboss/eis/TopicConnection" pool-name="TopicConnection" use-ccm="true" use-java-context="true">
        <config-property name="UserName">guest</config-property>
        <config-property name="AddressList">localhost</config-property>
        <config-property name="Password">guest</config-property>
        <xa-pool>
          <min-pool-size>0</min-pool-size>
          <max-pool-size>10</max-pool-size>
          <prefill>false</prefill>
          <use-strict-min>false</use-strict-min>
          <flush-strategy>FailingConnectionOnly</flush-strategy>
          <pad-xid>false</pad-xid>
          <wrap-xa-resource>true</wrap-xa-resource>
        </xa-pool>
        <security>
          <application/>
        </security>
      </connection-definition>
    </connection-definitions>
    <admin-objects>
      <admin-object class-name="com.sun.messaging.Queue" enabled="true" jndi-name="java:jboss/eis/ao/Queue" pool-name="Queue" use-java-context="true">

      </admin-object>
      <admin-object class-name="com.sun.messaging.Topic" enabled="true" jndi-name="java:jboss/eis/ao/Topic" pool-name="Topic" use-java-context="true">

      </admin-object>
    </admin-objects>

</ironjacamar>

Deploying the Resource Adapter file (ironjacamar.xml)

The simplest way to deploy the Resource Adapter is using an exploded directory which will contain all the configuration files: create a dir named imqjmsra.rar and save the file created with rar-info under the META-INF folder of it, with the name ironjacamar.xml

 Next step are Resource Adapter libraries : since you might want to deploy remote JMS clients (using OpenMQ JNDI) it’s better to install them as module so also other applications can use them.

Create the path modules\com\sun\openmq\main under your installation. There add the JAR files contained into imqjmsra.rar and configure the module in module.xml:

jboss as 7 resource adapter jca

<module xmlns="urn:jboss:module:1.1" name="com.sun.openmq">
    <properties>
        <property name="jboss.api" value="private"/>
    </properties>

    <dependencies>
        <module name="javax.jms.api" />
        <module name="javax.resource.api"/>
        <module name="javax.api"/>

    </dependencies>

    <resources>
       <resource-root path="fscontext.jar"/>
       <resource-root path="imqbroker.jar"/>
       <resource-root path="imqjmsbridge.jar"/>
       <resource-root path="imqjmsra.jar"/>
       <resource-root path="imqjmx.jar"/>
       <resource-root path="imqstomp.jar"/>

    </resources>
</module>

Fine, now let’s return to our exploded folder and let’s add the module dependency in MANIFEST.MF

Manifest-Version: 1.0
Dependencies: com.sun.openmq export

So this will be our imqjmsra.rar which will just contains three files into the META-INF folder:

imqjmsra.rar
+---META-INF
    ¦   ironjacamar.xml
    ¦   MANIFEST.MF
    ¦   ra.xml

So, when things go OK we should have finished with the resource adapter configuration. All we need is deploying the resource adapter into JBoss AS 7.
Unfortunately installing the RA took a lot more of headaches, since the OpenMQ RA does not seem 100% conform to ActivationSpec. But we fixed and with huge pleasure we managed to get it working!


First issue: when deploying the resource adapter as it is, the deployer raises the following error:
org.jboss.jca.validator.ValidatorException: Severity: ERROR
Section: 19.4.2
Description: A ResourceAdapter must implement a “public int hashCode()” method.
Code: com.sun.messaging.jms.ra.ResourceAdapter

Severity: ERROR
Section: 19.4.2
Description: A ResourceAdapter must implement a “public boolean equals(Object)” method.
Code: com.sun.messaging.jms.ra.ResourceAdapter

So obviously our RA doesn’t seem 100% compliant. There are two things you can do, one is implementing the two methods by yourself in the com.sun.messaging.jms.ra.ResourceAdapter class, or turn off validation on the JBoss JCA subsystem. We will choose the latter so turn off archive validation in your AS 7 configuration:

<subsystem xmlns="urn:jboss:domain:jca:1.1">
  <archive-validation enabled="false" fail-on-error="true" fail-on-warn="false"/>
  . . . .
</subsystem>

Second issue: the second issue we faced was a bit harder, as a matter of fact there was a NPE when the resource adapter was initialized:

13:34:15,054 ERROR [org.jboss.msc.service.fail] (MSC service thread 1-6) MSC00001: Failed to start service jboss.ra.deployer.imqjmsra: org.jboss.msc.service.StartException in service jboss.ra.deployer.imqjmsra: JBAS010446: Failed to start RA deployment [imqjmsra]
at org.jboss.as.connector.services.resourceadapters.deployment.ResourceAdapterDeploymentService.start(ResourceAdapterDeploymentService.java:105)
. . . . .
Caused by: org.jboss.jca.deployers.common.DeployException: IJ020052: Unable to associate com.sun.messaging.jms.ra.ActivationSpec
at org.jboss.jca.deployers.common.AbstractResourceAdapterDeployer.associateResourceAdapter(AbstractResourceAdapterDeployer.java:386)
… 10 more
Caused by: java.lang.NullPointerException
at com.sun.messaging.jms.ra.ResourceAdapter._getRAUID(ResourceAdapter.java:2081)
at com.sun.messaging.jms.ra.ActivationSpec.setResourceAdapter(ActivationSpec.java:331)

The problem is buried into the _getRAUID method which fails to return the Resource Adapter Id. The only viable choice is getting our hands dirty with the source code. This is the part of the code we are dealing with:

protected String _getRAUID() {

        if (raUID==null && !_isRADirect()){
            try {

                XAConnectionImpl xaci = (com.sun.messaging.jmq.jmsclient.XAConnectionImpl) xacf.createXAConnection();
                raUID = Long.toString(xaci.generateUID());
                xaci.close();
            } catch (JMSException jmse){
                _loggerB.severe("Unable to generate UID:"+jmse.getMessage());
            }
        }

        return null;
    }

What happens here is that a lazy initialization of the XAConnection is performed. As per ActivationSpec we should initialize our resources into the start method of our Resource Adapter, so change this method to:

protected String _getRAUID() {


        if (  !_isRADirect()){
            return raUID;
        }

        return null;
    }

and add the XA Connection creation at the end of the start method:

    public synchronized void start(BootstrapContext ctx) throws ResourceAdapterInternalException {
       . . ..
      try {
                XAConnectionImpl xaci = (com.sun.messaging.jmq.jmsclient.XAConnectionImpl) xacf.createXAConnection();
                raUID = Long.toString(xaci.generateUID());


                xaci.close();
            } catch (JMSException jmse){
                _loggerB.severe("Unable to generate UID:"+jmse.getMessage());
            }


    }

It was necessary to compile the new class (just add all the OpenMQ libraries in your classpath) and update the archive in the modules section:

jar uvf com imqjmsra.jar

(The class ResourceAdapter.java is contained in the package com.sun.messaging.jms.ra)
Great, we’re almost there. The last cry of the application server is about a duplicate resource java:jboss/eis/QueueConnection

07:09:21,668 ERROR [org.jboss.msc.service] (MSC service thread 1-5) MSC00002: Invocation of listener “org.jboss.as.connector.deployers.ra.processors.ParsedRaDeploymentProcessor$1@1e7a6ae” failed: java.lang.IllegalStateException: JBAS014666:
 Duplicate resource java:jboss/eis/QueueConnection

Recall the file ironjacamar.xml ? careful readers should have noticed that it actually contains a duplication for the resource resource java:jboss/eis/QueueConnection . However that’s not the fault of the rarinfo tool, it’s the ra.xml file (which ships with the RA) that contains the duplication (the rarinfo creates the sample RA connection from the ra.xml file)

So this is the final ironjacamar.xml file which needs to be deployed in META-INF:

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

<ironjacamar>

    <config-property name="UserName">guest</config-property>
    <config-property name="ConnectionURL">mq://localhost:7676/</config-property>
    <config-property name="Password">guest</config-property>
    <transaction-support>XATransaction</transaction-support>
    <connection-definitions>
      <connection-definition class-name="com.sun.messaging.jms.ra.ManagedConnectionFactory" enabled="true" jndi-name="java:jboss/eis/QueueConnection" pool-name="QueueConnection" use-ccm="true" use-java-context="true">
        <config-property name="UserName">guest</config-property>
        <config-property name="AddressList">localhost</config-property>
        <config-property name="Password">guest</config-property>
        <xa-pool>
          <min-pool-size>0</min-pool-size>
          <max-pool-size>10</max-pool-size>
          <prefill>false</prefill>
          <use-strict-min>false</use-strict-min>
          <flush-strategy>FailingConnectionOnly</flush-strategy>
          <pad-xid>false</pad-xid>
          <wrap-xa-resource>true</wrap-xa-resource>
        </xa-pool>
        <security>
          <application/>
        </security>
      </connection-definition>
      
      <connection-definition class-name="com.sun.messaging.jms.ra.ManagedConnectionFactory" enabled="true" jndi-name="java:jboss/eis/TopicConnection" pool-name="TopicConnection" use-ccm="true" use-java-context="true">
        <config-property name="UserName">guest</config-property>
        <config-property name="AddressList">localhost</config-property>
        <config-property name="Password">guest</config-property>
        <xa-pool>
          <min-pool-size>0</min-pool-size>
          <max-pool-size>10</max-pool-size>
          <prefill>false</prefill>
          <use-strict-min>false</use-strict-min>
          <flush-strategy>FailingConnectionOnly</flush-strategy>
          <pad-xid>false</pad-xid>
          <wrap-xa-resource>true</wrap-xa-resource>
        </xa-pool>
        <security>
          <application/>
        </security>
      </connection-definition>
    </connection-definitions>
    <admin-objects>
      <admin-object class-name="com.sun.messaging.Queue" enabled="true" jndi-name="java:jboss/eis/ao/Queue" pool-name="Queue" use-java-context="true">

      </admin-object>
      <admin-object class-name="com.sun.messaging.Topic" enabled="true" jndi-name="java:jboss/eis/ao/Topic" pool-name="Topic" use-java-context="true">

      </admin-object>
    </admin-objects>
 
</ironjacamar>

Great, now we’re finally ready. You need to start your Glassfish server where OpenMQ lives.

glassfish/bin/startserv.sh

Fine, now start JBoss AS 7 (hint: in order to avoid conflicts with glassfish, if you are running both on the same Pc, apply a port-offset to your configuration)

    <socket-binding-group name="standard-sockets" default-interface="public" port-offset="200">

On your JBoss AS 7 console you should now see:

 13:04:31,862 INFO [org.jboss.as.deployment.connector] (MSC service thread 1-3)
JBAS010401: Bound JCA ConnectionFactory [java:jboss/eis/TopicConnection]
13:04:31,863 INFO [org.jboss.as.deployment.connector] (MSC service thread 1-2)
JBAS010401: Bound JCA AdminObject [java:jboss/eis/ao/Topic]
13:04:31,864 INFO [org.jboss.as.deployment.connector] (MSC service thread 1-5)
JBAS010401: Bound JCA AdminObject [java:jboss/eis/ao/Queue]
13:04:31,863 INFO [org.jboss.as.deployment.connector] (MSC service thread 1-4)
JBAS010401: Bound JCA ConnectionFactory [java:jboss/eis/QueueConnection]

Great! now let’s test everything:


We will create a ConnectionFactory and a JMS Destination (Queue) on the Glassifsh side. Move to the GF administration console (http://localhost:4848):

Here’s the ConnectionFactory:

jboss as 7 resource adapter glassfish openmq jca

and here’s a Queue Destination which will be consumed by JBoss AS 7
jboss as 7 resource adapter glassfish openmq jca
So that is it: Now we need an MDB on your JBoss AS 7 instance which will consume messages from the external provider.

jboss as 7 resource adapter jca

Here’s our simple MDB impementation: notice the most important part: the @ResourceAdapter definition:

package com.sample;

import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;

import org.jboss.ejb3.annotation.ResourceAdapter;

@MessageDriven(activationConfig = {
         
    @ActivationConfigProperty(propertyName = "destination", propertyValue = "demoQueue"),
    @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue") 
        
})
@ResourceAdapter("imqjmsra.rar")
public class MDBSample implements MessageListener {
     
   public void onMessage(Message message) {

     TextMessage tm = (TextMessage) message;
       try {

         System.out.println("Received message "+tm.getText());

       } catch (JMSException e) {
  
        e.printStackTrace();
       }
 

   }

}

Deploy your MDB on JBoss AS 7. Now for testing it, I have created a simple JMS client, which will run on Glassfish server.

package com.sample;

import java.io.IOException;

import javax.annotation.Resource;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


@WebServlet("/Test")
public class Test extends HttpServlet {
    private static final long serialVersionUID = 1L;


    @Resource(name="connFactory", mappedName="GFConnectionFactory")
    private QueueConnectionFactory qFactory;

    @Resource(name="demoQueue", mappedName="demoQueue")
    private Queue queue;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        try {
            QueueConnection qConn = (QueueConnection)qFactory.
                    createConnection();

            Session session = qConn.createSession(false, Session.AUTO_ACKNOWLEDGE);
            MessageProducer publisher;
            publisher = session.createProducer(queue);

            qConn.start();

            TextMessage message = session.createTextMessage("Received Message Hello AS 7 !");
            publisher.send(message);
        } catch (JMSException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }


    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
    }

}

It was with great satisfaction that I could read the message on the JBoss AS 7 console!!!!

jboss as 7 resource adapter jca