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.

3 megjegyzés:

  1. I tried this method (JBOSS 7.1.1 and WSMQ 7.0.1) but my mdb still doesn't work. :(

    If I hard code the hostname and port with @ActivationConfigProperty annotation then it works.

    If I put it into standalone.xml like above it always wants to connect to localhost:1414

    Do you have any idea why JBOSS ignores the hostName and port in standalone.xml?

    Any help would be appreciated.

    VálaszTörlés
  2. Excellent write up Patrik. I was stuck on the app server configuration for a couple of days until I came across your blog. Thanks!

    VálaszTörlés
  3. I tried doing this set up on Windows PC and getting this error.
    So does this fix actually require that the Windows MQ Client is fully installed on the target PC?
    I really don't want to do this, I thought I would only need the client jars set up in the module.

    Eventually this is going to be deployed to RedHat, but wondering if you are familiar with this error.

    4 ERROR [stderr] (MSC service thread 1-8) | mqjbnd level :- CC=2;RC=2495;AMQ8568: The native JNI library 'mqjbnd' was not found. [3=mqjb
    here MQ\java\lib\mqjbnd.dll: Can't load IA 32-bit .dll on a AMD 64-bit platform

    VálaszTörlés