WSO2 ESB by Example - J2EE Connector Architecture(JCA) and ESB
- Rajika Kumarasiri
- Senior Software Engineer - WSO2
Applies To
WSO2 ESB | 4.0.3 |
JBoss | 5.1.0GA |
IBM MQ | 6.0.2(Fix pack 6.0.2.1 applied) |
JRE | 1.6_25(32 bit) |
Microsoft Windows | XP |
Document notations
$ESB_HOME | Location where WSO2 ESB is extracted |
$ESB_REPO | Location where WSO2 ESB repository is copied |
$JBOSS_HOME | The location JBoss application server is installed |
$MQ_HOME | The installation directory of WebShpere MQ |
Table of Content
- Applies To
- Table of Content
- Introduction
- Configuring WSO2 ESB to use a JCA adapter
- Conclusion
- Future work
- References
- Resources
Introduction
J2EE Connector Architecture(JCA) adapters are used in conjunction application servers in order to connect to different legacy systems. JCA adapters normally handle the underline connections, transactions and the security resources providing users with an easy to use API. JCA adapters plays a major role when integrating legacy systems into other enterprise applications.
This guide describes how to consume JMS objects defined in WebSphere(MQ) via the WebSphere MQ JCA adapter within WSO2 ESB. A mediator has been developed which shows how to look up the JMS objects via JNDI. Any interested users who wish to use a different type of JCA adapter should be able to write a similar mediator to access the resources exposed via the JCA adapter through JNDI.
The first few sections describes how to define JMS objects on WebSphere MQ and then configure the JCA resources and finally how to consume those resources within WSO2 ESB.
Configuring WSO2 ESB to use a JCA adapter
Note that the below instructions were tested on Microsoft Windows XP as given in the Applies To section. Same instruction should be easily adapted to other platforms as well.
Pre-requites
- Download and install the latest fix pack for your WebSphere MQ copy. The fix pack can be obtained from official IBM website.
- Need Apache Ant installed on the system to run the sample.
Configuring JMS objects on WebShpere MQ
1. Start IBM WebSphere MQ Explore and crate a new queue manager. Check the option to make it the default queue manager and leave the rest default. The example uses 'mymgr' as the queue manager name. Refer to Figure1 below.
Figure1: Defining a queue manager
2. Go to the created queue manager and expand the list and create a new local queue. Refer to Figure2 below.
Figure2: Defining local queue
3. Expand 'Advanced' sub tree and add a 'server-connection' channel. Refer to Figure3 below.
Figure3: Adding server-connection channel
4. Go to $MQ_HOME/java/bin and invoke the IVTRun application to check the status of the queue manager. You should see something to similar below.
C:\Program Files (x86)\IBM\WebSphere MQ\Java\bin>IVTRun.bat -nojndi 5724-H72, 5655-L82, 5724-L26 (c) Copyright IBM Corp. 2002,2005. All Rights Reserved. Websphere MQ classes for Java(tm) Message Service 6.0 Installation Verification Test Creating a QueueConnectionFactory Creating a Connection Creating a Session Creating a Queue Creating a QueueSender Creating a QueueReceiver Creating a TextMessage Sending the message to SYSTEM.DEFAULT.LOCAL.QUEUE Reading the message back again Got message: JMS Message class: jms_text JMSType: null JMSDeliveryMode: 2 JMSExpiration: 0 JMSPriority: 4 JMSMessageID: ID:414d51206d796d6772202020202020208ad1a24f20000802 JMSTimestamp: 1336031322783 JMSCorrelationID:null JMSDestination: queue:///SYSTEM.DEFAULT.LOCAL.QUEUE JMSReplyTo: null JMSRedelivered: false JMS_IBM_PutDate:20120503 JMSXAppID:ava\jdk1.6.0_25\bin\java.exe JMS_IBM_Format:MQSTR JMS_IBM_PutApplType:11 JMS_IBM_MsgType:8 JMSXUserID:Administrato JMS_IBM_PutTime:07484278 JMSXDeliveryCount:1 A simple text message from the MQJMSIVT program Reply string equals original string Closing QueueReceiver Closing QueueSender Closing Session Closing Connection IVT completed OK IVT finished
5. Now, execute the same application with the configured parameters. Ensure that you pass the correct parameters which suit your environment. If successful, something similar to the following should be received.
C:\Program Files (x86)\IBM\WebSphere MQ\Java\bin>IVTRun.bat -nojndi -client -m mymgr -host localhost -channel mychannel 5724-H72, 5655-L82, 5724-L26 (c) Copyright IBM Corp. 2002,2005. All Rights Reserved. Websphere MQ classes for Java(tm) Message Service 6.0 Installation Verification Test Creating a QueueConnectionFactory Creating a Connection Creating a Session Creating a Queue Creating a QueueSender Creating a QueueReceiver Creating a TextMessage Sending the message to SYSTEM.DEFAULT.LOCAL.QUEUE Reading the message back again Got message: JMS Message class: jms_text JMSType: null JMSDeliveryMode: 2 JMSExpiration: 0 JMSPriority: 4 JMSMessageID: ID:414d51206d796d6772202020202020208ad1a24f20000902 JMSTimestamp: 1336032465862 JMSCorrelationID:null JMSDestination: queue:///SYSTEM.DEFAULT.LOCAL.QUEUE JMSReplyTo: null JMSRedelivered: false JMS_IBM_PutDate:20120503 JMSXAppID:WebSphere MQ Client for Java JMS_IBM_Format:MQSTR JMS_IBM_PutApplType:28 JMS_IBM_MsgType:8 JMSXUserID:MUSR_MQADMIN JMS_IBM_PutTime:08074586 JMSXDeliveryCount:1 A simple text message from the MQJMSIVT program Reply string equals original string Closing QueueReceiver Closing QueueSender Closing Session Closing Connection IVT completed OK IVT finished
6. Go to $MQ_HOME/java/bin and open the JMS client configuration file JMSAdmin.config and set the following two parameters. The provider url must be point to an empty folder. This folder contains the JNDI meta data.
INITIAL_CONTEXT_FACTORY=com.sun.jndi.fscontext.RefFSContextFactory PROVIDER_UR=file:/C:/Documents and Settings/Administrator/Desktop/ibm-mq-jndi
7. Run IVTSetup script(IVTSetup.bat on Windows) to create the default JNDI bindings. If successful, you will see something similar to the following.
C:\Program Files (x86)\IBM\WebSphere MQ\Java\bin>IVTSetup.bat + Creating script for object creation within JMSAdmin + Calling JMSAdmin in batch mode to create objects 5724-H72, 5655-L82, 5724-L26 (c) Copyright IBM Corp. 2002,2005. All Rights Reserved. Starting Websphere MQ classes for Java(tm) Message Service Administration InitCtx> Unable to bind object InitCtx> Unable to bind object InitCtx> Unable to bind object InitCtx> Unable to bind object InitCtx> Stopping Websphere MQ classes for Java(tm) Message Service Administration + Administration done; tidying up files + Done!
8. Run IVTRun script (IVTRun.bat) to create the default bindings. Provide the JNDI location given in the previous step. If successful, you will see something similar to the following.
C:\Program Files (x86)\IBM\WebSphere MQ\Java\bin>IVTRun.bat -url "file:/C:/Documents and Settings/Administrator/Desktop/ibm-mq-jndi" -icf com.sun.jndi.fscontext.RefFSContextFactory 5724-H72, 5655-L82, 5724-L26 (c) Copyright IBM Corp. 2002,2005. All Rights Reserved. Websphere MQ classes for Java(tm) Message Service 6.0 Installation Verification Test Using administered objects, please ensure that these are available Retrieving a QueueConnectionFactory from JNDI Creating a Connection Creating a Session Retrieving a Queue from JNDI Creating a QueueSender Creating a QueueReceiver Creating a TextMessage Sending the message to SYSTEM.DEFAULT.LOCAL.QUEUE Reading the message back again Got message: JMS Message class: jms_text JMSType: null JMSDeliveryMode: 2 JMSExpiration: 0 JMSPriority: 4 JMSMessageID: ID:414d51206d796d6772202020202020208ad1a24f20000a02 JMSTimestamp: 1336032569705 JMSCorrelationID:null JMSDestination: queue:///SYSTEM.DEFAULT.LOCAL.QUEUE JMSReplyTo: null JMSRedelivered: false JMS_IBM_PutDate:20120503 JMSXAppID:WebSphere MQ Client for Java JMS_IBM_Format:MQSTR JMS_IBM_PutApplType:28 JMS_IBM_MsgType:8 JMSXUserID:MUSR_MQADMIN JMS_IBM_PutTime:08092970 JMSXDeliveryCount:1 A simple text message from the MQJMSIVT program Reply string equals original string Closing QueueReceiver Closing QueueSender Closing Session Closing Connection IVT completed OK IVT finished
9. Start JMSAdmin script(on Windows JMSAdmin.bat) at $MQ_HOME/java/bin (on Windows C:/Program Files/IBM/WebSphere MQ/java/bin) and make some modifications to the created bindings. Provide the correct queue manager name that you defined.
ALTER QCF(ivtQCF) TRANSPORT(CLIENT) ALTER QCF(ivtQCF) QMGR(mymgr)
10. There is a couple of services distributed with WSO2 ESB by default. By enabling JMS transport, these services also start to listen on JMS queues. Let's define a single queue for these services and call it as the bogusq. Start the JMSAdmin script (on Windows JMSAdmin.bat) and execute the following two commands.
DEFINE Q(bogusq) QMGR(mymgr) ALTER Q(bogusq) QUEUE(localq)
11. Now we have created a QueueConnectionFactory with the name 'ivtQCF', two queues with the names 'ivtQ' and 'bogusq'. You can display the same information by typing 'display ctx' at the JMSAdmin console. This guide uses these QueueConnectionFactory and queues in the following sections.
5724-H72, 5655-L82, 5724-L26 (c) Copyright IBM Corp. 2002,2005. All Rights Reserved. Starting Websphere MQ classes for Java(tm) Message Service Administration InitCtx> display ctx Contents of InitCtx .bindings java.io.File a ivtQ com.ibm.mq.jms.MQQueue a ivtTCF com.ibm.mq.jms.MQTopicConnectionFactory a ivtQCF com.ibm.mq.jms.MQQueueConnectionFactory a ivtT com.ibm.mq.jms.MQTopic a bogusq com.ibm.mq.jms.MQQueue 6 Object(s) 0 Context(s) 6 Binding(s), 5 Administered
Deploying the JCA adapter into JBoss AS
Next we need to deploy the WebSphere MQ JCA adapter into JBoss. Copy the JCA adapter wmq.jmsra.rar in($MQ_HOME/Java/lib/jca) into the JBoss server's deploy folder (JBOSS_HOME/server/default/deploy). If the adapter is registered successfully, a log similar to the following can be seen on the JBoss console log.
02:53:05,971 INFO [RARDeployment] Required license terms exist, view vfszip:/C:/Documents and Settings/Administrator/Desktop/jca/jboss-5.1.0.GA/server/default/deploy/wmq.jmsra.rar/ META-INF/ra.xml
Configuring JCA resources
The required JCA resources need to be configured in the JCA adapter, such as QueueConnectionFactory and the two queues defined above. They can be defined in wmq-jca-ds.xml, which needs to be copied to $JBOSS_HOME/server/default/deploy/. The content of wmq-jca-ds.xml is given below. There are wide variety of configuration options that is available for WebSphere MQ JCA adapter and the documentation of the adapter is recommended[1].<?xml version="1.0" encoding="UTF-8"?> <connection-factories> <mbean code="org.jboss.resource.deployment.AdminObject" name="jca.wmq:name=ivtQ"> <attribute name="JNDIName">ivtQ</attribute> <depends optional-attribute-name="RARName"> jboss.jca:service=RARDeployment,name='wmq.jmsra.rar' </depends> <attribute name="Type">javax.jms.Queue</attribute> <attribute name="Properties"> baseQueueManagerName=mymgr baseQueueName=SYSTEM.DEFAULT.LOCAL.QUEUE </attribute> </mbean> <mbean code="org.jboss.resource.deployment.AdminObject" name="jca.wmq:name=bogusq"> <attribute name="JNDIName">bogusq</attribute> <depends optional-attribute-name="RARName"> jboss.jca:service=RARDeployment,name='wmq.jmsra.rar' </depends> <attribute name="Type">javax.jms.Queue</attribute> <attribute name="Properties"> baseQueueManagerName=mymgr baseQueueName=SYSTEM.DEFAULT.LOCAL.QUEUE </attribute> </mbean> <tx-connection-factory> <jndi-name>ivtQCF</jndi-name> <rar-name>wmq.jmsra.rar</rar-name> <use-java-context>false</use-java-context> <connection-definition>javax.jms.QueueConnectionFactory</connection-definition> <min-pool-size>1</min-pool-size> <max-pool-size>10</max-pool-size> <blocking-timeout-millis>30000</blocking-timeout-millis> <idle-timeout-minutes>30</idle-timeout-minutes> <prefill>false</prefill> <background-validation>false</background-validation> <background-validation-millis>0</background-validation-millis> <validate-on-match>true</validate-on-match> <statistics-formatter>org.jboss.resource.statistic.pool. JBossDefaultSubPoolStatisticFormatter</statistics-formatter> <isSameRM-override-value>false</isSameRM-override-value> <allocation-retry>0</allocation-retry> <allocation-retry-wait-millis>5000</allocation-retry-wait-millis> <config-property type="java.lang.String" name="channel">SYSTEM.DEF.SVRCONN </config-property> <config-property type="java.lang.String" name="hostName">localhost </config-property> <config-property type="java.lang.String" name="port">1414</config-property> <config-property type="java.lang.String" name="queueManager">mymgr </config-property> <config-property type="java.lang.String" name="transportType">BINDINGS </config-property> <security-domain-and-application>JmsXARealm</security-domain-and-application> </tx-connection-factory> </connection-factories>
Class mediator for consume resources via JNDI
Following describes the source of the class mediator. It basically look up the previously defined JMS objects(such as ConnectionFactory and queues) to produce and consume messages into a queue defined in WebSphere MQ via the JCA adapter. This is the basic way to use any JCA resource adapter with WSO2 ESB. Users need to write a mediator which looks up the resources via JNDI. The mediator first lookup a queue(ivtQ) and send a message to that queue then later lookup the same queue and consume the message in that queue. The source is attached at the end of the article with the build file.
/* * Copyright WSO2, Inc. (https://wso2.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.wso2.carbon.mediator.jca.ibm.mq; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.synapse.MessageContext; import org.apache.synapse.mediators.AbstractMediator; import javax.jms.*; import javax.naming.InitialContext; /** * This is a mediator which let users to consume JMS objects access via JNDI * @author rajika kumarasiri - [email protected] */ public class IBMMQJCAAdapterMediator extends AbstractMediator { private static final Log log = LogFactory.getLog(IBMMQJCAAdapterMediator.class); private String connectionFactoryType; private String connectionFactoryName; private String queueName; private String action; public static final String QUEUE_CONNECTION_FACTORY_TYPE = "queue"; public boolean mediate(MessageContext messageContext) { try { if (connectionFactoryName == null) { log.warn("No value found for the ConnectionFactory parameter. Default value 'ivtQCF'" + " will be used."); connectionFactoryName = "ivtQCF"; } if (queueName == null) { log.warn("No value found for the Queue name parameter. Default value 'ivtQ'" + " will be used."); queueName = "ivtQ"; } if (action == null) { log.warn("No value found for the is action parameter. Default value" + "'consume' will be used."); action = "consume"; } if (connectionFactoryType == null) { log.warn("No value found for connection factory type. QueueConnection Factory type" + " will be used by default"); connectionFactoryType = QUEUE_CONNECTION_FACTORY_TYPE; } log.info("Using the JMS objects..\n" + "ConnectionFactoryType - '" + connectionFactoryType + "'.\n" + "ConnectionFactoryName - '" + connectionFactoryName + "'." + "\nQueueName - '" + queueName + "'.\nProduce ? - " + action + "."); InitialContext ctx = new InitialContext(); if (QUEUE_CONNECTION_FACTORY_TYPE.equals(connectionFactoryType)) { QueueConnectionFactory cfac = (QueueConnectionFactory) ctx.lookup(connectionFactoryName); if (cfac == null) { log.error("NULL connection factory definition was received for the connection " + "factory name '" + connectionFactoryName + "'"); } else { QueueConnection connection = cfac.createQueueConnection(); if (connection != null) { connection.start(); Queue destination = (Queue) ctx.lookup(queueName); QueueSession session = connection.createQueueSession(false, QueueSession.AUTO_ACKNOWLEDGE); log.info("Sending message to destination : '" + queueName + "'"); // first create a sender to produce the message on to the queue QueueSender sender = session.createSender(destination); QueueReceiver receiver = session.createReceiver(destination); sender.send(session.createTextMessage( messageContext.getEnvelope().toString())); log.info("Waiting for messages from destination '" + queueName + "'.."); Message msg = receiver.receive(); if (msg != null && msg instanceof TextMessage) { log.info("Message received : " + ((TextMessage) msg).getText()); } else { log.info("No text messages were received!"); } // close the JMS objects to avoid resource leaking connection.close(); session.close(); sender.close(); } else { log.error("No connection to the broker"); } } } else { // logic for topic connection factory } } catch (Exception e) { log.error("Cloud not mediate the message. " + e.getMessage(), e); } return true; } public String getConnectionFactoryType() { return connectionFactoryType; } public void setConnectionFactoryType(String connectionFactoryType) { this.connectionFactoryType = connectionFactoryType; } public String getConnectionFactoryName() { return connectionFactoryName; } public void setConnectionFactoryName(String connectionFactoryName) { this.connectionFactoryName = connectionFactoryName; } public String getQueueName() { return queueName; } public void setQueueName(String queueName) { this.queueName = queueName; } public String getAction() { return action; } public void setAction(String action) { this.action = action; } }
Deploying WSO2 ESB as a web app
WSO2 ESB has to deploy as a webapp in JBoss application in order to access the resources via the JCA adapter. Following section describes how to deploy WSO2 ESB together with the mediator as a webapp in JBoss.
1. Extract the WSO2 ESB distribution into a folder. Copy $ESB_HOME/repository into a folder called wso2esb(this location will be referred as $ESB_REPO in this guide) and it is assumed that $ESB_REPO=C:/wso2esb in the rest of the article.
2. Create a new folder named wso2esb.war in the $JBOSS_HOME/server/default/deploy directory.
3. Copy the WEB-INF folder located in $ESB_HOME/lib/core/WEB-INF to $JBOSS_HOME/server/default/deploy/wso2esb.war.
4. Copy the jars in ESB_HOME/lib/api/*.jar except geronimo-jms_1.1_spec-1.1.0.wso2v1.jar( see the trouble shooting section)$JBOSS_HOME/server/default/deploy/wso2esb.war/WEB-INF/lib.
5. Open file $JBOSS_HOME/server/default/deploy/jbossweb.sar/server.xml and paste the following entry inside service element(edit the keystoreFile and keystorePass in this entry according to your environment).
<Connector address='${jboss.bind.address}' SSLEnabled='true' keystoreFile='REPO_HOME\repository\resources\security\wso2carbon.jks' keystorePass='wso2carbon' maxSpareThreads='76' port='8443' protocol='HTTP/1.1' maxThreads='150' secure='true' scheme='https' sslProtocol='TLS' clientAuth='false'/>
6. Open file carbon.xml, which can be found in $ESB_REPO/repository/conf directory and change the ServerURL element as https://localhost:8443/wso2esb/services/
7. Edit the context as '<WebContextRoot>/wso2esb </WebContextRoot> ' and save the content.
8. Open registry.xml, which can be found in $ESB_REPO/repository/conf directory and edit the DB URL.
'jdbc:h2:C:/wso2esb/repository/database/WSO2CARBON_DB;DB_CLOSE_ON_EXIT=FALSE' and save the changes.
9. Open user-mgt.xml, which can be found in $ESB_REPO/repository/conf directory and edit the DB URL.
'jdbc:h2:C:/wso2esb/repository/database/WSO2CARBON_DB;DB_CLOSE_ON_EXIT=FALSE' and save the changes.
10. Open axis2.xml which can be found in the ESB_REPO/repository/conf directory and edit the keystore locations, trust store locations(under the parameter keystore and truststore) in https transport receiver and sender pair.
<parameter name="keystore" locked="false"> <KeyStore> <Location>ESB_REPO/repository/resources/security/wso2carbon.jks</Location> <Type>JKS</Type> <Password>wso2carbon</Password> <KeyPassword>wso2carbon</KeyPassword> </KeyStore> </parameter> <parameter name="truststore" locked="false"> <TrustStore> <Location>ESB_REPO/repository/resources/security/client-truststore.jks</Location> <Type>JKS</Type> <Password>wso2carbon</Password> </TrustStore> </parameter>
11. Specify the syanspe-config folder location in the axis2.xml ESB_HOME/repository/conf directory and edit the parameter 'SynapseConfig.ConfigurationFile' to point to the location of the synapse-config folder location. Note that the normal folder structure of a traditional ESB configuration should be available under a folder called 'default' in this file path.
<parameter name="SynapseConfig.ConfigurationFile" locked="false">ESB_REPO/repository/deployment/server/synapse-configs/ </parameter>
Configuring the mediator
1. Drop the mediator jar into the location $ESB_REPO\components\lib.
2. Drop the main.xml main sequence configuration(that is in the sample folder) into $ESB_REPO/repository/deployment/server/synapse-config/default/sequence. This sequence uses the mediator which looks up the resources via the JCA adapter.
Using the mediator to consume the resources via JNDI
1. Before starting JBoss server we need to set the CARBON_HOME environment variable to point to the $ESB_REPO location defined above.
2. Go to $ESB_HOME/samples/axis2Client and type 'ant stockquote -Dtrpurl=https://localhost:8280/' to invoke the main sequence.
Trouble shooting
This section describes couple of common problems that can be received by the users.
1.Receiving javax.naming.NameNotFoundException. Example :
javax.naming.NameNotFoundException: ivtQCF not bound while looking up the ConnectionFactory ivtQCF.
This is probably because registration of JMS objects hasn't happened properly(via wmq-jca-ds.xml). Make sure
relevant configurations are available in wmq-jca-ds.xml. Check the JNDI
tree via the JBoss management console to make sure that the Connection Factory definition has registered successfully.
2. Receiving java.lang.ClassCastException or similar exceptions. Example :
java.lang.ClassCastException: com.ibm.mq.connector.outbound.ConnectionFactoryImpl cannot be cast to javax.jms.ConnectionFactory
This can occur due to two reasons.
(i). The geronimo-jms_1.1_spec-1.1.0.wso2v1.jar that distributes with WSO2 ESB
(in ESB_HOME/lib/api) is in the WSO2 ESB class path deployed as a web app.
Just remove the jar and restart JBoss AS.
(ii). Make sure that you have defined correct connection factory definitions in wmq-jca-ds.xml under the property
connection-definition
Example:
<connection-definition>javax.jms.ConnectionFactory </connection-definition>instead
of
<connection-definition>javax.jms.QueueConnectionFactory </connection-definition>
3. Receiving java.lang.UnsatisfiedLinkError. Example : {15:28:01,953 WARN [JBossManagedConnectionPool] Throwable while attempting to get a new connection: null
java.lang.UnsatisfiedLinkError: C:/Program Files (x86)/IBM/WebSphere MQ/Java/lib/mqjbnd05.dll: Can't load IA 32-bit .dll on a AMD 64-bit platform}
. Make sure you runs on 32 bit version of JRE.
Conclusion
This article describes how to consume a resource via JNDI that is exposed using a JCA adapter. It also covers a sample which describes how to consume resources via JNDI and also provides the required configurations.
Future work
The given approach required users to deploy WSO2 ESB as a web app in the application server. A more suitable way to provide the JCA adapter hosting facility within WSO2 Carbon environment itself. This can be achieved by integrating a light weight JCA component into WSO2 Carbon.
References:
- https://publib.boulder.ibm.com/infocenter/wmqv6/v6r0/index.jsp?topic=/com.ibm.mq.csqzaw.doc/uj40010_.htm
- https://www.ibm.com/developerworks/websphere/library/techarticles/0710_ritchie/0710_ritchie.html
Resources
- Sample Task and the configurations : jca-config.zip
Author
Rajika Kumarasiri, Senior Software Engineer, [email protected]