Configuring WSO2 ESB and IBM WebSphere MQ using RefFSContextFactory

  • By Hasitha Abeykoon
  • 23 Feb, 2013

Introduction

WebSphere MQ is a Message Broker from IBM where as WSO2 ESB [1] is a lightweight and high performing enterprise service bus which is recognized for its performance, user friendliness and first in class support for enterprise application integration patterns. In this article we discuss how to connect these two separate worlds and implement useful messaging patterns.

Concept behind RefFSContextFactory

RefFSContextFactory uses a JNDI folder as the provider URL. That JNDI folder has .binding files which contains
the mapping between JNDI name and actual IBM Websphere MQ objects, such as a queue manager, queue, topic, and so forth. It basically
connects WSO2 ESB world and WebSphere MQ world.

WebSphere RefFSContextFactory overview

JMS Bindings and Client Transport

Configuring WebSphere MQ

Following are basic steps needed to get WebSphere MQ ready to accept messages from WSO2 ESB. The WebSphere installation
directory will be referred to as [MQ_HOME] from here onwards in this article.

  1. Changing JSAdmin.conf file

    1. Go to the /Java/bin directory and open the JSAdmin.config file with a text editor.
    2. Comment out the existing INITIAL_CONTEXT_FACTORY and add a INITIAL_CONTEXT_FACTORY named com.sun.jndi.fscontext.RefFSContextFactory
    3. Comment the default PROVIDER_URL and use a directory path instead. Make sure that directory is created in the file system (ex: C:/JNDI-Directory).

      Note: If exists, delete any earlier versions of the .bindings files from that folder.

      ex: After changing your JSAdmin.config file should look like as follows.

      #  appropriate one should be uncommented.
      #
      #INITIAL_CONTEXT_FACTORY=com.sun.jndi.ldap.LdapCtxFactory
      INITIAL_CONTEXT_FACTORY=com.sun.jndi.fscontext.RefFSContextFactory
      #INITIAL_CONTEXT_FACTORY=com.ibm.ejs.ns.jndi.CNInitialContextFactory
      #INITIAL_CONTEXT_FACTORY=com.ibm.websphere.naming.WsnInitialContextFactory
      #INITIAL_CONTEXT_FACTORY=com.ibm.websphere.naming.WMQInitialContextFactory
      #
      #  The following line specifies the URL of the service provider's initial
      #  context. It currently refers to an LDAP root context. Examples of a
      #  file system URL and WebSphere's JNDI namespace are also shown, commented
      #  out.
      #
      #PROVIDER_URL=ldap://polaris/o=ibm,c=us
      PROVIDER_URL=file:/C:/JNDI-Directory
      #PROVIDER_URL=iiop://localhost/
      #PROVIDER_URL=localhost:1414/SYSTEM.DEF.SVRCONN
      ......
                              
    4. Restart the IBM WebSphere service
  2. Creating a queue on WebSphere MQ

    WebSphere MQ Queue Creation

    WebSphere MQ Queue Creation




    1. Open the WebSphere MQ explorer and right click on Queue Manager and select New >Queue Manager.
    2. Provide a name for the Queue Manager ( myQueueManager) and Make it default. Press Next.
    3. Select the options to Start Queue Manager, Autostart Queue Manager and Create server connection channel. Then press on Next.
    4. Select the option to Create listener configuration for TCP/IP and provide a port number (1415).
    5. Once the Queue Manager is created, right click on the Queues icon belonging to the queue manager (myQueueManager) and select New->Local Queue.
    6. Provide a name for the Queue (myQueue) and press on Next. Go with the default configurations and press on Finish.
    7. Right click on the Channels icon under the Advanced icon and select New > Server-connection Channel. Provide a name for the channel (myChannel) and press on Next.
      Set the transmission protocol as TCP and press on Finish.
    8. Once these steps are completed, a listener will be created running on port 1415 and you should be able to view it by clicking on the listeners icon.
  3. Generating .bindings file

    1. RefFSContextFactory uses a file system url as the provider url. We are going to create a .bindings file inside C:/JNDI-Directory folder using JSAdmin.config file we prepared. Navigate
      to [MQ_HOME]/Java/bin directory and run JMSAdmin.bat file.
    2. Create bindings as appropriate. You have to use the names of resources you have defined. You can choose JNDI names as you want.
      //define a context factory. If in same machine you can use "define qcf(MYCF) //qmgr(CUSTOMER_QUEUE_MANAGER) transport(BIND)" as well.
      define qcf(MYCF) qmgr(CUSTOMER_QUEUE_MANAGER) host() port() CHANNEL(SYSTEM.DEF.SVRCONN) transport(CLIENT)
      //define a queue
      define q(JMS_QUEUE) qmgr(CUSTOMER_QUEUE_MANAGER) queue(MY_QUEUE)
                              

      Following is an example commands that goes with configs we have made under Creating a queue
      on WebSphere MQ
      . You can check created objects by display ctx command.

      Welcome to the Admin Tool's command-line interface
      
      InitCtx> def qcf(MQ_JMS_MANAGER) qmgr(myQueueManager) tran(client) chan(myChannel) host(175.157.120.102) port(1415)
      InitCtx> def q(JMS_QUEUE) qmgr(myQueueManager) queue(myQueue)
      InitCtx> display ctx
      
        Contents of InitCtx
      
            .bindings                 java.io.File
         a  JMS_QUEUE                 com.ibm.mq.jms.MQQueue
         a  MQ_JMS_MANAGER            com.ibm.mq.jms.MQQueueConnectionFactory
      
        3 Object(s)
          0 Context(s)
          3 Binding(s), 2 Administered
      
      InitCtx> end
      Stopping Websphere MQ classes for Java(tm) Message Service Administration
      C:\IBM\WebSphere MQ\Java\bin>
                              
    3. Now go to C:/JNDI-Directory . You should be able to view .bindings file there.

