MQ Request/Reply Scenario with Async Getter Thread

Ok. Continuing on with the previous blog posting here, the StackOverflow user says that s/he wants the getter component to be run in a separate thread.

To me, the request does not really make a lot of sense. If the user wants to do a Get with CorrelId then they should be coding for synchronous processing and not Asynchronous.

So, I have created a fully functioning Java/MQ application that will perform the request/response as an asynchronous process. The code uses Java’s Blocking Queue to communicate between the 2 threads. It will put 10 messages on the queue and then pass 3 random numbers as CorrelId to the Getter thread to perform a Get by CorrelId and a 4th invalid random number on the blocking queue to show how a failure would work.

You can download the source code from here.

import java.io.IOException;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Hashtable;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

import com.ibm.mq.MQException;
import com.ibm.mq.MQGetMessageOptions;
import com.ibm.mq.MQMessage;
import com.ibm.mq.MQPutMessageOptions;
import com.ibm.mq.MQQueue;
import com.ibm.mq.MQQueueManager;
import com.ibm.mq.constants.CMQC;

/**
 * Program Name
 *  MQTest11Async
 *
 * Description
 *  This java class will connect to a remote queue manager with the
 *  MQ setting stored in a HashTable, 
 *  
 *  Functionality:
 *  - Parse input parameters
 *  - Start a child thread for retrieving messages by CorrelId
 *  - Connect and open queue 
 *  - Put 10 message on a queue with unique CorrelIds
 *  - Pass the CorrelId to the child thread via the Blocking Queue
 *  - Close and disconnect
 *  
 *  Child thread:
 *  - Connect and open queue
 *  - Waiting on Blocking Queue 
 *  - Retrieve the message by CorrelId
 *  - When the QUIT message is received exit loop
 *  - Close and disconnect
 *
 * Sample Command Line Parameters
 *  -m MQA1 -h 127.0.0.1 -p 1414 -c TEST.CHL -q TEST.Q1 -u UserID -x Password
 *
 * @author Roger Lacroix
 */
public class MQTest11Async
{
   private static final SimpleDateFormat  LOGGER_TIMESTAMP = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS");
   private static final String            QUIT_MSG = "QUIT";
   private Hashtable<String,String>       params = new Hashtable<String,String>();
   private Hashtable<String,Object>       mqht = new Hashtable<String,Object>();

   /**
    * The constructor
    */
   public MQTest11Async()
   {
      super();
   }

   /**
    * Make sure the required parameters are present.
    * @return true/false
    */
   private boolean allParamsPresent()
   {
      boolean b = params.containsKey("-h") && params.containsKey("-p") &&
                  params.containsKey("-c") && params.containsKey("-m") &&
                  params.containsKey("-q") &&
                  params.containsKey("-u") && params.containsKey("-x");
      if (b)
      {
         try
         {
            Integer.parseInt((String) params.get("-p"));
         }
         catch (NumberFormatException e)
         {
            b = false;
         }
      }

      return b;
   }

   /**
    * Extract the command-line parameters and initialize the MQ HashTable.
    * @param args
    * @throws IllegalArgumentException
    */
   private void init(String[] args) throws IllegalArgumentException
   {
      int port = 1414;
      if (args.length > 0 && (args.length % 2) == 0)
      {
         for (int i = 0; i < args.length; i += 2)
         {
            params.put(args[i], args[i + 1]);
         }
      }
      else
      {
         throw new IllegalArgumentException();
      }

      if (allParamsPresent())
      {

         try
         {
            port = Integer.parseInt((String) params.get("-p"));
         }
         catch (NumberFormatException e)
         {
            port = 1414;
         }
         
         mqht.put(CMQC.CHANNEL_PROPERTY, params.get("-c"));
         mqht.put(CMQC.HOST_NAME_PROPERTY, params.get("-h"));
         mqht.put(CMQC.PORT_PROPERTY, new Integer(port));
         mqht.put(CMQC.USER_ID_PROPERTY, params.get("-u"));
         mqht.put(CMQC.PASSWORD_PROPERTY, params.get("-x"));

         // I don't want to see MQ exceptions at the console.
         MQException.log = null;
      }
      else
      {
         throw new IllegalArgumentException();
      }
   }

