The best I can get is described below, illustrated as the context of the Spring XML application, which itself contains the CAMEL context and routes. This example works with the native MQ JCA resource adapter for IBM v7.5, CAMEL 2.16, Spring core 4.2. I deployed it to Glassfish, Weblogic, and JBoss EAP7 servers.
The difficulty lies in processing the MQ report stream, whose philosophy is contrary to the idea of a simple JMS response message. For a detailed explanation, see Deploying your own websphere MQ with CoD on top of the Camel JMS component
This example, based on the CAMEL XML DSL, is self-contained and easy to test.
Let's start with the Spring and CAMEL declarations:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util" xmlns:context="http://www.springframework.org/schema/context" xmlns:camel="http://camel.apache.org/schema/spring" xmlns:jee="http://www.springframework.org/schema/jee" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd">
The CAMEL context follows with two routes: MQ - JMS and JMS - MQ, here is a chain to create a bridge to facilitate testing.
<camel:camelContext id="mqBridgeCtxt"> <camel:route id="mq2jms" autoStartup="true">
Strange: on Weblogic, the only way to get (for example) 3 listeners is to use 3 connections (with 3 camels: from sequences) with a maximum of 1 session each, otherwise error MQ occurs: MQJCA1018: only one session per connection is allowed. On JBoss, you can simply configure concurrentConsumers = ...
<camel:from uri="wmq:queue:TEST.Q1?concurrentConsumers=1&disableReplyTo=true& acknowledgementModeName=SESSION_TRANSACTED"/>
The disable parameter disableReplyTo above ensures that CAMEL does not respond before we can check the MQ message type to 1 = Request (-reply) or 8 = datagram (one way!). This test and answer construct is not illustrated here.
Then we apply EIP to InOnly the next time we host it on a simple JMS so that it matches the Inbound MQ mode.
<camel:setExchangePattern pattern="InOnly"/> <camel:to uri="ref:innerQueue" /> </camel:route>
This completes the MQ-to-jms route; the following is the jms-to-MQ route, still in the same CAMEL context:
<camel:route id="jms2mq" autoStartup="true"> <camel:from uri="ref:innerQueue" /> <camel:removeHeaders pattern="*"/> <camel:removeProperties pattern="*" />
A request flag now appears for the MQ CoD report, which should be sent by the remote recipient. We also apply the MQ message to the datagram type (value 8).
<camel:setHeader headerName="JMS_IBM_Report_COD"><camel:simple resultType="java.lang.Integer">2048</camel:simple></camel:setHeader> <camel:setHeader headerName="JMS_IBM_Report_Pass_Correl_ID"><camel:simple resultType="java.lang.Integer">64</camel:simple></camel:setHeader> <camel:setHeader headerName="JMS_IBM_MsgType"><camel:simple resultType="java.lang.Integer">8</camel:simple></camel:setHeader>
The ReplyTo queue can be specified either through the ReplyTo uri parameter, or as a header, as shown below.
Next, we use the CamelJmsDestinationName header to force the suppression of the JQ MQ MQRFH2 JQ message header (using the targetClient MQ URL parameter value). In other words, we want to send a normal binary message with vanilla MQ (i.e., only the MQMD message descriptor followed by the payload).
<camel:setHeader headerName="JMSReplyTo"><camel:constant>TEST.REPLYTOQ</camel:constant></camel:setHeader> <camel:setHeader headerName="CamelJmsDestinationName"> <camel:constant>queue://MYQMGR/TEST.Q2?targetClient=1</camel:constant></camel:setHeader>
Additional MQMD fields can be controlled using the reserved JMS properties, as shown below. See Limitations in an IBM document.
<camel:setHeader headerName="JMS_IBM_Format"><camel:constant>MQSTR </camel:constant></camel:setHeader> <camel:setHeader headerName="JMSCorrelationID"><camel:constant>_PLACEHOLDER_24_CHARS_ID_</camel:constant></camel:setHeader>
The target queue in the URI is overwritten by CamelJmsDestinationName above, so the queue name in the URI becomes a placeholder.
The preserveMessageQos URI parameter is one that has been observed to send a message with the ReplyTo settings set (to get an MQ CoD report), but it does not allow CAMEL to instantiate a message listener using InOnly MEP.
<camel:to uri="wmq:queue:PLACEHOLDER.Q.NAME?concurrentConsumers=1& exchangePattern=InOnly&preserveMessageQos=true& includeSentJMSMessageID=true" /> </camel:route> </camel:camelContext>
We are not done yet, we have to declare our queue factories for both our own JMS provider and Websphere MQ (through our own IBM WMQ JCA Resource Resource adapter), which will be configured to your context. We use JNDI requests for administrative objects here.
<camel:endpoint id="innerQueue" uri="jmsloc:queue:transitQueue"> </camel:endpoint> <jee:jndi-lookup id="mqQCFBean" jndi-name="jms/MYQMGR_QCF"/> <jee:jndi-lookup id="jmsraQCFBean" jndi-name="jms/jmsra_QCF"/> <bean id="jmsloc" class="org.apache.camel.component.jms.JmsComponent"> <property name="connectionFactory" ref="jmsraQCFBean" /> </bean> <bean id="wmq" class="org.apache.camel.component.jms.JmsComponent"> <property name="connectionFactory" ref="mqQCFBean" /> </bean> </beans>
An alternative to fetching plants (and JCA adapters) from JNDI is to declare the JMS client as Spring beans. In Weblogic and Glassfish, you will be better inspired to deploy your own IBM JCA resource adapter and create JNDI resources, which then reference the Spring context, as described above, in JBoss, an MQ bean direct client declaration works best, as shown below)
<bean id="mqCFBean" class="com.ibm.mq.jms.MQXAConnectionFactory"> <property name="hostName" value="${mqHost}"/> <property name="port" value="${mqPort}"/> <property name="queueManager" value="${mqQueueManager}"/> <property name="channel" value="${mqChannel}"/> <property name="transportType" value="1"/> <property name="appName" value="${connectionName}"/> </bean> <bean id="wmq" class="org.apache.camel.component.jms.JmsComponent"> <property name="connectionFactory" ref="mqCFBean"/> <property name="transacted" value="true"/> <property name="acknowledgementModeName" value="AUTO_ACKNOWLEDGE"/> </bean>
Comments and improvements are welcome.