2012. december 19., szerda

XA Transactions with IBM MQ Resource Adapter on JBoss AS7

Introduction and Problems

This post shows how to configure the IBM MQ resource adapter and the extended transactional client (for XA transactions) on JBoss AS7 as I have not yet found a proper description about this. If you are trying to make things works based on your experience with AS6, you will possibly try to just copy the com.ibm.mqetclient.jar and wmq.jmsra.rar files into some deployment folder like JBOSS_HOME/standalone/deployments/ and test your application.

Then you might see the following warning on server startup:

MQJCA4005: Distributed transactions are not available in client mode.
The extended transactional client JAR file, com.ibm.mqetclient.jar, was not be found in the class path.
If the extended transactional client is not required, no action is necessary.

(Note the poetic "was not be found" part of the log message. ;))

...or end up with the following exception when you eventually try to use the IBM MQ JMS client (during application deployment, MDB onMessage() call etc.):

com.ibm.mq.connector.DetailedResourceException: MQJCA1004: Distributed transactions are unavailable., error code: MQJCA1004 An attempt was made to use distributed transactions in an environment where they are not available. Make sure that the WebSphere MQ extended transactional client is installed if required, or use a bindings connection.

There is an article in the JBoss wiki about WebSphere MQ integration but in AS7 things are a bit different. Basically what you have to do is create a module for the extended client (com.ibm.mqetclient.jar and some other jar files) and explicitly declare a dependency to it in the resource adapter (wmq.jmsra.rar).

The resource adapter can be just copied into the deployments directory, but the client library will need a few additional jar files from MQ so it's easier to manage and reference them together as a JBoss server module.

You can find the detailed steps below for JBoss standalone mode.

Creating a JBoss Server Module for the IBM WSMQ Extended Client Library

If you create the module only referring the com.ibm.mqetclient.jar file, you might end up with the MQJCA4005 ("Distributed transactions are not available in client mode") or MQJCA1004 ("Distributed transactions are unavailable") exceptions again. So you might try adding more JAR files from the MQ distribution's Java library directory, or just add all, or start googling. Funnily, the most useful source I've found on this matter can be found on Oracle's website. It's the "Installing Third-Party JAR Files for the WebSphere MQ Adapter" section of the Java CAPS Documentation, and it goes into details about which JAR files are needed for the resource adapter to work. This mostly applies to our situation with JBoss AS7 as well.

So, create the JBOSS_HOME/modules/com/ibm/mqetclient/main/ directory and JBOSS_HOME/modules/com/ibm/mqetclient/main/module.xml file as:

<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.1" name="com.ibm.mqetclient">

    <resources>
        <resource-root path="com.ibm.mq.jar"/>
        <resource-root path="com.ibm.mqjms.jar"/>
        <resource-root path="com.ibm.mq.jmqi.jar"/>
        <resource-root path="connector.jar"/>
        <resource-root path="dhbcore.jar"/>
        <resource-root path="com.ibm.mqetclient.jar"/>
    </resources>

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

</module>

Then copy the referenced JAR files from your IBM MQ installation's Java library folder (most probably /opt/mqm/java/lib/ under Linux) into JBOSS_HOME/modules/com/ibm/mqetclient/main/.