   /**
    * Connect, open queue, write 10 messages, close queue and disconnect.
    */
   private void testSend()
   {
      MQPutMessageOptions     pmo = new MQPutMessageOptions();
      pmo.options = CMQC.MQPMO_NO_SYNCPOINT | CMQC.MQPMO_FAIL_IF_QUIESCING;
      MQQueueManager          qMgr = null;
      MQQueue                 queue = null;
      MQMessage               sendmsg;
      String                  msgData;
      DecimalFormat           df = new DecimalFormat("0000");
      BlockingQueue<Object>   toGetter_BQ = new ArrayBlockingQueue<Object>(100);
      String                  qMgrName = (String) params.get("-m");;
      String                  outputQName = (String) params.get("-q");

      /**
       * Start up child "getter" thread.
       */
      Thread t1 = new Thread(new Getter(toGetter_BQ, qMgrName, outputQName));
      t1.start();

      try
      {
         qMgr = new MQQueueManager(qMgrName, mqht);
         logger("successfully connected to "+ qMgrName);

         queue = qMgr.accessQueue(outputQName, CMQC.MQOO_OUTPUT | CMQC.MQOO_FAIL_IF_QUIESCING);
         logger("successfully opened "+ outputQName);
         
         /*
          * Code to send 10 messages with a specific CorrelId.  i.e. 0001, 0002, etc.
          */
         for (int i=0; i < 10; i++)
         {
            // Define a simple MQ message, and write some text
            sendmsg = new MQMessage();
            sendmsg.format = CMQC.MQFMT_STRING;
            sendmsg.messageId = CMQC.MQMI_NONE;
            sendmsg.correlationId = df.format(i+1).getBytes();

            // Write message data
            msgData = "This is a test message from MQTest11Async. CorrelID is "+new String(sendmsg.correlationId);
            sendmsg.writeString(msgData);

            // put the message on the queue
            queue.put(sendmsg, pmo);
            logger("Sent: Message Data>>>" + msgData);
         }
         
         try
         {
            // Put 3 random numbers on the blocking queue to be used as CorrelId by the child thread. 
            toGetter_BQ.put(df.format(5).getBytes());
            toGetter_BQ.put(df.format(3).getBytes());
            toGetter_BQ.put(df.format(7).getBytes());

            // Put on unknown CorrelId
            toGetter_BQ.put(df.format(99).getBytes());

            // Ok, tell child we are done.
            toGetter_BQ.put(QUIT_MSG);
         }
         catch (InterruptedException ie)
         {
            logger("InterruptedException: "+ie.getMessage());
         }
      }
      catch (MQException e)
      {
         logger("CC=" +e.completionCode + " : RC=" + e.reasonCode);
      }
      catch (IOException e)
      {
         logger("IOException:" +e.getLocalizedMessage());
      }
      finally
      {
         try
         {
            if (queue != null)
            {
               queue.close();
               logger("closed: "+ outputQName);
            }
         }
         catch (MQException e)
         {
            logger("CC=" +e.completionCode + " : RC=" + e.reasonCode);
         }
         try
         {
            if (qMgr != null)
            {
               qMgr.disconnect();
               logger("disconnected from "+ qMgrName);
            }
         }
         catch (MQException e)
         {
            logger("CC=" +e.completionCode + " : RC=" + e.reasonCode);
         }
      }
      
      try
      {
         // Wait for child thread to be done.
         t1.join();
      }
      catch (InterruptedException ie)
      {
         logger("InterruptedException: "+ie.getMessage());
      }
   }

   /**
    * Getter class to be run as a separate thread to retrieve messages from a queue. 
    * Connect, open queue, wait on blocking queue for instructions on what to do.  
    * Loop until we get the QUIT message then close queue and disconnect. 
    */
   class Getter implements Runnable 
   {
      private BlockingQueue<Object> toGetter_BQ;
      private String                qMgrName;
      private String                replyQName;
      private boolean               working = true;

      /**
       * The constructor
       * @param toGetter_BQ Blocking Queue
       * @param qMgrName Queue Manager name
       * @param replyQName Reply queue name
       */
      public Getter(BlockingQueue<Object> toGetter_BQ, String qMgrName, String replyQName)
      {
         super();
         this.toGetter_BQ = toGetter_BQ;
         this.qMgrName = qMgrName;
         this.replyQName = replyQName;
      }
      
      @Override
      public void run() 
      {
         Object         o;
         MQQueueManager qMgr = null;
         MQQueue        queue = null;

         try
         {
            qMgr = new MQQueueManager(qMgrName, mqht);
            logger("successfully connected to "+ qMgrName);

            queue = qMgr.accessQueue(replyQName, CMQC.MQOO_INPUT_SHARED | CMQC.MQOO_FAIL_IF_QUIESCING);
            logger("successfully opened "+ replyQName);
            
            while (working)
            {
               // poll returns immediately with either an object or null
               o = toGetter_BQ.poll();

               /*
                * Check what we got off the blocking queue.
                */
               if (o == null)
               {
                  try
                  {
                     // Nothing to do! Put a slight pause in the loop. 
                     if (working)
                        Thread.sleep(50); // time in milliseconds
                  }
                  catch (InterruptedException ie)
                  {}
               }
               else if (o instanceof byte[])
               {
                  logger("Retrieve the next CorrelId: " + new String((byte[])o));
                  getMessage((byte[])o, queue);
               }
               else if (o instanceof String)
               {
                  if (QUIT_MSG.equals((String)o))
                  {
                     logger("quitting time. ");
                     working = false;
                  }
                  else
                  {
                     logger("Error: unknown string command: " + (String)o);
                  }
               }
               else
               {
                  logger("Error: unknown object passed into BlockingQueue.");
               }
            }
         }
         catch (MQException e)
         {
            logger("CC=" +e.completionCode + " : RC=" + e.reasonCode);
         }
         finally
         {
            try
            {
               if (queue != null)
               {
                  queue.close();
                  logger("closed: "+ replyQName);
               }
            }
            catch (MQException e)
            {
               logger("CC=" +e.completionCode + " : RC=" + e.reasonCode);
            }
            try
            {
               if (qMgr != null)
               {
                  qMgr.disconnect();
                  logger("disconnected from "+ qMgrName);
               }
            }
            catch (MQException e)
            {
               logger("CC=" +e.completionCode + " : RC=" + e.reasonCode);
            }
         }
      }

