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. It works with its own MQ JCA resource adapter for IBM v7.5, CAMEL 2.15, Spring core 4.2. I can deploy it on both Glassfish and Weblogic servers.
Of course, DSL is used in a real implementation taking into account numerous variables. This example, based on the CAMEL XML DSL, is self-contained and easy to test.
Let's start with 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: when using your own MQ resource adapter, the only way to get (for example) 3 listeners is to use 3 connections (with 3 camels: from the operators in the sequence) with a maximum of 1 session each, otherwise error MQ: MQJCA1018: Only one session per connection is allowed . However, if you use MQ client banks instead, the concurentConsumers option in CAMEL JMS works fine.
<camel:from uri="wmq:queue:TEST.Q1?concurrentConsumers=1&disableReplyTo=true& acknowledgementModeName=SESSION_TRANSACTED"/> <camel:from uri="wmq:queue:TEST.Q1?concurrentConsumers=1&disableReplyTo=true& acknowledgementModeName=SESSION_TRANSACTED"/> <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>
Next is the jms-to-MQ route:
<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 the IBM documentation.
<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>
In your context, you need to adjust the following. It provides queue factories for both its own JMS provider and Websphere MQ (through its own IBM WMQ JCA Resource Adapter). 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>
Alternatively, if you use MQ client banners instead of a resource adapter, you will declare the factory beans connection as (instead of JNDI requests, as mentioned above):
<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.