Configure WSO2 Enterprise Service Bus (Running on a Different Machine)

The following configuration changes are to be made on the ESB for it to be able to remotely
communicate with WebSphere MQ.
The ESB installation directory will be reffered to as [ESB_HOME].

  1. Prepare .Bindings File

    Copy C:/JNDI-Directory from above machine to a suitable place in new machine where WSO2 ESB is to run
    . We will consider this place in a Ubuntu machine as /home/user/JNDI-Directory. This binding file will
    be referred by ESB configs. Beauty of this method is, if you have this bindings file, you can access MQ queues from any machine in the network.

  2. Copying WebSphere MQ Libraries

    Copy all the jars found in the jar_files.zip attachment to the [ESB_HOME]/repository/ components/lib directory.
    In addition, you have to copy fscontext.jar file found in [MQ_HOME]/Java/lib directory as well.. All these jars can be found at
    [MQ_HOME]/Java/lib directory.

    • com.ibm.mq.jar
    • com.ibm.mqjms.jar
    • commonservices.jar
    • connector.jar
    • dhbcore.jar
    • mqcontext.jar
    • providerutil.jar
  3. Enable JMS Transport and Configure

    In order to route messages to WebSphere MQ, JMS Transports should be enabled. WSO2 ESB comes with two sets of configurations
    for this. Transport Receiver section should be added in order to receive messages from WebSphere MQ queues. Transport Sender section
    should be added in order to send messages to WebSphere MQ queues.

    Following is the Transport Receiver Configuration:

                     <transportReceiver name="jms" class="org.apache.axis2.transport.jms.JMSListener">
                            <parameter name="myQueueConnectionFactory" locked="false">
                            <parameter name="java.naming.factory.initial" locked = "false">com.sun.jndi.fscontext.RefFSContextFactory</parameter>
                            <parameter name="java.naming.provider.url" locked="false">file:/home/user/JNDI-Directory</parameter>
                            <parameter name="transport.jms.ConnectionFactoryJNDIName" locked="false">MQ_JMS_MANAGER</parameter>
                            <parameter name="transport.jms.ConnectionFactoryType" locked="false">queue</parameter>
                            <parameter name="transport.jms.Destination">JMS_QUEUE</parameter>
                            <parameter name="transport.jms.JMSSpecVersion">1.0</parameter>
                        </parameter>
    
                        <parameter name="default" locked="false">
                            <parameter name="java.naming.factory.initial" locked = "false">com.sun.jndi.fscontext.RefFSContextFactory</parameter>
                            <parameter name="java.naming.provider.url" locked="false">file:/home/user/JNDI-Directory</parameter>
                            <parameter name="transport.jms.ConnectionFactoryJNDIName" locked="false">MQ_JMS_MANAGER</parameter>
                            <parameter name="transport.jms.ConnectionFactoryType" locked="false">queue</parameter>
                            <parameter name="transport.jms.Destination">JMS_QUEUE</parameter>
                            <parameter name="transport.jms.JMSSpecVersion">1.0</parameter>
                        </parameter>
                     </transportReceiver>
                    

    Following is the Transport Sender Configuration:

                     <transportSender name="jms" class="org.apache.axis2.transport.jms.JMSSender">
                        <parameter name="myQueueConnectionFactory" locked="false">
                            <parameter name="java.naming.factory.initial" locked = "false">com.sun.jndi.fscontext.RefFSContextFactory</parameter>
                            <parameter name="java.naming.provider.url" locked="false">file:/home/user/JNDI-Directory</parameter>
                            <parameter name="transport.jms.ConnectionFactoryJNDIName" locked="false">MQ_JMS_MANAGER</parameter>
                            <parameter name="transport.jms.ConnectionFactoryType" locked="false">queue</parameter>
                            <parameter name="transport.jms.Destination">JMS_QUEUE</parameter>
                            <parameter name="transport.jms.JMSSpecVersion">1.0</parameter>
                        </parameter>
    
                        <parameter name="default" locked="false">
                            <parameter name="java.naming.factory.initial" locked = "false">com.sun.jndi.fscontext.RefFSContextFactory</parameter>
                            <parameter name="java.naming.provider.url" locked="false">file:/home/user/JNDI-Directory</parameter>
                            <parameter name="transport.jms.ConnectionFactoryJNDIName" locked="false">MQ_JMS_MANAGER</parameter>
                            <parameter name="transport.jms.ConnectionFactoryType" locked="false">queue</parameter>
                            <parameter name="transport.jms.Destination">JMS_QUEUE</parameter>
                            <parameter name="transport.jms.JMSSpecVersion">1.0</parameter>
                        </parameter>
                     </transportSender>
                    

    Navigate to [ESB_HOME]/repository/conf/axis2.xml file and add either Transport Receiver or Transport
    Sender or both depending on your need.

  4. Running ESB

    Start the ESB by providing the -Duser.name=SYSTEM jvm parameter. ex: ./wso2server.sh -Duser.name=SYSTEM
    (or for Windows OS wso2server.bat -Duser.name=SYSTEM)

    On a successful connection, an INFO level log message similar to the following is printed on the esb startup.

    INFO - JMSConnectionFactory JMS ConnectionFactory : myQueueConnectionFactory initialized