      /**
       * Retrieve a message by specific CorrelId
       * @param correlId
       * @param queue
       */
      private void getMessage(byte[] correlId, MQQueue queue)
      {
         logger("Attempting to get message from queue.");
         
         MQGetMessageOptions gmo = new MQGetMessageOptions();
         gmo.options = CMQC.MQGMO_NO_SYNCPOINT | CMQC.MQGMO_WAIT | CMQC.MQGMO_CONVERT | CMQC.MQGMO_FAIL_IF_QUIESCING;
         gmo.matchOptions = CMQC.MQMO_MATCH_CORREL_ID;
         gmo.waitInterval = 5000; // 5 seconds or you can use CMQC.MQWI_UNLIMITED
         
         // Define a simple MQ message, and write some text
         MQMessage receiveMsg = new MQMessage();
         receiveMsg.messageId = CMQC.MQMI_NONE;
         receiveMsg.correlationId = correlId;

         try
         {
            // get the message on the queue
            queue.get(receiveMsg, gmo);

            if (CMQC.MQFMT_STRING.equals(receiveMsg.format))
            {
               String msgStr = receiveMsg.readStringOfByteLength(receiveMsg.getMessageLength());
               logger("Received: Message Data>>>" + msgStr);
            }
            else
            {
               byte[] b = new byte[receiveMsg.getMessageLength()];
               receiveMsg.readFully(b);
               logger("Received: Message Data>>>" + new String(b));
            }
         }
         catch (MQException e)
         {
            logger("CC=" +e.completionCode + " : RC=" + e.reasonCode);
         }
         catch (IOException e)
         {
            logger("IOException:" +e.getLocalizedMessage());
         }
      }
  }
   /**
    * A simple logger method
    * @param data
    */
   public static void logger(String data)
   {
      String className = Thread.currentThread().getStackTrace()[2].getClassName();

      // Remove the package info.
      if ( (className != null) && (className.lastIndexOf('.') != -1) )
         className = className.substring(className.lastIndexOf('.')+1);

      System.out.println(LOGGER_TIMESTAMP.format(new Date())+" "+className+": "+Thread.currentThread().getStackTrace()[2].getMethodName()+": "+data);
   }

   /**
    * main line
    * @param args
    */
   public static void main(String[] args)
   {
      MQTest11Async mqta = new MQTest11Async();

      try
      {
         mqta.init(args);
         mqta.testSend();
         
         logger("exiting.");
      }
      catch (IllegalArgumentException e)
      {
         logger("Usage: java MQTest11Async -m QueueManagerName -h host -p port -c channel -q QueueName -u UserID -x Password");
         System.exit(1);
      }

      System.exit(0);
   }
}

And the output will look like:

2021/07/08 13:27:04.684 MQTest11Async: testSend: successfully connected to MQA1
2021/07/08 13:27:04.684 MQTest11Async$Getter: run: successfully connected to MQA1
2021/07/08 13:27:04.700 MQTest11Async: testSend: successfully opened TEST.Q1
2021/07/08 13:27:04.700 MQTest11Async$Getter: run: successfully opened TEST.Q1
2021/07/08 13:27:04.700 MQTest11Async: testSend: Sent: Message Data>>>This is a test message from MQTest11Async. CorrelID is 0001
2021/07/08 13:27:04.700 MQTest11Async: testSend: Sent: Message Data>>>This is a test message from MQTest11Async. CorrelID is 0002
2021/07/08 13:27:04.700 MQTest11Async: testSend: Sent: Message Data>>>This is a test message from MQTest11Async. CorrelID is 0003
2021/07/08 13:27:04.700 MQTest11Async: testSend: Sent: Message Data>>>This is a test message from MQTest11Async. CorrelID is 0004
2021/07/08 13:27:04.700 MQTest11Async: testSend: Sent: Message Data>>>This is a test message from MQTest11Async. CorrelID is 0005
2021/07/08 13:27:04.700 MQTest11Async: testSend: Sent: Message Data>>>This is a test message from MQTest11Async. CorrelID is 0006
2021/07/08 13:27:04.700 MQTest11Async: testSend: Sent: Message Data>>>This is a test message from MQTest11Async. CorrelID is 0007
2021/07/08 13:27:04.700 MQTest11Async: testSend: Sent: Message Data>>>This is a test message from MQTest11Async. CorrelID is 0008
2021/07/08 13:27:04.700 MQTest11Async: testSend: Sent: Message Data>>>This is a test message from MQTest11Async. CorrelID is 0009
2021/07/08 13:27:04.700 MQTest11Async: testSend: Sent: Message Data>>>This is a test message from MQTest11Async. CorrelID is 0010
2021/07/08 13:27:04.700 MQTest11Async: testSend: closed: TEST.Q1
2021/07/08 13:27:04.715 MQTest11Async: testSend: disconnected from MQA1
2021/07/08 13:27:04.762 MQTest11Async$Getter: run: Retrieve the next CorrelId: 0005
2021/07/08 13:27:04.762 MQTest11Async$Getter: getMessage: Attempting to get message from queue.
2021/07/08 13:27:04.769 MQTest11Async$Getter: getMessage: Received: Message Data>>>This is a test message from MQTest11Async. CorrelID is 0005
2021/07/08 13:27:04.769 MQTest11Async$Getter: run: Retrieve the next CorrelId: 0003
2021/07/08 13:27:04.769 MQTest11Async$Getter: getMessage: Attempting to get message from queue.
2021/07/08 13:27:04.769 MQTest11Async$Getter: getMessage: Received: Message Data>>>This is a test message from MQTest11Async. CorrelID is 0003
2021/07/08 13:27:04.769 MQTest11Async$Getter: run: Retrieve the next CorrelId: 0007
2021/07/08 13:27:04.769 MQTest11Async$Getter: getMessage: Attempting to get message from queue.
2021/07/08 13:27:04.769 MQTest11Async$Getter: getMessage: Received: Message Data>>>This is a test message from MQTest11Async. CorrelID is 0007
2021/07/08 13:27:04.769 MQTest11Async$Getter: run: Retrieve the next CorrelId: 0099
2021/07/08 13:27:04.769 MQTest11Async$Getter: getMessage: Attempting to get message from queue.
2021/07/08 13:27:09.774 MQTest11Async$Getter: getMessage: CC=2 : RC=2033
2021/07/08 13:27:09.774 MQTest11Async$Getter: run: quitting time.
2021/07/08 13:27:09.774 MQTest11Async$Getter: run: closed: TEST.Q1
2021/07/08 13:27:09.774 MQTest11Async$Getter: run: disconnected from MQA1
2021/07/08 13:27:09.774 MQTest11Async: main: exiting.

