1

My Apache-camel based app is consuming message from one of IBM queue, for e.g. below is the details for connection factory

hostname=host1000
QManager=QM1000
Port="some port"
Channel="common channel"

Camel flow to consume and process and send the response to ReplyQueue which is coming from message header.

 from(wmq:queue:<INPUT_QUEUE>)
.bean("processBean")
.bean("beanToSendMsgToReplyQueue")

In camel header I am getting below JMSReplyQueue. You can see that it's a different Queue manager and this queue manager is from different host but in a clusters environment.

JMSReplyTo = queue://QM1012/TEST.REPLY?targetClient=1

Also queue manager is coming in between. like

queue://<queue-manager>//<queue-name>?<other parameters>

Below is the exception which I am getting while sending message.

ERROR o.apache.camel.processor.DefaultErrorHandler:215 - Failed delivery for (MessageId: ID-xxxxxxxxx-0-4 on ExchangeId: ID-xxxxxx-42443-1492594420697-0-1). Exhausted after delivery attempt: 1 caught: org.apache.camel.ResolveEndpointFailedException: Failed to resolve endpoint: wmq://queue://QM1012/TEST.REPLY?targetClient=1 due to: Failed to resolve endpoint: wmq://queue://TAP2001R5/TEST?targetClient=1 due to: There are 1 parameters that couldn't be set on the endpoint. Check the uri if the parameters are spelt correctly and that they are properties of the endpoint. Unknown parameters=[{targetClient=1}]. Processed by failure processor: FatalFallbackErrorHandler[Pipeline[[Channel[sendTo(Endpoint[wmq://queue:BACKOUT_Q])], Channel[DelegateSync[com.xxx.yyy.listener.XXXOnExceptionProcessor@21c66ee4]], Channel[Stop]]]]

Can anyone please help me on sending message to different queue-manager queue which is there in different host but all are in same cluster. Also queue-manager name is coming in middle of the string, so how to resolve that. Please let me know if you need more details.

Update -1: tried with same queue manager and without parameters

JMSReplyTo = queue://QM1000/QUEUE_V1 below exception i am getting

org.springframework.jms.InvalidDestinationException: JMSWMQ2008: Failed to open MQ queue 'QM1000/QUEUE_V1'.; nested exception is com.ibm.msg.client.jms.DetailedInvalidDestinationException: JMSWMQ2008: Failed to open MQ queue 'QM1000/QUEUE_V1'. JMS attempted to perform an MQOPEN, but WebSphere MQ reported an error. Use the linked exception to determine the cause of this error. Check that the specified queue and queue manager are defined correctly.; nested exception is com.ibm.mq.MQException: JMSCMQ0001: WebSphere MQ call failed with compcode '2' ('MQCC_FAILED') reason '2189' ('MQRC_CLUSTER_RESOLUTION_ERROR').

Update-2

I am able to send message to JMSReplyTo using plain javax.jms.* and com.ibm.mq.jms.* api's, but not via Apache camel. Can anyone from Camel user/developer group help me to process the same using camel component.