Test Message Receiving from Queue

  1. Copy paste the following proxy service to ESB synapse configuration. This will listen to messages coming from created queue at
    Websphere MQ (myQueue) then log it and drop the message. But in a real scenario, it can be
    sent to another queue, route to an endpoint looking at a parameter in message body, or deliver to a service pre-specified in headers.

                    <proxy name="SimpleStockQuoteService" transports="jms" startOnLoad="true">
                        <target>
                            <inSequence>
                                <property name="OUT_ONLY" value="true"/>
                                <log level="full"/>
                                <drop/>
                            </inSequence>
                            <outSequence/>
                        </target>
                        <parameter name="transport.jms.ContentType">
                            <rules>
                                <jmsProperty>contentType</jmsProperty>
                                <default>application/xml</default>
                            </rules>
                        </parameter>
                        <parameter name="transport.jms.ConnectionFactory">myQueueConnectionFactory</parameter>
                        <parameter name="transport.jms.DestinationType">queue</parameter>
                        <parameter name="transport.jms.Destination">JMS_QUEUE</parameter>
                    </proxy>
                    
  2. Right click queue in MQ explorer and put a test message to the queue (should be a xml). At that instance you will see that
    your message is consumed by the queue and logged at ESB running on remote machine.

Route Messages to WebSphere MQ Queue

  1. Copy paste the following proxy service to ESB synapse configuration. This proxy will route messages coming to the proxy
    to the WebSphere MQ queue (myQueue).
                   <proxy name="StockQuoteProxy"
                          transports="http"
                          startOnLoad="true"
                          trace="disable">
                      <description/>
                      <target>
                         <endpoint>
                            <address uri="jms:/JMS_QUEUE"/>
                         </endpoint>
                         <inSequence>
                            <log level="custom">
                               <property name="STATE" value="message is sent to queue"/>
                            </log>
                            <property name="OUT_ONLY" value="true"/>
                            <property name="FORCE_SC_ACCEPTED" value="true" scope="axis2"/>
                            <property name="Accept-Encoding" scope="transport" action="remove"/>
                            <property name="Content-Length" scope="transport" action="remove"/>
                            <property name="User-Agent" scope="transport" action="remove"/>
                            <property name="Content-Type" scope="transport" action="remove"/>
                         </inSequence>
                         <outSequence/>
                      </target>
                      <parameter name="transport.jms.ConnectionFactory">myQueueConnectionFactory</parameter>
                   </proxy>
                    

    The incoming HTTP message contains a bunch of HTTP headers that contain the ‘-‘ character. Some notable examples
    are ‘Content-length’ header and the ‘Transfer-encoding’ header. When ESB attempts to forward the message over
    JMS it sets the headers of the incoming message to the outgoing JMS message as JMS properties. According to the
    JMS spec the ‘-‘ character is prohibited in JMS property names. Some JMS brokers like ActiveMQ do not strictly check
    for this and hence they won’t complain. But certain other brokers like WebSphere MQ will throw exceptions. Hence we remove
    such message properties from incoming message.

  2. Send following SOAP message to the StockQuoteProxy above.
                    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://services.samples" xmlns:xsd="http://services.samples/xsd">
                       <soapenv:Header/>
                       <soapenv:Body>
                          <ser:getQuote>
                             <!--Optional:-->
                             <ser:request>
                                <!--Optional:-->
                                <xsd:symbol>IBM</xsd:symbol>
                             </ser:request>
                          </ser:getQuote>
                       </soapenv:Body>
                    </soapenv:Envelope>
                    
  3. You will notice Current Queue Depth increased by 1, which means message has been put to the queue.