Regards,
Roger Lacroix
Capitalware Inc.

HPE NonStop, IBM i (OS/400), IBM MQ, IBM MQ Appliance, Java, Linux, macOS (Mac OS X), Open Source, Programming, Raspberry Pi, Unix, Windows Comments Off on MQ Request/Reply Scenario with Async Getter Thread

Put 2 Messages with Unique CorrelId and Get 1 Message by CorrelId

On StackOverflow, someone asked a question about putting 2 messages on a queue with unique correlation ids and then retrieve a message with a particular correlation id.

Here is a fully functioning Java/MQ program that will put 2 messages on a queue with unique correlation ids and then retrieve a message with a particular correlation id (i.e. “0002”).

You can download the source code from here.

import java.io.IOException;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Hashtable;

import com.ibm.mq.MQException;
import com.ibm.mq.MQGetMessageOptions;
import com.ibm.mq.MQMessage;
import com.ibm.mq.MQPutMessageOptions;
import com.ibm.mq.MQQueue;
import com.ibm.mq.MQQueueManager;
import com.ibm.mq.constants.CMQC;

/**
 * Program Name
 *  MQTest11B
 *
 * Description
 *  This java class will connect to a remote queue manager with the
 *  MQ setting stored in a HashTable, put 2 message on a queue with unique CorrelIds
 *  and then retrieve the message with a  CorrelId of "0002".
 *
 * Sample Command Line Parameters
 *  -m MQA1 -h 127.0.0.1 -p 1414 -c TEST.CHL -q TEST.Q1 -u UserID -x Password
 *
 * @author Roger Lacroix
 */
public class MQTest11B
{
   private static final SimpleDateFormat  LOGGER_TIMESTAMP = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS");

   private Hashtable<String,String> params;
   private Hashtable<String,Object> mqht;
   private String qMgrName;
   private String outputQName;

   /**
    * The constructor
    */
   public MQTest11B()
   {
      super();
      params = new Hashtable<String,String>();
      mqht = new Hashtable<String,Object>();
   }

   /**
    * Make sure the required parameters are present.
    * @return true/false
    */
   private boolean allParamsPresent()
   {
      boolean b = params.containsKey("-h") && params.containsKey("-p") &&
                  params.containsKey("-c") && params.containsKey("-m") &&
                  params.containsKey("-q") &&
                  params.containsKey("-u") && params.containsKey("-x");
      if (b)
      {
         try
         {
            Integer.parseInt((String) params.get("-p"));
         }
         catch (NumberFormatException e)
         {
            b = false;
         }
      }

      return b;
   }

