JBoss EAP application server is Java EE 6 certified, it implements via IronJacamar framework, the Java EE Connector Architecture (JCA) standard. This standard provides connection ability for Java EE applications to interact with external data system like JMS broker, file system or mainframe. The JCA standard is similar to the JDBC standard. The latter interacts only with relational databases but JCA can interact with more data system providers and it can integrate different kinds of Enterprise Information System (EIS).

JCA standard manages severals features like :

  • System connection (socket, IPC, …)
  • Transactions management (locales, XA)
  • Security management and user access
  • Life cycle and thread management

Service provider supplies to developer or integrator a JCA driver a.k.a : Resource Adapter

Goal of this post :

Best practices for integrating WebSphere MQ (WMQ) with JBoss EAP 6 (JEAP)

Prerequisites

For this lab, you need the following software :

Warning

Warning, it is trial or evaluation only, production deployment requires the purchase of subscription for JBoss and license for IBM

Development

In order to test our integration we need to develop a client for sending, and a MDB to receive JMS message.

For sending

In our case we create a Stateless EJB to send the JMS message. It is possible to use another mechanism for the transmission, a servlet , for example.

// ..OMIT .. //

@Stateless
public class MoMSenderBean {

  // ..OMIT .. //

	@Resource(name = "jms/connectionFactory")
	ConnectionFactory factory;

	@Resource(name = "jms/queue/Queue")
	Queue queue;

	public void sendMessage(String message) throws JMSException {

            Connection connection = factory.createConnection();
            LOGGER.trace("Recuperation d'une connexion {}", connection);

            Session session = connection.createSession(false, AUTO_ACKNOWLEDGE);
            MessageProducer producer = session.createProducer(queue);
            LOGGER.trace("Creation du sender {} sur la queue {}", producer, queue);

            TextMessage textMessage = session.createTextMessage(message);
            textMessage.setJMSCorrelationID(UUID.randomUUID().toString());
            producer.send(textMessage);
            producer.close();
            session.close();
            connection.close();
	}
}

Note in this code, JMS resources ( Factory and Queue ) are retrieved from the scope of the current module.

More information about JNDI and JBoss

For reception

In the Java EE world, to allow applications to receive JMS messages asynchronously, we must develop a special bean. This bean is a Message Driven Bean (MDB), the object is called when a message is received in the JMS queue. The class has to implement javax.jms.MessageListener interface.

// ..OMIT .. //

public class MoMMDB implements MessageListener {

  // ..OMIT .. //

	public void onMessage(Message message) {
		LOGGER.info("Received Message from queue: {}" + (TextMessage) message);

	}
}

Of course, it’s possible to add ActivationSpec annotations to MDB, but these annotations drowns application contextual problematic (environment outside the application, WMQ server hostname / port for example) into code. With other resource adapter/system provider it could be easier (HornetQ / ActiveMQ).

Application configuration

Now, let’s configure the application to connect MDB to the correct JMS queue. First, we will use the ejb-jar.xml file to define main objects.

Java EE Definition (ejb-jar.xml)

  <ejb-jar xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd" version="3.0">
    <description>MoM Application EJB / MDB </description>
    <display-name>MoM Application EJB / MDB</display-name>
    <enterprise-beans>
      <message-driven>
        <description>MoM MDB</description>
        <ejb-name>MessageDrivenBean</ejb-name>
        <ejb-class>net.a.g.jee.mom.mdb.MoMMDB</ejb-class>
        <transaction-type>Container</transaction-type>
        <message-destination-type>javax.jms.Queue</message-destination-type>
      </message-driven>
      <session>
        <ejb-name>MoMSenderBean</ejb-name>
        <ejb-class>net.a.g.jee.mom.ejb.MoMSenderBean</ejb-class>
      </session>
    </enterprise-beans>
  </ejb-jar>

Specific WMQ and JBoss implementation configuration (jboss-ejb3.xml)

As we want to connect MDB to a server WebSphereMQ, we have to configure several WMQ specific properties. We also map environment resources from the application server to the resources of the application.