After some trial and error to minimize the amount of JAR files I've found that the com.ibm.mq.headers.jar and com.ibm.mq.commonservices.jar files are not needed, that's why they are not listed in the module above. (Note that this is a difference compared to what's in the documentation at Oracle).


Configuring the WebSphere MQ Resource Adapter


Declaring the Dependency to the IBM WSMQ Extended Client Library Module

You have to modify wmq.jmsra.rar/META-INF/MANIFEST.MF by adding the following line:

Dependencies: com.ibm.mqetclient

You could also do this by creating a wmq.jmsra.rar/META-INF/jboss-deployment-structure.xml file as follows, but in this case it has no advantage over MANIFEST.MF as we are not using any additional features of this descriptor. Just for reference:

<jboss-deployment-structure>
    <dependencies>
        <module name="com.ibm.mqetclient" />
    </dependencies>
</jboss-deployment-structure>

Configuring the Adapter in the JBoss Server Instance

Copy the modified wmq.jmsra.rar into JBOSS_HOME/standalone/deployments/.

Define the resource adapter and configure the concection, queues etc. in JBOSS_HOME/standalone/configuration/standalone.xml, it's gonna be something like this:

<subsystem xmlns="urn:jboss:domain:resource-adapters:1.0">
    <resource-adapters>
        <resource-adapter>
            <archive>
                wmq.jmsra.rar
            </archive>
            <transaction-support>XATransaction</transaction-support>
            <connection-definitions>
                <connection-definition class-name="com.ibm.mq.connector.outbound.ManagedConnectionFactoryImpl" jndi-name="java:/jms/MyConnectionFactory" enabled="true" use-java-context="false" pool-name="MyConnectionFactory">
                    <config-property name="port">
                        1414
                    </config-property>
                    <config-property name="hostName">
                        localhost
                    </config-property>
                    <config-property name="channel">
                        MY.CHANNEL
                    </config-property>
                    <config-property name="transportType">
                        CLIENT
                    </config-property>
                    <config-property name="queueManager">
                        MY.QM
                    </config-property>
                </connection-definition>
            </connection-definitions>
            <admin-objects>
                <admin-object class-name="com.ibm.mq.connector.outbound.MQQueueProxy" jndi-name="java:/jms/MyQueue1" enabled="true" use-java-context="false" pool-name="MyQueue1Pool">
                    <config-property name="baseQueueName">
                        MY.QUEUE.1
                    </config-property>
                    <config-property name="baseQueueManagerName">
                        MY.QM
                    </config-property>
                </admin-object>
                <admin-object class-name="com.ibm.mq.connector.outbound.MQQueueProxy" jndi-name="java:/jms/MyQueue2" enabled="true" use-java-context="false" pool-name="MyQueue2Pool">
                    <config-property name="baseQueueName">
                        MY.QUEUE.2
                    </config-property>
                    <config-property name="baseQueueManagerName">
                        MY.QM
                    </config-property>
                </admin-object>
            </admin-objects>
        </resource-adapter>
    </resource-adapters>
</subsystem>

Take care here, as it seems that the pool names (pool-name attributes of the admin-object elements) should be unique. (The "5.2.1. Resource adapter descriptor" section of the documentation is not totally clear about what is the exact meaning of the pool-name property. See JBoss forum "Websphere MQ integration" for details and related possible exceptions.)

Also add the adapter reference to the EJB subsystem:
<subsystem xmlns="urn:jboss:domain:ejb3:1.3">
    <mdb>
        <resource-adapter-ref resource-adapter-name="wmq.jmsra.rar" />
        <bean-instance-pool-ref pool-name="mdb-strict-max-pool" />
    </mdb>
    ...
</subsystem>

Application Configuration

That's all. Now you should be able to use the resource adapter in your application. To do this, you have to reference the resource adapter either using annotations or deployment descriptors. Here you can see an example jboss.xml on how to configure an MDB to use the adapter:

<?xml version="1.0" encoding="UTF-8"?>
<jboss>
    <security-domain>mydomain</security-domain>
    <enterprise-beans>
        <message-driven>
            <ejb-name>MyVerySmartMDB</ejb-name>
            <resource-adapter-name>wmq.jmsra.rar</resource-adapter-name>
            <configuration-name>Standard Message Driven Bean</configuration-name>

            <resource-ref>
                <description>My WSMQ ConnectionFactory</description>
                <res-ref-name>jms/ConnectionFactory</res-ref-name>
                <jndi-name>java:/jms/ConnectionFactory</jndi-name>
                <res-type>javax.jms.ConnectionFactory</res-type>
                <res-auth>Container</res-auth>
            </resource-ref>
        </message-driven>
    </enterprise-beans>
</jboss>

And that's all. Really. ;)

Cheers.

Infinispan Cache Naming: Is "config" a Reserved Word?

Assume you have some EJBs, e.g. an EJB 3.1 singleton, and some Infinispan caches defined in you JBoss AS7 instance.
@Resource(mappedName = "java:jboss/infinispan/cache/mycontainer/mycache")
protected Cache<String, String> mycache;
The @Resource annotation here is referring to an Infinispan cache defined in JBoss AS7 as:
<cache-container name="mycontainer" default-cache="mycache">
    <transport lock-timeout="60000"/>
    <replicated-cache name="mycache" mode="SYNC" batching="true">
        <locking isolation="READ_COMMITTED" />
    </replicated-cache>
</cache-container>
Now if you try to add another cache named "config" as follows:
    <replicated-cache name="config" mode="SYNC" batching="true">
        <locking isolation="READ_COMMITTED" />
    </replicated-cache>
...and try to inject it into your bean:
@Resource(mappedName = "java:jboss/infinispan/cache/mycontainer/config")
protected Cache<String, String> config;
...you might encounter the following deployment error:
JBAS015870: Deploy of deployment "MyApplication" was rolled back with failure message {
...
"jboss.naming.context.java.module.\"MyApplication\".\"MyApplication\".env.\"com.example.MyBean\".config Missing[jboss.naming.context.java.jboss.infinispan.cache.mycontainer.config]",
...
}

Moreover you have no stack traces in the server log, so it's a trial and error process.

But... If you rename the cache to "myconfig", everything works as expected.

So is "config" a reserved word in Infinispan? If so, where is this documented? Anyways...