   /**
    * Extract the command-line parameters and initialize the MQ HashTable.
    * @param args
    * @throws IllegalArgumentException
    */
   private void init(String[] args) throws IllegalArgumentException
   {
      int port = 1414;
      if (args.length > 0 && (args.length % 2) == 0)
      {
         for (int i = 0; i < args.length; i += 2)
         {
            params.put(args[i], args[i + 1]);
         }
      }
      else
      {
         throw new IllegalArgumentException();
      }

      if (allParamsPresent())
      {
         qMgrName = (String) params.get("-m");
         outputQName = (String) params.get("-q");

         try
         {
            port = Integer.parseInt((String) params.get("-p"));
         }
         catch (NumberFormatException e)
         {
            port = 1414;
         }
         
         mqht.put(CMQC.CHANNEL_PROPERTY, params.get("-c"));
         mqht.put(CMQC.HOST_NAME_PROPERTY, params.get("-h"));
         mqht.put(CMQC.PORT_PROPERTY, new Integer(port));
         mqht.put(CMQC.USER_ID_PROPERTY, params.get("-u"));
         mqht.put(CMQC.PASSWORD_PROPERTY, params.get("-x"));

         // I don't want to see MQ exceptions at the console.
         MQException.log = null;
      }
      else
      {
         throw new IllegalArgumentException();
      }
   }

   /**
    * Connect, open queue, write a message, close queue and disconnect.
    *
    */
   private void testSendAndReceive()
   {
      MQQueueManager qMgr = null;
      MQQueue queue = null;
      int openOptions = CMQC.MQOO_INPUT_SHARED | CMQC.MQOO_OUTPUT | CMQC.MQOO_FAIL_IF_QUIESCING;
      MQPutMessageOptions pmo = new MQPutMessageOptions();
      pmo.options = CMQC.MQPMO_NO_SYNCPOINT | CMQC.MQPMO_FAIL_IF_QUIESCING;
      MQGetMessageOptions gmo = new MQGetMessageOptions();
      gmo.options = CMQC.MQGMO_NO_SYNCPOINT | CMQC.MQGMO_WAIT | CMQC.MQGMO_CONVERT | CMQC.MQGMO_FAIL_IF_QUIESCING;
      gmo.matchOptions = CMQC.MQMO_MATCH_CORREL_ID;
      gmo.waitInterval = CMQC.MQWI_UNLIMITED;
      MQMessage sendmsg;
      String msgData;
      DecimalFormat df = new DecimalFormat("0000");

      try
      {
         qMgr = new MQQueueManager(qMgrName, mqht);
         logger("successfully connected to "+ qMgrName);

         queue = qMgr.accessQueue(outputQName, openOptions);
         logger("successfully opened "+ outputQName);
         
         /*
          * Code to send 2 messages with a specific CorrelId.  i.e. 0001 and 0002
          */
         for (int i=0; i < 2; i++)
         {
            // Define a simple MQ message, and write some text
            sendmsg = new MQMessage();
            sendmsg.format = CMQC.MQFMT_STRING;
            sendmsg.messageId = CMQC.MQMI_NONE;
            sendmsg.correlationId = df.format(i+1).getBytes();

            // Write message data
            msgData = "This is a test message from MQTest11B. CorrelID is "+new String(sendmsg.correlationId);
            sendmsg.writeString(msgData);

            // put the message on the queue
            queue.put(sendmsg, pmo);
            logger("Sent: Message Data>>>" + msgData);
         }
         
         /*
          * Code to receive a message with a specific CorrelId.  i.e. 0002
          */
         
         // Define a simple MQ message, and write some text
         MQMessage receiveMsg = new MQMessage();
         receiveMsg.messageId = CMQC.MQMI_NONE;
         receiveMsg.correlationId = "0002".getBytes();

         // get the message on the queue
         queue.get(receiveMsg, gmo);

         if (CMQC.MQFMT_STRING.equals(receiveMsg.format))
         {
            String msgStr = receiveMsg.readStringOfByteLength(receiveMsg.getMessageLength());
            logger("Received: Message Data>>>" + msgStr);
         }
         else
         {
            byte[] b = new byte[receiveMsg.getMessageLength()];
            receiveMsg.readFully(b);
            logger("Received: Message Data>>>" + new String(b));
         }
      }
      catch (MQException e)
      {
         logger("CC=" +e.completionCode + " : RC=" + e.reasonCode);
      }
      catch (IOException e)
      {
         logger("IOException:" +e.getLocalizedMessage());
      }
      finally
      {
         try
         {
            if (queue != null)
            {
               queue.close();
               logger("closed: "+ outputQName);
            }
         }
         catch (MQException e)
         {
            logger("CC=" +e.completionCode + " : RC=" + e.reasonCode);
         }
         try
         {
            if (qMgr != null)
            {
               qMgr.disconnect();
               logger("disconnected from "+ qMgrName);
            }
         }
         catch (MQException e)
         {
            logger("CC=" +e.completionCode + " : RC=" + e.reasonCode);
         }
      }
   }

   /**
    * A simple logger method
    * @param data
    */
   public static void logger(String data)
   {
      String className = Thread.currentThread().getStackTrace()[2].getClassName();

      // Remove the package info.
      if ( (className != null) && (className.lastIndexOf('.') != -1) )
         className = className.substring(className.lastIndexOf('.')+1);

      System.out.println(LOGGER_TIMESTAMP.format(new Date())+" "+className+": "+Thread.currentThread().getStackTrace()[2].getMethodName()+": "+data);
   }