<jboss:ejb-jar xmlns:jboss="http://www.jboss.com/xml/ns/javaee"
  xmlns="http://java.sun.com/xml/ns/javaee" xmlns:jee="http://java.sun.com/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mdb="urn:resource-adapter-binding"
  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" impl-version="2.0">
  <jee:enterprise-beans>
    <jee:message-driven>
      <jee:ejb-name>MessageDrivenBean</jee:ejb-name>
      <jee:ejb-class>net.a.g.jee.mom.mdb.MoMMDB</jee:ejb-class>
      <jee:transaction-type>Container</jee:transaction-type>
      <jee:message-destination-type>javax.jms.Queue</jee:message-destination-type>
      <jee:activation-config>
        <jee:activation-config-property>
          <jee:activation-config-property-name>channel</jee:activation-config-property-name>
          <jee:activation-config-property-value>${websphere.channel}</jee:activation-config-property-value>
        </jee:activation-config-property>
        <jee:activation-config-property>
          <jee:activation-config-property-name>queueManager</jee:activation-config-property-name>
          <jee:activation-config-property-value>${websphere.queueManager}</jee:activation-config-property-value>
        </jee:activation-config-property>
        <jee:activation-config-property>
          <jee:activation-config-property-name>transportType</jee:activation-config-property-name>
          <jee:activation-config-property-value>${websphere.transportType}</jee:activation-config-property-value>
        </jee:activation-config-property>
        <jee:activation-config-property>
          <jee:activation-config-property-name>hostName</jee:activation-config-property-name>
          <jee:activation-config-property-value>${websphere.hostName}</jee:activation-config-property-value>
        </jee:activation-config-property>
        <jee:activation-config-property>
          <jee:activation-config-property-name>port</jee:activation-config-property-name>
          <jee:activation-config-property-value>${websphere.port}</jee:activation-config-property-value>
        </jee:activation-config-property>
        <jee:activation-config-property>
          <jee:activation-config-property-name>username</jee:activation-config-property-name>
          <jee:activation-config-property-value>${websphere.username}</jee:activation-config-property-value>
        </jee:activation-config-property>
        <jee:activation-config-property>
          <jee:activation-config-property-name>password</jee:activation-config-property-name>
          <jee:activation-config-property-value>${websphere.password}</jee:activation-config-property-value>
        </jee:activation-config-property>
        <jee:activation-config-property>
          <jee:activation-config-property-name>destination</jee:activation-config-property-name>
          <jee:activation-config-property-value>java:/jboss/jms/wmq/queue/Queue</jee:activation-config-property-value>
        </jee:activation-config-property>
        <jee:activation-config-property>
          <jee:activation-config-property-name>useJNDI</jee:activation-config-property-name>
          <jee:activation-config-property-value>true</jee:activation-config-property-value>
        </jee:activation-config-property>
        <jee:activation-config-property>
          <jee:activation-config-property-name>destinationType</jee:activation-config-property-name>
          <jee:activation-config-property-value>javax.jms.Queue</jee:activation-config-property-value>
        </jee:activation-config-property>
      </jee:activation-config>
    </jee:message-driven>
    <jee:session>
      <jee:ejb-name>MoMSenderBean</jee:ejb-name>
      <jee:ejb-class>net.a.g.jee.mom.ejb.MoMSenderBean</jee:ejb-class>
      <jee:resource-ref>
        <jee:res-ref-name>jms/connectionFactory</jee:res-ref-name>
        <jee:res-type>javax.jms.ConnectionFactory</jee:res-type>
        <jee:lookup-name>java:/jboss/jms/wmq/connectionFactory</jee:lookup-name>
      </jee:resource-ref>
      <jee:resource-env-ref>
        <jee:resource-env-ref-name>jms/queue/Queue</jee:resource-env-ref-name>
        <jee:lookup-name>java:/jboss/jms/wmq/queue/Queue</jee:lookup-name>
      </jee:resource-env-ref>
    </jee:session>
  </jee:enterprise-beans>
  <jee:assembly-descriptor>
    <mdb:resource-adapter-binding>
      <jee:ejb-name>MessageDrivenBean</jee:ejb-name>
      <mdb:resource-adapter-name>${websphere.resource.adapter}</mdb:resource-adapter-name>
    </mdb:resource-adapter-binding>
  </jee:assembly-descriptor>
</jboss:ejb-jar>

Important information from this xml file :

  • Placeholder in the whole of file to externalize values inside JBoss standalone.xml file
    • hostName -> ${websphere.hostName}
    • port -> ${websphere.port}
    • queueManager -> ${websphere.queueManager}
  • useJNDI property is true to pickup the queue from JNDI context
  • JNDI mapping name : global scope and module scope
    • Queue name inside server setting : java:/jboss/jms/wmq/queue/Queue
    • Queue name inside application scope : jms/queue/Queue
  • Binding of WMQ ressource adapteur (JCA driver) with MDB bean.
    • <mdb:resource-adapter-binding> element

