IBM MQ V9.2.3 Announced

IBM has announced IBM MQ V9.2.3:
https://www.ibm.com/common/ssi/ShowDoc.wss?docURL=/common/ssi/rep_ca/1/877/ENUSZP21-0231/index.html

Highlights:

MQ 9.2.3:

  • – Streaming queues to make message data from existing flows available to Apache Kafka streaming applications, artificial intelligence (AI), and analytics applications, with zero impact to the existing applications or their messages and without a need for rearchitecting your message flows
  • – Broadened uniform cluster support for improved fault tolerance
  • – MQ Console remote queue manager support, enabling clients to manage their entire MQ estate
  • – Security enhancements
  • – A dead-letter queue (DLQ) handler included in the MQ client package to enable processing of messages on remote queue managers
  • – Additional channel attributes for Advanced Message Queueing Protocol (AMQP)
  • MQ Advanced 9.2.3:

  • – Native high availability (HA), delivered in 9.2.2 under an Early Access license, becomes generally available in 9.2.3. Native HA is for clients deploying container-based queue managers to IBM Cloud Pak® for Integration on Red Hat® OpenShift® using the MQ certified container.
  • – Replicated Data Queue Manager (RDQM) kernel module serviceability enhancements.
  • MQ Appliance 9.2.3 firmware:

  • – Streaming queues
  • – Broadened uniform cluster support
  • – Additional channel attributes for AMQP.
  • Planned availability for IBM MQ V9.2.3 is July 22, 2021 for Electronic software delivery.

    IBM MQ (aka WebSphere MQ) homepage
    https://www.ibm.com/products/mq

    Regards,
    Roger Lacroix
    Capitalware Inc.

    IBM MQ, IBM MQ Appliance, Linux, Unix, Windows, z/OS Comments Off on IBM MQ V9.2.3 Announced

    Time Warp – AIX and MQ v6

    Last week, a customer emailed an interesting request. They have 2 AIX hub servers running WebSphere MQ V6.0 and they want to determine what applications are using the 2 MQ hub servers, so that they can upgrade to a newer release of MQ. That made me clean my glasses because MQ v6 went out of support in September 2012 (almost 9 years ago).

    They asked if either MQ Auditor and/or MQ Channel Connection Inspector (MQCCI) could obtain the IP address of the incoming MQ connection. I said yes, both can get the IP address and if they didn’t want any other information (i.e. queue names, MQ headers, message data, etc) then MQCCI would be the best/easiest solution.

    Yesterday, I drove over to my offsite facility to search for backup DVDs of MQ v6. I have 2 boxes of DVD backups, it took about 30 minutes to find 2 backup DVDs with MQ v6 for AIX. I figured at least one of them should be ok.

    So, I created a reservation on IBM’s CECC portal for AIX v7.1, uploaded and installed MQ v6 and, of course, I had to install IBM’s XLC compiler because for some strange reason, CECC does not include it for AIX (but they do for Linux on Power and IBM i).

    MQCCI was created and launched in May 2018, long, long after MQ v6 went out of support. So, when I tried to do a build, I got several errors about unknown defines (i.e. MQCD_VERSION_9). So, I added a bunch of “#ifdefs” to the code and bam, it compiled and linked successfully.

    I created a queue manager and I did a bunch of tests with MQCCI. Everything went smoothly, so I let the customer know that I have a build for them to test out.

    At this point in my life, I’m never surprised by the odd requests I get. 🙂

    Regards,
    Roger Lacroix
    Capitalware Inc.

    Capitalware, IBM MQ, MQ Auditor, MQ Channel Connection Inspector, Unix Comments Off on Time Warp – AIX and MQ v6

    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