   /**
    * main line
    * @param args
    */
   public static void main(String[] args)
   {
      MQTest11B write = new MQTest11B();

      try
      {
         write.init(args);
         write.testSendAndReceive();
      }
      catch (IllegalArgumentException e)
      {
         logger("Usage: java MQTest11B -m QueueManagerName -h host -p port -c channel -q QueueName -u UserID -x Password");
         System.exit(1);
      }

      System.exit(0);
   }
}

And the output will look like:

2021/07/02 14:01:59.316 MQTest11B: testSendAndReceive: successfully connected to MQA1
2021/07/02 14:01:59.332 MQTest11B: testSendAndReceive: successfully opened TEST.Q1
2021/07/02 14:01:59.332 MQTest11B: testSendAndReceive: Sent: Message Data>>>This is a test message from MQTest11B. CorrelID is 0001
2021/07/02 14:01:59.347 MQTest11B: testSendAndReceive: Sent: Message Data>>>This is a test message from MQTest11B. CorrelID is 0002
2021/07/02 14:01:59.347 MQTest11B: testSendAndReceive: Received: Message Data>>>This is a test message from MQTest11B. CorrelID is 0002
2021/07/02 14:01:59.347 MQTest11B: testSendAndReceive: closed: TEST.Q1
2021/07/02 14:01:59.347 MQTest11B: testSendAndReceive: disconnected from MQA1

Regards,
Roger Lacroix
Capitalware Inc.

HPE NonStop, IBM i (OS/400), IBM MQ, IBM MQ Appliance, Java, Linux, macOS (Mac OS X), Open Source, Programming, Raspberry Pi, Unix, Windows Comments Off on Put 2 Messages with Unique CorrelId and Get 1 Message by CorrelId

Enhancement to MQ Auditor

Capitalware has an MQ solution called MQ Auditor.

MQ Auditor is a solution that allows a company to audit/track all MQ API calls performed by MQ applications that are connected to a queue manager.

I have enhanced MQ Auditor with a new keyword called ExcludePCFMessages. It has a default value of ‘N’. When it is set to ‘Y’, MQ Auditor will exclude messages with a MQMD format value of either MQADMIN or MQEVENT.

I have completed a wide variety of tests and everything looks good.

If anyone would like to test out the latest release then send the email to support@capitalware.com

Regards,
Roger Lacroix
Capitalware Inc.

Capitalware, IBM i (OS/400), IBM MQ, Linux, MQ Auditor, Unix, Windows Comments Off on Enhancement to MQ Auditor

Capitalware Product Advisory – MQ Auditor

A bug was discovered in Capitalware’s MQ Auditor related to PCF response message for the PCF MQCMD_INQUIRE_Q_NAMES command. The PCF response message to the MQCMD_INQUIRE_Q_NAMES command has 2 parts: list of queues and list of queue types. To format the queue types (i.e. local, alias, etc.), the code used a 1024 byte static array. The formatting of each queue type is a single digit number followed by a colon “:”. Hence, it means 2 bytes are used per queue type and that a maximum of 512 queues (1024/2) can be processed. If the PCF MQCMD_INQUIRE_Q_NAMES command requests data for more than 512 queues then MQ Auditor crashes and MQ generates an FDC.

I have updated the code to use a dynamic buffer rather than a static array.

Note: If you have excluded the “AMQ.*” temporary dynamic queues in the IniFile then MQ Auditor will not process PCF response messages. Hence, the issue will not affect you.
i.e.

UseExcludeQueues = Y           
ExcludeQueues = AMQ.*

To receive the latest release of MQ Auditor, please send an email to support@capitalware.com

Regards,
Roger Lacroix
Capitalware Inc.

Capitalware, IBM i (OS/400), IBM MQ, Linux, MQ Auditor, Unix, Windows Comments Off on Capitalware Product Advisory – MQ Auditor

IBM MQ Knowledge Center Icons

IBM keeps added more and more icons in the MQ Knowledge Center to highlight which platforms and/or versions that a particular feature is supported on. You can find a complete list and explanation of the icons on the following MQ Knowledge Center page:
https://www.ibm.com/docs/en/ibm-mq/latest?topic=mq-release-platform-icons-in-product-documentation

Regards,
Roger Lacroix
Capitalware Inc.

Education, HPE NonStop, IBM i (OS/400), IBM MQ, IBM MQ Appliance, Linux, macOS (Mac OS X), Unix, Windows, z/OS Comments Off on IBM MQ Knowledge Center Icons

IBM MQ Fix Pack 9.1.0.8 Released

IBM has just released Fix Pack 9.1.0.8 for IBM MQ V9.1 LTS:
https://www.ibm.com/support/pages/downloading-ibm-mq-9108

Regards,
Roger Lacroix
Capitalware Inc.

Fix Packs for MQ, IBM i (OS/400), IBM MQ, IBM MQ Appliance, Linux, Unix, Windows Comments Off on IBM MQ Fix Pack 9.1.0.8 Released

Microsoft OpenJDK Builds

I had to rub my eyes and read the announcement twice!! 🙂

Microsoft announces the general availability of Microsoft Build of OpenJDK:
https://devblogs.microsoft.com/java/announcing-general-availability-of-microsoft-build-of-openjdk/