Warning

It is recommended to use XML files in a business context. Indeed, changing environment and frequent changes often requires some information modifications (clustering integration, authentication capability for example). Java code should depend as little as possible of the environment and external systems.

Info

If application needs to interact with another JMS provider (ActiveMQ or HornetQ), just copy/paste this file and change values with correct configuration. We can switch very easily from a provider to another.

JBoss configuration

In this chapter, we will setup JBoss EAP file a.k.a standalone.xml.

System properties

We set all WebSphereMQ connection environment settings we’ve seen in the jboss-ejb3.xml file.

<system-properties>
  <property name="websphere.hostName"         value="localhost"/>
  <property name="websphere.port"             value="1414"/>
  <property name="websphere.username"         value="mqm"/>
  <property name="websphere.password"         value="mqm"/>
  <property name="websphere.channel"          value="SYSTEM.AUTO.SVRCONN"/>
  <property name="websphere.transportType"    value="CLIENT"/>
  <property name="websphere.queueManager"     value="QUEUE.MANAGER"/>
  <property name="websphere.queueName"        value="Q_QUEUE"/>
  <property name="websphere.resource.adapter" value="wmq.jmsra.rar"/>
</system-properties>

Info

BINDING mode is the second transport type available to connect to WebsphereMQ, but operating-system binaries have to be installed.

Placeholders

JBoss will use system properties to replace placeholders with correct values inside several object type.

JBoss can manage placeholder replacements for :

  • Standard Java EE descriptor files.
    • spec-descriptor-property-replacement
    • web.xml, ejb-jar.xml, etc…
  • JBoss specific descriptor files.
    • jboss-descriptor-property-replacement
    • jboss-web.xml, jboss-ejb3.xml, etc…
  • Annotations
    • annotation-property-replacement

<subsystem xmlns="urn:jboss:domain:ee:1.2">
  <spec-descriptor-property-replacement>false</spec-descriptor-property-replacement>
  <jboss-descriptor-property-replacement>true</jboss-descriptor-property-replacement>
  <annotation-property-replacement>false</annotation-property-replacement>
</subsystem>

Note

I prefer use replacement only for JBoss specific descriptor files. Because another application server couldn’t manage placeholder replacements inside Java EE standard descriptor files.

WMQ connection and resource adapter configuration

Finally, let’s configure the WMQ resource adapter inside JBoss instance standalone.xml.


<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/jms/wmq/connectionFactory" use-java-context="true" pool-name="MQConnectionFactory">
          <config-property name="port">${websphere.port:1414}</config-property>
          <config-property name="hostName">${websphere.hostName:localhost}</config-property>
          <config-property name="username">${websphere.username:mqm}</config-property>
          <config-property name="password">${websphere.password:mqm}</config-property>
          <config-property name="channel">${websphere.channel:SYSTEM.AUTO.SVRCONN}</config-property>
          <config-property name="transportType">${websphere.transportType:CLIENT}</config-property>
          <config-property name="queueManager">${websphere.queueManager:QUEUE.MANAGER}</config-property>
          <security>
            <application/>
          </security>
        </connection-definition>
      </connection-definitions>
      <admin-objects>
        <admin-object class-name="com.ibm.mq.connector.outbound.MQQueueProxy" jndi-name="java:/jboss/jms/wmq/queue/Queue" use-java-context="true" pool-name="QueuePool">
          <config-property name="baseQueueManagerName">${websphere.queueManager:QUEUE.MANAGER}</config-property>
          <config-property name="baseQueueName">${websphere.queueName:Q_QUEUE}</config-property>
        </admin-object>
      </admin-objects>
    </resource-adapter>
  </resource-adapters>
</subsystem>

Info

You will notice we are still using placeholders into this part of JBoss configuration file.

Put the Websphere MQ resource adapter wmq.jmsra.rar and your application into JBoss instance deployment directory ${JBOSS_HOME}/standalone/deployments.

Et voilà ;-)

Conclusion

Integrating WebSphereMQ resource adapter is an easy operation on the JBoss application server. If you follow the general ideas in this post, it is possible to obtain a modular and easy application to maintain. Deploying your app in different environments (DEV , INT, PROD) is simplified, and only system properties in standalone.xml file should be changed.


URL

Thanks

  • Akram B. A. @RedHat for resources
  • Guillaume C. @RedHat for advices
  • Jean-Yves C. for corrections