0

I copy the java client code for how to call IBM MQ, and pass the request to the queue, but sometimes I getting the wrong response back from the queue.

For example, if I submit the following request: F LOYFI6331760101046481882

I expect from the response I should get F LOYFA36331760101046481882

But actually I getting F LOYFA36331760101051292448

As you can see the card number is wrong.

Here is the code

import javax.jms.BytesMessage;
import javax.jms.Destination;
import javax.jms.JMSConsumer;
import javax.jms.JMSContext;
import javax.jms.JMSException;
import javax.jms.JMSProducer;
import javax.jms.TextMessage;

import com.ibm.msg.client.jms.JmsConnectionFactory;
import com.ibm.msg.client.jms.JmsFactoryFactory;
import com.ibm.msg.client.wmq.WMQConstants;

public class MQClient {
    // System exit status value (assume unset value to be 1)
    private static int status = 1;

    public static byte[] sendAndReceive(String HOST, Integer PORT, String QMGR, String CHANNEL, String requestQueue, String responseQueue, String payload) {
        // Variables
        JMSContext context = null;
        Destination destination = null;
        JMSProducer producer = null;
        JMSConsumer consumer = null;
        BytesMessage receivedMessage = null;
        byte[] result = null;
        try {
            // Create a connection factory
            JmsFactoryFactory ff = JmsFactoryFactory.getInstance(WMQConstants.WMQ_PROVIDER);
            JmsConnectionFactory cf = ff.createConnectionFactory();

            // Set the properties
            cf.setStringProperty(WMQConstants.WMQ_HOST_NAME, HOST);
            cf.setIntProperty(WMQConstants.WMQ_PORT, PORT);
            cf.setStringProperty(WMQConstants.WMQ_CHANNEL, CHANNEL);
            cf.setIntProperty(WMQConstants.WMQ_CONNECTION_MODE, WMQConstants.WMQ_CM_CLIENT);
            cf.setStringProperty(WMQConstants.WMQ_QUEUE_MANAGER, QMGR);
            cf.setStringProperty(WMQConstants.WMQ_APPLICATIONNAME, "JmsPutGet (JMS)");
            cf.setStringProperty(WMQConstants.WMQ_TARGET_CLIENT, "1");
            // Create JMS objects
            context = cf.createContext();
            destination = context.createQueue("queue:///" + requestQueue +"?targetClient=1");

            TextMessage message = context.createTextMessage(payload);

            producer = context.createProducer();
            producer.send(destination, message);
            System.out.println("Sent message:\n" + message);

            destination = context.createQueue("queue:///" + responseQueue + "?targetClient=1");
            consumer = context.createConsumer(destination); // autoclosable
            receivedMessage= (BytesMessage)consumer.receive();
            System.out.println("Receiving message:" + receivedMessage);
            int text_length = new Long(receivedMessage.getBodyLength()).intValue();
            result = new byte[text_length];
            receivedMessage.readBytes(result, text_length);

            System.out.println("\nReceived message:\n" + new String(result));

            recordSuccess();

        } catch (JMSException jmsex) {
            recordFailure(jmsex);
        }finally {
            context.close();
        }

        return result;

    }
}

I have another project to run concurrently to call MQClient.sendAndReceive() method, with same host, port, QMGR, channel, requestQueue and responseQueue, only payload is different.

So how do I fix the code above to make sure I always getting the correct response corresponding to the request?

EDIT: 1. For JoshMac questions, app means the IBM MQ one? Or the app that will call my sendAndReceive function?

  1. Here is the flow I have, I using mule flow take the request from the POS, process the request, which need to call IBM MQ (which sit on AS400), to get the response back from MQ, and send back to POS. (In this example, I need to submit my request to INQ1 and get the response from INQR1). Based on the answer below, it seems like the sendAndReceive function is treat as Requester, I need another flow to call the Responder to handle the response, so receivedMessage= (BytesMessage)consumer.receive(); will not getting stuck? Correct me if I am wrong
Vincent Zheng
  • 385
  • 1
  • 4
  • 19

2 Answers2

0

Can you use different topic to differentiate?

That's a bad idea when you are doing point-to-point messaging.

destination = context.createQueue("queue:///" + responseQueue + "?targetClient=1");

It sounds like your responseQueue is shared between multiple consumers. You have 2 options:

  1. Create your own temporary dynamic queue and set it as the "Reply-To" queue

i.e.

Queue replyQ = session.createTemporaryQueue();
  1. Use MsgId / CorrelId request-reply messaging pattern.

i.e. Follow the suggestions on this page: Not able to get response from IBM MQ using JMS application

Roger
  • 7,062
  • 13
  • 20
  • I don't know how to setup temporary queue properly, because i need to send the request to INQ1, and I getting the response from INQR1.. – Vincent Zheng May 28 '19 at 17:06
0

It looks like all responses are being sent to INQR1. To help your app identify which of the responses are for it and which it can ignore, you can make use of a selector, and normally the selector is by correlation id, though you could use other fields.

Essentially you are using a request / response pattern for which there are JMS samples - https://github.com/ibm-messaging/mq-dev-patterns/tree/master/JMS

Your requester would have logic like:

String correlationID = String.format("%24.24s", UUID.randomUUID().toString());
message.setJMSCorrelationIDAsBytes(b);

to generate a correlation id, and add it to the message.

Your requester would then create a correlation id based selector to filter the response queue:

try {
      b = correlationID.getBytes();
      selector = "JMSCorrelationID='ID:" + getHexString(b) + "'";
    } catch (Exception e) {
       ...
    }

which you use to construct your message consumer:

JMSConsumer consumer = context.createConsumer(requestQueue, selector);
Message receivedMessage = consumer.receive();

When your responder receives the initial request, it can get hold of the correlation id:

String correlationID = receivedMessage.getJMSCorrelationID();

and use it to set the correlation id for the response:

message.setJMSCorrelationID(correlationID);
chughts
  • 4,210
  • 2
  • 14
  • 27
  • I still confuse, how to make sure I send my request to INQ1 and getting the response back from INQR1? And based on the github example, the requestQueue is temporary queue,not the INQR1... – Vincent Zheng May 29 '19 at 15:03
  • That remains unchanged in your code. If you have a response queue, then you don't need a temporary queue, just use the response queue. You already have `destination = context.createQueue("queue:///" + requestQueue +"?targetClient=1");` and `destination = context.createQueue("queue:///" + responseQueue + "?targetClient=1");` They remain the same. The only difference will be how you create the consumer on the response queue, which in your case would become `JMSConsumer consumer = context.createConsumer(destination, selector);` – chughts May 29 '19 at 15:54
  • Tried, and now it getting stuck on ```receivedMessage = (BytesMessage)consumer.receive();``` – Vincent Zheng May 29 '19 at 17:25
  • What chughts described requires the responding app to copy the correlation id from your request message to the correlation id of the reply. In my experience a more common pattern is that the message id of the request message is copied to the correlation id of the reply. I asked about this but you have not responded to that question. – JoshMc May 29 '19 at 23:30
  • So now your requester is waiting for messages with the matching selector. Your responder needs to set the appropriate selector, in this case correlation id. As @JoshMc says the common pattern is to use the message id of the original request message as the correlation id, and the selector would make use of that. Which means that you don't need to generate a correlation id. Either way your requester and responder need to sync and make use of the same selector / value. – chughts May 30 '19 at 08:59