Connecting Mechanisms Other than RefFSContextFactory

WSO2 ESB can be configured to communicate with WebSphere MQ using WMQInitialContextFactory as well. Following are the only deviation points
in the process. There is no need of creating .bindings file as above when using WMQInitialContextFactory

  • In JSAdmin.config file Comment out the existing INITIAL_CONTEXT_FACTORY and add a INITIAL_CONTEXT_FACTORY named com.ibm.mq.jms.context.WMQInitialContextFactory.

    ex: INITIAL_CONTEXT_FACTORY=com.ibm.mq.jms.context.WMQInitialContextFactory
  • Comment the default PROVIDER_URL and use the following instead, and restart the IBM WebSphere service for the changes to take effect
    PROVIDER_URL=localhost:1415/SYSTEM.DEF.SVRCONN
  • Create the queue on WebSphere MQ in the same way.
  • Axis2 configuration of ESB should change like following:
    • transportReceiver configuration:
                              <transportReceiver name="jms" class="org.apache.axis2.transport.jms.JMSListener">
                                <parameter name="myQueueConnectionFactory" locked="false">
                                  <parameter name="java.naming.factory.initial" locked="false">com.ibm.mq.jms.context.WMQInitialContextFactory</parameter>
                                  <parameter name="java.naming.provider.url" locked="false">{MQ_SERVER_IP}:{PORT}/{CHANNEL_NAME}</parameter>
                                  <parameter name="transport.jms.ConnectionFactoryJNDIName" locked="false">{QUEUE_MANAGER_NAME}</parameter>
                                  <parameter name="transport.jms.ConnectionFactoryType" locked="false">queue</parameter>
                                  <parameter name="transport.jms.Destination">{QUEUE_NAME}</parameter>
                                  <parameter name="transport.jms.JMSSpecVersion">1.0</parameter>
                                </parameter>
                                <parameter name="default" locked="false">
                                  <parameter name="java.naming.factory.initial" locked="false">com.ibm.mq.jms.context.WMQInitialContextFactory</parameter>
                                  <parameter name="java.naming.provider.url" locked="false">{MQ_SERVER_IP}:{PORT}/{CHANNEL_NAME}</parameter>
                                      <parameter name="transport.jms.ConnectionFactoryJNDIName" locked="false">{QUEUE_MANAGER_NAME}</parameter>
                                      <parameter name="transport.jms.ConnectionFactoryType" locked="false">queue</parameter>
                                      <parameter name="transport.jms.Destination">{QUEUE_NAME}</parameter>
                                      <parameter name="transport.jms.JMSSpecVersion">1.0</parameter>
                                </parameter>
                              </transportReceiver>
                              
    • transportSender Configuration:
                              <transportSender name="jms" class="org.apache.axis2.transport.jms.JMSSender">
                                <parameter name="myQueueConnectionFactory" locked="false">
                                  <parameter name="java.naming.factory.initial" locked="false">com.ibm.mq.jms.context.WMQInitialContextFactory</parameter>
                                  <parameter name="java.naming.provider.url" locked="false">{MQ_SERVER_IP}:{PORT}/{CHANNEL_NAME}</parameter>
                                  <parameter name="transport.jms.ConnectionFactoryJNDIName" locked="false">{QUEUE_MANAGER_NAME}</parameter>
                                  <parameter name="transport.jms.ConnectionFactoryType" locked="false">queue</parameter>
                                  <parameter name="transport.jms.Destination">{QUEUE_NAME}</parameter>
                                  <parameter name="transport.jms.JMSSpecVersion">1.0</parameter>
                                </parameter>
                                <parameter name="default" locked="false">
                                  <parameter name="java.naming.factory.initial" locked="false">com.ibm.mq.jms.context.WMQInitialContextFactory</parameter>
                                  <parameter name="java.naming.provider.url" locked="false">{MQ_SERVER_IP}:{PORT}/{CHANNEL_NAME}</parameter>
                                      <parameter name="transport.jms.ConnectionFactoryJNDIName" locked="false">{QUEUE_MANAGER_NAME}</parameter>
                                      <parameter name="transport.jms.ConnectionFactoryType" locked="false">queue</parameter>
                                      <parameter name="transport.jms.Destination">{QUEUE_NAME}</parameter>
                                      <parameter name="transport.jms.JMSSpecVersion">1.0</parameter>
                                </parameter>
                              </transportSender>
                              
    • Note to change the values of the MQ_SERVER_IP, PORT, CHANNEL_NAME, QUEUE_MANAGER_NAME and QUEUE_NAME accordingly.
      Specific to above discussed configuration:
              MQ_SERVER_IP:175.157.120.102
              PORT:1415
              CHANNEL_NAME:myChannel
              QUEUE_MANAGER_NAME:myQueueManager
              QUEUE_NAME:myQueue
                              
  • Following jars should be copied to [ESB_HOME]/repository/components/lib directory from [MQ_HOME]/java/lib directory:

    com.ibm.mq.jar, com.ibm.mqjms.jar, commonservices.jar, connector.jar, dhbcore.jar, mqcontext.jar, providerutil.jar
  • Message receiving and message routing to the queue should work without any change.

Conclusion

The WSO2 ESB and IBM WebSphere MQ can easily be configured together to implement useful messaging patterns such as different message traffic patterns, handling fail over scenarios, priority mediation of messages, throttling message flows and reliable message delivery. Using RefFSContextFactory is a simple and clean way to do it.
In this article we discussed how to route messages to different queues of WebSphere MQ, and also how to configure ESB to listen to queues. These configurations are important when
implementing robust messaging with recover-ability of messages in some failure during transactions.

References

  1. WSO2 Enterprise Service Bus