3 Replies Latest reply on May 24, 2016 10:33 AM by filosofisto

    JBOSS EAP 6.1 and WebSphere MQ 7.0.1 Integration

    joshid

      As a WebSphere MQ Developer and budding JBOSS enthusiast I'm posting what has been for me a few days of research. There are several great posting about making JBOSS work with Websphere MQ. I've found them to either be a bit dated  and/or not necessarily covering everything that I needed to do. So I decided to try posting it in hopes of helping others get JBOSS working with MQ. My goals were thus:

       

      • Use an EJB3 Message Driven Bean to read off of a WebSphere MQ queue.
      • Use JMS to send a reply message back in the aforementioned MDB using the ReplyToQueue and reading Correlation ID header values from the requesting message.
      • Without having to give up on Annotations externalize the MQ-specific connection  configuration information out of the code and EAR and into standalone.xml.
      • Be able to connect to any Queue Manager (local and remote) using wmq.jmsra.rar.

       

      Here are the versions of software I used:

      • Java 1.7
      • JBOSS EAP 6.1
      • WebSphere MQ 7.0.1.9. BEWARE: I attempted to use 7.5.0.2's rar file and it wouldn't deploy but gave me an FDC. It looks like I ran into an IBM incident IC96105 targeted to be fixed in 7.5.0.3.

       

      1.   Get a copy of wmq.jmsra.rar from <mq-home>/java/lib/jca/ and copy it to your JBOSS deployment directory.

      2.   Update standalone.xml (or whatever environment you are editing) and add the following entry to it. I put it under <subsystem xmlns="urn:jboss:domain:remoting:1.1"> Note that I scrubbed my real values with test values so I apologize in advance if a typo gets introduced:


      <subsystem xmlns="urn:jboss:domain:resource-adapters:1.1">

           <resource-adapters>    

                <resource-adapter id="wmq.jmsra.rar">

                     <archive>wmq.jmsra.rar</archive>

                     <transaction-support>LocalTransaction</transaction-support>

                     <connection-definitions>

                          <connection-definition class-name="com.ibm.mq.connector.outbound.ManagedConnectionFactoryImpl" jndi-name="java:jboss/WMQ.TESTQMGR" pool-name="wmq.jmsra">

                               <config-property name="port">1414</config-property>

                               <config-property name="channel">SYSTEM.DEF.SVRCONN</config-property>

                               <config-property name="hostName">1.2.3.4</config-property>

                               <config-property name="channel">SYSTEM.DEF.SVRCONN</config-property>

                               <config-property name="transportType">CLIENT</config-property>

                               <config-property name="queueManager">TESTQMGR</config-property>

                          </connection-definition>

               </connection-definitions>

               <admin-objects>

               </admin-objects>

           </resource-adapter>

      </resource-adapters>

      </subsystem>

       

      At this point you should be able to start JBOSS and not get any errors with the rar deployed (of course you have no code to do anything with it but for a JBOSS newbie like me its nice to see iterative progress).

       

      3.  Define a your Message Driven Bean and put in some annotations. In particular you will notice that we have @ResourceAdapter and @TransactionAttribute defined. Both of these are required:

       

      @MessageDriven( name="AttTestMessageBean", activationConfig =

      {

           @ActivationConfigProperty(propertyName = "destinationType",propertyValue = "javax.jms.Queue"),

           @ActivationConfigProperty(propertyName = "useJNDI", propertyValue = "false"),

      })

      @ResourceAdapter(value = "wmq.jmsra.rar")

      @TransactionAttribute(TransactionAttributeType.NEVER)

      public class TestMessageBean implements MessageListener {

      [snip-- the rest is at the end]

       

      4. There are additional @ActivationConfigProperty entries that you could add to the above. However, as a WebSphere MQ guy these are really environmental settings and not something that should be embedded in the code or the EAR file for that matter. So to externalize the variables I define an ejb-jar.xml that I put this in META-INF of my ear file. This xml only contains the escaped properties I didn't define as annotations in the previous step (EJB 3 supports partial deployment descriptors):


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

      <jboss:ejb-jar xmlns:jboss="http://www.jboss.com/xml/ns/javaee"

           xmlns="http://java.sun.com/xml/ns/javaee"

           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

           xmlns:c="urn:clustering:1.0"

           xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee

                                              http://www.jboss.org/j2ee/schema/jboss-ejb3-2_0.xsd

                                              http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd" version="3.1">

      <enterprise-beans>

           <message-driven>

                <ejb-name>AttTestMessageBean</ejb-name>

                <ejb-class>com.testdomain.test.endpoint.TestMessageBean</ejb-class>


                <activation-config> 

                <activation-config-property> 

                     <activation-config-property-name>destination</activation-config-property-name> 

                     <activation-config-property-value>${att.destination}</activation-config-property-value> 

                </activation-config-property> 

                <activation-config-property>

                     <activation-config-property-name>hostName</activation-config-property-name>

                     <activation-config-property-value>${att.hostName}</activation-config-property-value>

                </activation-config-property>

                <activation-config-property>

                     <activation-config-property-name>queueManager</activation-config-property-name>

                     <activation-config-property-value>${att.queueManager}</activation-config-property-value>

                </activation-config-property>

                <activation-config-property>

                     <activation-config-property-name>channel</activation-config-property-name>

                     <activation-config-property-value>${att.channel}</activation-config-property-value>

                </activation-config-property>

                <activation-config-property>

                     <activation-config-property-name>transportType</activation-config-property-name>

                     <activation-config-property-value>${att.transportType}</activation-config-property-value>

                </activation-config-property>

                </activation-config>

           </message-driven>

      </enterprise-beans>

      </jboss:ejb-jar>


      With the above values ${...} these are now escaped to Java VM System properties. So we can now define these values externally from the the EAR file.

       

      5. In order for JBOSS to read the ${...} values as escaped Java VM System properties and not take them literally we need to enable the <spec-descriptor-property-replacement> parameter in standalone.xml:


      <subsystem xmlns="urn:jboss:domain:ee:1.1">

           <spec-descriptor-property-replacement>true</spec-descriptor-property-replacement>

           <jboss-descriptor-property-replacement>true</jboss-descriptor-property-replacement>

      </subsystem>

       

      6. Finally you need to add the <system-properties> with the values for the aforementioned system properties defined in ejb-jar.xml. This tag goes just under <extensions> in standalone.xml:

       

      <system-properties>

           <property name="att.hostName" value="1.2.3.4"/>

           <property name="att.queueManager" value="TESTQMGR"/>

           <property name="att.destination" value="TEST"/>

           <property name="att.port" value="1414"/>

           <property name="att.channel" value="SYSTEM.DEF.SVRCONN"/>

           <property name="att.transportType" value="Client"/>

      </system-properties>

               

      At this point if you have coded up your MDB, package up your ear file, deploy and you have a message driven bean that can read off of a remote queue. For those who may be interested, here is the base code for my onMessage() that shows me reading the correlation id using the reply to queue to put same message back to another queue:

       

      @Override

          public void onMessage(Message message)

          {

              try

              {

                  byte[] payload = null;         

                  String correlationId;

                  Destination replyToQueue;

       

                  correlationId = message.getJMSCorrelationID();

                  replyToQueue = message.getJMSReplyTo();

                 

                  if (correlationId == null || correlationId.equals(""))

                  {

                      throw new Exception("Correlation ID is NULL");

                  }

                 

                  if (replyToQueue == null)

                  {

                      throw new Exception("replyToQueue is NULL");

                  }

                 

                  if (message instanceof BytesMessage)

                  {

                      BytesMessage bm = (BytesMessage)message;

                      payload = new byte[(int)bm.getBodyLength()];

                      bm.readBytes(payload);

                  }

                  else if (message instanceof TextMessage)

                  {

                      TextMessage tm = (TextMessage)message;

                      payload = tm.getText().getBytes();

                  }

                  System.out.println("DJDJDJD I Got it! " + new String(payload));

       

                  ConnectionFactory cf = null;

                  InitialContext ic = new InitialContext();

       

                  cf = (javax.jms.ConnectionFactory) ic.lookup("java:jboss/WMQ.TESTQMGR");

                  System.out.println("cf " + cf);

       

                  Connection conn = (Connection)cf.createConnection();

                  Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);

                  MessageProducer producer = session.createProducer(replyToQueue);

                  System.out.println("replyToQueue " + replyToQueue);

                  producer.send(message);

              }

              catch (Exception ex)

              {

                  System.err.println("ERROR! " + ex);

                  ex.printStackTrace();

              }