Java at Microsoft spans from Azure to Minecraft, across SQL Server to Visual Studio Code, LinkedIn and beyond! We use more Java than one can imagine.

The Microsoft Build of OpenJDK is a new no-cost long-term supported distribution and Microsoft’s new way to collaborate and contribute to the Java ecosystem.

I downloaded JDK 11 from Microsoft, tested with a variety of MQ/Java programs this afternoon and everything worked as expected.

Microsoft is offering OpenJDK builds for Windows (of course), Linux and macOS (Mac OS X). There are early access builds for Linux on ARM64 and Windows on ARM64.

20 years later, Microsoft is fully supporting Java. Either the world is upside down or just gone mad. 🙂

Regards,
Roger Lacroix
Capitalware Inc.

Java, JMS, Linux, macOS (Mac OS X), Open Source, Programming, Windows Comments Off on Microsoft OpenJDK Builds

Java MQ Code to Clear a Queue of Messages

If you have done the following runmqsc command to clear a queue:

CLEAR QLOCAL( {QueueName} )

And you wanted to do the same thing via a program, here is a fully functioning Java MQ example that will connect to a remote queue manager, issue a PCF “Clear Queue” command, get the PCF response messages and output if the PCF command was successful or not. You can download the source code from here.

Note: The PCF “Clear Queue” command will fail if an application has the queue open.

For more information about the PCF “Clear Queue” command, go to MQ KnowLedge Center here.

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Hashtable;

import com.ibm.mq.MQException;
import com.ibm.mq.MQQueueManager;
import com.ibm.mq.constants.CMQC;
import com.ibm.mq.constants.CMQCFC;
import com.ibm.mq.constants.MQConstants;
import com.ibm.mq.headers.MQDataException;
import com.ibm.mq.headers.pcf.PCFMessage;
import com.ibm.mq.headers.pcf.PCFMessageAgent;

/**
 * Program Name
 *  MQClearQueue01
 *
 * Description
 *  This java class issues a PCF "Clear Q" command for a queue to delete all messages 
 *  in the queue of a remote queue manager.
 *    
 *  Note: The PCF "Clear Q" command will fail if an application has the queue open.
 *
 * Sample Command Line Parameters
 *  -m MQA1 -h 127.0.0.1 -p 1414 -c TEST.CHL -q TEST.Q1 -u UserID -x Password
 *
 * @author Roger Lacroix
 */
public class MQClearQueue01
{
   private static final SimpleDateFormat  LOGGER_TIMESTAMP = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS");

   private Hashtable<String,String> params;
   private Hashtable<String,Object> mqht;

   public MQClearQueue01()
   {
      super();
      params = new Hashtable<String,String>();
      mqht = new Hashtable<String,Object>();
   }

   /**
    * Make sure the required parameters are present.
    * @return true/false
    */
   private boolean allParamsPresent()
   {
      boolean b = params.containsKey("-h") && params.containsKey("-p") &&
                  params.containsKey("-c") && params.containsKey("-m") &&
                  params.containsKey("-q") &&
                  params.containsKey("-u") && params.containsKey("-x");
      if (b)
      {
         try
         {
            Integer.parseInt((String) params.get("-p"));
         }
         catch (NumberFormatException e)
         {
            b = false;
         }
      }

      return b;
   }

   /**
    * Extract the command-line parameters and initialize the MQ HashTable.
    * @param args
    * @throws IllegalArgumentException
    */
   private void init(String[] args) throws IllegalArgumentException
   {
      int port = 1414;
      if (args.length > 0 && (args.length % 2) == 0)
      {
         for (int i = 0; i < args.length; i += 2)
         {
            params.put(args[i], args[i + 1]);
         }
      }
      else
      {
         throw new IllegalArgumentException();
      }

      if (allParamsPresent())
      {
         try
         {
            port = Integer.parseInt((String) params.get("-p"));
         }
         catch (NumberFormatException e)
         {
            port = 1414;
         }

         mqht.put(CMQC.CHANNEL_PROPERTY, params.get("-c"));
         mqht.put(CMQC.HOST_NAME_PROPERTY, params.get("-h"));
         mqht.put(CMQC.PORT_PROPERTY, new Integer(port));
         mqht.put(CMQC.USER_ID_PROPERTY, params.get("-u"));
         mqht.put(CMQC.PASSWORD_PROPERTY, params.get("-x"));

         // I don't want to see MQ exceptions at the console.
         MQException.log = null;
      }
      else
      {
         throw new IllegalArgumentException();
      }
   }