@Override
public void process(Exchange exchange)
    throws Exception {

    QueueConnection m_connection = this.connectionFactory.createQueueConnection();
    //m_connection.start();
    boolean transacted = false;

    QueueSession session = m_connection.createQueueSession(transacted, QueueSession.AUTO_ACKNOWLEDGE);
    TextMessage outMessage = session.createTextMessage();
    outMessage.setText(exchange.getIn().getBody());
    MQQueue mq = new MQQueue(
        "queue://QM1012/TEST.REPLY");
    QueueSender queueSender = session.createSender((MQQueue) mq);
    queueSender.send(outMessage);

    /* producerTemplate.send("wmq:" + "queue://QM1012/TEST.REPLY", exchange); */
}
srakshit
  • 59
  • 1
  • 10
  • targetClient=1 indicates that JMS should not add the IBM MQ Classes for JMS headers which are referred to as RFH2 headers, in other words the remote application may not be a JMS application. Is it possible you need to specify this in a different way in a Camel flow? – JoshMc Apr 20 '17 at 19:54
  • yes, we can specify and resolve targetClient=1 in a different way. write now first problem is how to resolve or send message to different Queue hosted in different queue manager and hostname in IBM MQ cluster. – srakshit Apr 21 '17 at 02:13
  • 1
    The URI that you have minus the wmq:// is how you specify a queue on a specific queue manager to the IBM MQ classes for JMS. I'm not familar with Camel to help you more. You would need permission to either the SCTQ if MQ 7.0 or prior which are out of support or in MQ 7.1 and later you can provide permission to the rqmname of specific remote queue managers. – JoshMc Apr 21 '17 at 02:23
  • yes that URI is for queue on a specific queue manager. now in general to connect to a queue/topic or database, we have to provide at least hostname and other details, but here i am getting only QueueManger and Queue name. I checked with my ibm mq team from where app is consuming messages. As per them all are in cluster environment and i should be able to send message to that different queue-manager/queue. but they are not sure from Java, Camel how to do that. – srakshit Apr 21 '17 at 03:18
  • 2
    You just connect to the same queue manager where you consumed the original message and send it via the URL you have specified. As long as the queue manager you are connected to is part of the cluster where the other queue manager is located and you have the proper permissions you can send it. You only need connectivity details of the queue manager you connect to since you would not connect directly to the other queue managers. – JoshMc Apr 21 '17 at 03:45
  • Thanks for your comments. so you are saying using same connection object (where app consumed message) i will be able to send message to JMSReplyTo URI(which contains different queue manager) with cluster and permission. Actually above code tried the same and it's failing. I have permission and all queue-mangers what ever i am using are in cluster as per MQ team. – srakshit Apr 21 '17 at 04:02
  • The error is about Camel not understanding targetClient right? – JoshMc Apr 21 '17 at 04:27
  • 1
    Before doing anything with Camel, I would suggest you test your uri and the sending of the message to the MQ cluster with RFHUTIL on the same host as Camel. If you are successful then the MQ stuff works as intended. The URI for Camel should just follow the MQ uri pattern. But first get it to work with RFHUTIL. – Souciance Eqdam Rashti Apr 21 '17 at 06:56
  • 1
    In your first example QM1000 is the queue manager you read the request message from, and QM1012 is where the reply queue is located? If that is the case then the reply message should be put to queue://QM1012/QUEUE_V1 but your connection will still be to QM1000. @SoucianceEqdamRashti suggestion is a excellent way to verify the URI is correct. – JoshMc Apr 21 '17 at 07:33
  • yes JoshMc, first I tried with different queue manager (which is requirement), then tried with same queue manager(just to test whether it's going to same queue manager queue or not). Thanks @Souciance Eqdam Rashti, I will work with MQ team to test the same. – srakshit Apr 21 '17 at 08:57
  • Can you post the error from when you tried to different queue manager? FYI the RFHUTIL (AKA the IH03 SupportPac) is a client program that you can use to perform testing with out needing to have your MQ team involved. You can locate it [here](http://www-01.ibm.com/support/docview.wss?uid=swg24000637). – JoshMc Apr 21 '17 at 09:41
  • I am using the same location finally after accepting all terms I am getting below ftp url. ftp://public.dhe.ibm.com/software/integration/support/supportpacs/individual/ih03.zip which is showing blank. – srakshit Apr 21 '17 at 09:49
  • Please find my update-2, where I am able to send message to JMSReplyTo using plain javax.jms.* and com.ibm.mq.jms.* api's, but not via Apache camel and looking for Camel API solution for the same. Thanks @JoshMc and @ Souciance Eqdam Rashti to help me on MQ side. – srakshit Apr 21 '17 at 16:48

2 Answers2

3

You want to communicate with two different queue managers, so you'll need to define two Camel JMS component instances accordingly. Camel cannot magically know what QM1000 or QM1012 means and how the QMs can be accessed.

You first need the two JMS connection factory instances for the two WMQ QMs. How to get these depends on your execution environment. On JEE servers the connection pools can be accessed using JNDI after being configured. Have a look at your appserver documentantion on how to setup JMS pools. If you run stand-alone have a look at Spring JMS connection caching or Atomikos if you want XA transactions. Lets assume the CF for QM1000 is sourceCF and for QM1012 is targetCF.

Now you can define two instances of Camel JMS Component, one for each QM. Inject the connection factory into the JMS Components (.setConnectionFactory(...)). Assume you define a Camel JMS Component with id "jmssource", inject sourceCF. In JMS component id "jmstarget" inject targetCF. How to do that depends on your environment (JEE/CDI, Spring, plain Java). Have a look around stackoverflow, there are examples.

You can now specify Camel JMS producers and consumers on a Camel route using the syntax:

.from("jmssource:INPUT_QUEUE")
  ...
  (do some processing)
  ...
  .to("jmstarget:QUEUE_V1")

You cannot use Camel's JMS reply-to logic (using the JMSReplyTo header) to reply to another queue manager. I think this is not allowed by the JMS standard. You need to do the reply explicitly by sending to the reply queue.

For setting the targetClient option a destination resolver may be useful:

import org.springframework.jms.support.destination.DynamicDestinationResolver;
import org.springframework.jms.support.destination.DestinationResolver;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Session;
import com.ibm.mq.jms.JMSC;
import com.ibm.mq.jms.MQDestination;

public class WMQDestinationResolver extends DynamicDestinationResolver implements DestinationResolver {
  private int targetClient = JMSC.MQJMS_CLIENT_JMS_COMPLIANT;

  public void setTargetClient(int targetClient) {
    this.targetClient = targetClient;
  }

  public Destination resolveDestinationName(Session session, String destinationName, boolean isPubSubDomain) throws JMSException {
    Destination destination = super.resolveDestinationName(session, destinationName, isPubSubDomain);
    if (destination instanceof MQDestination) {
      MQDestination mqDestination = (MQDestination) destination;
      mqDestination.setTargetClient(targetClient);
    }
    return destination;
  }
}
SebastianBrandt
  • 439
  • 2
  • 10
  • Hi @Sebastian Brandt , Thanks for your suggestion. In case of multiple Queue manager that should be the right approach (using multiple camel jms component). Also we can't directly send – srakshit Apr 25 '17 at 09:00
  • Can you elaborate why directly sending to the reply queue is not an option? You could change the Camel exchange pattern to InOnly, then Camel does not send a reply by itself. Technically it doesn't make a difference whether Camel sends the response with InOut pattern or you do it explicitly using InOnly pattern. – SebastianBrandt Apr 25 '17 at 09:25
  • sorry, my last post was incomplete. Hi @Sebastian Brandt , Thanks for your suggestion. In case of multiple Queue manager that should be the right approach (using multiple camel jms component). Also we can't directly send targetClient=1 in camel endpoint. for that Camel JMS component API documentation has given some information(Section: Setting JMS Provider Options on the Destination). Any how Camel's disableReplyTo=false by default. Using that feature it's working fine and Camel is able to send message to queue which are there in different queue manager (clustered). – srakshit Apr 25 '17 at 10:11
  • I am going to post my working example soon using default camel feature. – srakshit Apr 25 '17 at 10:12
  • I fixed my answer wrt to the targetClient / destination resolver. – SebastianBrandt Apr 26 '17 at 15:15
1

First, thanks all for your support. My use case is as follows(which is there above too).

Connect to a MQ host(hostname, queueManager, port, channel) using Apache Camel and consume message from queue which is belongs to the same host/Qmanager. message is coming with replyToQueue (JMSReplyTo) header value. The value of ReplyToQueue (JMSReplyTo) is as follows

for e.g.

queue://Different_QueueManager_in_Cluster/TEST.REPLY?mdReadEnabled=true&messageBody=0&mdWriteEnabled=true&XMSC_WMQ_REPLYTO_STYLE=1&targetClient=1

Now question is, how to send reply message to a different queue with different queue manager while connection object is connecting to above mentioned host and queue manager.

NOTE: All MQ queue managers are in clustered environment.

Solution 1: for e.g.

form(wmq:queue:INPUT_MSG_Q)
 .bean(requestProcessor)
 .bean(responseProcessor)

Apache Camel by default handle ReplyToQ (JMSReplyTo). If you don't want to send reply to ReplyToQ (JMSReplyTo) then use disableReplyTo=true while consuming

NOTE: While sending to queue://Different_QueueManager_in_Cluster/TEST.REPLY, using same connection/connection factory, MQ cluster will check that message has to go to specified queue manager with specified queue in Cluster. Regarding following parameters ?mdReadEnabled=true&messageBody=0&mdWriteEnabled=true&XMSC_WMQ_REPLYTO_STYLE=1&targetClient=1, Apache Camel is able to resolve automatically without using any 3nd party resolver while auto reply to JMSReplyTo.

Solution 2:

Disable auto reply using disableReplyTo=true and get the queue details from header and send message using plain javax.jms.* and com.ibm.mq.jms.* api's. Code for the same as follows.

@Override
public void process(Exchange exchange)
    throws Exception {

    QueueConnection m_connection = this.connectionFactory.createQueueConnection();
    //m_connection.start();
    boolean transacted = false;

    QueueSession session = m_connection.createQueueSession(transacted, QueueSession.AUTO_ACKNOWLEDGE);
    TextMessage outMessage = session.createTextMessage();
    outMessage.setText(exchange.getIn().getBody());
    MQQueue mq = new MQQueue(
        "queue://Different_QueueManager_in_Cluster/TEST.REPLY");
    QueueSender queueSender = session.createSender((MQQueue) mq);
    queueSender.send(outMessage);

    /* producerTemplate.send("wmq:" + "queue://Different_QueueManager_in_Cluster/TEST.REPLY", exchange); */
}

for parameters, use destination resolver as mentioned by @Sebastian Brandt (post)

Community
  • 1
  • 1
srakshit
  • 59
  • 1
  • 10