   /**
    * Handle connecting to the queue manager, issuing PCF command then
    * looping through PCF response messages and disconnecting from
    * the queue manager.
    */
   private void doPCF()
   {
      MQQueueManager  qMgr   = null;
      PCFMessageAgent agent  = null;
      PCFMessage   request   = null;
      PCFMessage[] responses = null;
      String qMgrName  = (String) params.get("-m");
      String queueName = (String) params.get("-q");

      try
      {
         qMgr = new MQQueueManager(qMgrName, mqht);
         MQClearQueue01.logger("successfully connected to "+ qMgrName);

         agent = new PCFMessageAgent(qMgr);
         MQClearQueue01.logger("successfully created agent");

         // https://www.ibm.com/docs/en/ibm-mq/latest?topic=formats-clear-queue
         request = new PCFMessage(CMQCFC.MQCMD_CLEAR_Q);

         request.addParameter(CMQC.MQCA_Q_NAME, queueName);

         responses = agent.send(request);

         MQClearQueue01.logger("responses.length="+responses.length);

         for (int i = 0; i < responses.length; i++)
         {
            if ((responses[i]).getCompCode() == CMQC.MQCC_OK)
               MQClearQueue01.logger("Successfully cleared queue '"+queueName+"' of messages.");
            else
               MQClearQueue01.logger("Error: Failed to clear queue '"+queueName+"' of messages.");
         }
      }
      catch (MQException e)
      {
         MQClearQueue01.logger("CC=" +e.completionCode + " : RC=" + e.reasonCode + " [" + MQConstants.lookup(e.reasonCode, "MQRC_.*") + "]");
      }
      catch (IOException e)
      {
         MQClearQueue01.logger("IOException:" +e.getLocalizedMessage());
      }
      catch (MQDataException e)
      {
         if ( (e.completionCode == CMQC.MQCC_FAILED) && (e.reasonCode == CMQCFC.MQRCCF_OBJECT_OPEN) )
            MQClearQueue01.logger("Error: Failed to clear queue '"+queueName+"' of messages. An application has the queue open.");
         else
            MQClearQueue01.logger("CC=" +e.completionCode + " : RC=" + e.reasonCode + " [" + MQConstants.lookup(e.reasonCode, "MQRC_.*") + "]");
      }
      finally
      {
         try
         {
            if (agent != null)
            {
               agent.disconnect();
               MQClearQueue01.logger("disconnected from agent");
            }
         }
         catch (MQDataException e)
         {
            MQClearQueue01.logger("CC=" +e.completionCode + " : RC=" + e.reasonCode + " [" + MQConstants.lookup(e.reasonCode, "MQRC_.*") + "]");
         }

         try
         {
            if (qMgr != null)
            {
               qMgr.disconnect();
               MQClearQueue01.logger("disconnected from "+ qMgrName);
            }
         }
         catch (MQException e)
         {
            MQClearQueue01.logger("CC=" +e.completionCode + " : RC=" + e.reasonCode + " [" + MQConstants.lookup(e.reasonCode, "MQRC_.*") + "]");
         }
      }
   }

   /**
    * A simple logger method
    * @param data
    */
   public static void logger(String data)
   {
      String className = Thread.currentThread().getStackTrace()[2].getClassName();

      // Remove the package info.
      if ( (className != null) && (className.lastIndexOf('.') != -1) )
         className = className.substring(className.lastIndexOf('.')+1);

      System.out.println(LOGGER_TIMESTAMP.format(new Date())+" "+className+": "+Thread.currentThread().getStackTrace()[2].getMethodName()+": "+data);
   }

   public static void main(String[] args)
   {
      MQClearQueue01 mqcq = new MQClearQueue01();

      try
      {
         mqcq.init(args);
         mqcq.doPCF();
      }
      catch (IllegalArgumentException e)
      {
         MQClearQueue01.logger("Usage: java MQClearQueue01 -m QueueManagerName -h host -p port -c channel -q QueueName -u UserID -x Password");
         System.exit(1);
      }

      System.exit(0);
   }
}

Regards,
Roger Lacroix
Capitalware Inc.

HPE NonStop, IBM i (OS/400), IBM MQ, IBM MQ Appliance, Java, Linux, macOS (Mac OS X), Open Source, PCF, Programming, Raspberry Pi, Unix, Windows Comments Off on Java MQ Code to Clear a Queue of Messages

Updates to My ByteArray Class

Last year, I posted a blog item called Java’s Missing Byte Array Class regarding the missing ByteArray class in Java and I published my ByteArray class for all to use.

I have fixed a bug in the getBytes method and added 2 new methods:

  • public int convertToInt_BE()
  • public void replaceAndResize(byte[] oldByteArray, byte[] newByteArray)

I have generated the JavaDocs for the ByteArray class. I zipped up the JavaDocs, Test_ByteArray.java program and the ByteArray class. You can download the source code from here.

Simply copy the ByteArray class to your utility (or handler) package of your application and then start using it.

Regards,
Roger Lacroix
Capitalware Inc.

HPE NonStop, IBM i (OS/400), Java, JMS, Linux, macOS (Mac OS X), Open Source, Programming, Raspberry Pi, Unix, Windows, z/OS Comments Off on Updates to My ByteArray Class

Ubuntu 21.04 Released

Canonical has just released Ubuntu v21.04.
http://releases.ubuntu.com/21.04/

Super-fast, easy to use and free, the Ubuntu operating system powers millions of desktops, netbooks and servers around the world. Ubuntu does everything you need it to. It’ll work with your existing PC files, printers, cameras and MP3 players. And it comes with thousands of free apps.

Regards,
Roger Lacroix
Capitalware Inc.

Linux, Open Source, Operating Systems Comments Off on Ubuntu 21.04 Released