Updated Request & Reply JMS/MQRFH2 Messaging Scenario

The person on StackOverflow made a comment that I was using a static string as my reply message. Humm. Some times people cannot see the forest for the trees.

So, I have updated my sample Java/MQ (i.e. MQTest77.java) in this posting to do the following:

  • Connect to a remote queue manager
  • Open an input queue, get a message, close the queue
  • Extract the MQRFH2 variables
  • Take a checksum of the incoming message payload
  • Use the checksum as a seed value for the random number generator
  • Generate Powerball numbers and a create reply payload with that information
  • Open the output queue
  • Create a new reply message with the extracted RFH2 values and the reply payload then send the reply message
  • Close the output queue and disconnect from the queue manager

There you go, nothing static about the reply text in the reply message.

Although, this did feel like doing a lab assignment for a Java programming course. 🙂

And of course, if anyone wins the Powerball lottery or any other lottery with my code, I DO expect a 10% cut of the winnings!! 🙂

Regards,
Roger Lacroix
Capitalware Inc.

IBM i (OS/400), IBM MQ, Linux, macOS (Mac OS X), Programming, Unix, Windows, z/OS Comments Off on Updated Request & Reply JMS/MQRFH2 Messaging Scenario

Inhibit/Allow the Get & Put Actions of a Queue

On StackOverflow, someone asked a question about inhibiting get actions against a queue.

Here is a simple Java/MQ program that will set inhibit or allow for the GET and PUT attributes of a queue.

You can download the source code from here.

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

import com.ibm.mq.MQException;
import com.ibm.mq.MQQueue;
import com.ibm.mq.MQQueueManager;
import com.ibm.mq.constants.CMQC;

/**
 * Program Name
 *  MQTest15
 *
 * Description
 *  This java class will connect to a remote queue manager with the
 *  MQ setting stored in a HashTable and set the queue's get & put attributes to 
 *  either inhibit or allow.
 *
 * Sample Command Line Parameters
 *  -m MQA1 -h 127.0.0.1 -p 1414 -c TEST.CHL -q TEST.Q1 -i 0/1 -o 0/1 -u UserID -x Password
 *
 * @author Roger Lacroix
 */
public class MQTest15
{
   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;
   private boolean getInhibit = false;
   private boolean putInhibit = false;

   /**
    * The constructor
    */
   public MQTest15()
   {
      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("-i") && params.containsKey("-o") &&
                  params.containsKey("-u") && params.containsKey("-x");
      if (b)
      {
         try
         {
            Integer.parseInt((String) params.get("-p"));
            Integer.parseInt((String) params.get("-i"));
            Integer.parseInt((String) params.get("-o"));
         }
         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;
         }

         try
         {
            int temp = Integer.parseInt((String) params.get("-i"));
            if (temp == 1)
               getInhibit = true;
         }
         catch (NumberFormatException e)
         {
            getInhibit = false;
         }

         try
         {
            int temp = Integer.parseInt((String) params.get("-o"));
            if (temp == 1)
               putInhibit = true;
         }
         catch (NumberFormatException e)
         {
            putInhibit = false;
         }
         
         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, set the queue's get & put attributes with 
    * either inhibit or allow, close queue and disconnect.
    *
    */
   private void doIt()
   {
      MQQueueManager qMgr = null;
      MQQueue queue = null;
      int openOptions = CMQC.MQOO_SET + CMQC.MQOO_FAIL_IF_QUIESCING;

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

         queue = qMgr.accessQueue(outputQName, openOptions);
         MQTest15.logger("successfully opened "+ outputQName);

         if (getInhibit)
         {
            queue.setInhibitGet(CMQC.MQQA_GET_INHIBITED);
            MQTest15.logger("Set inhibited get on queue: " +outputQName);
         }
         else
         {
            queue.setInhibitGet(CMQC.MQQA_GET_ALLOWED);
            MQTest15.logger("Set allowed get on queue: " +outputQName);
         }

         if (putInhibit)
         {
            queue.setInhibitPut(CMQC.MQQA_PUT_INHIBITED);
            MQTest15.logger("Set inhibited put on queue: " +outputQName);
         }
         else
         {
            queue.setInhibitPut(CMQC.MQQA_PUT_ALLOWED);
            MQTest15.logger("Set allowed put on queue: " +outputQName);
         }
      }
      catch (MQException e)
      {
         MQTest15.logger("CC=" +e.completionCode + " : RC=" + e.reasonCode);
      }
      finally
      {
         try
         {
            if (queue != null)
            {
               queue.close();
               MQTest15.logger("closed: "+ outputQName);
            }
         }
         catch (MQException e)
         {
            MQTest15.logger("CC=" +e.completionCode + " : RC=" + e.reasonCode);
         }
         try
         {
            if (qMgr != null)
            {
               qMgr.disconnect();
               MQTest15.logger("disconnected from "+ qMgrName);
            }
         }
         catch (MQException e)
         {
            MQTest15.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)
   {
      MQTest15 write = new MQTest15();

      try
      {
         write.init(args);
         write.doIt();
      }
      catch (IllegalArgumentException e)
      {
         MQTest15.logger("Usage: java MQTest15 -m QueueManagerName -h host -p port -c channel -q QueueName -i 0/1 -o 0/1 -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, Programming, Raspberry Pi, Unix, Windows, z/OS Comments Off on Inhibit/Allow the Get & Put Actions of a Queue

Request & Reply JMS/MQRFH2 Messaging Scenario

Sometimes people over think a problem and write a complicated solution when really the solution is no different than any other Request/Reply scenario (use case).

On StackOverflow, someone asked a question about sending a reply JMS/MQRFH2 message using the RFH2 header and folder values from the original request message.

The code will copy the RFH2 header and folders from the request message to the reply message.

I did 3 extra things:

(1) Updated the destination queue name in the “jms” folder.

(2) Set the reply message’s Correlation Id to contain the request message’s Message Id.

(3) Used the checksum of the request message’s payload as a seed value for the random number generator that generated Powerball numbers for the reply message’s payload.

You can download the source code from here.

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Hashtable;
import java.util.Random;
import java.util.zip.CRC32;
import java.util.zip.Checksum;

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

/**
 * Program Name
 *  MQTest77
 *
 * Description
 *  This java class will connect to a remote queue manager with the MQ setting stored in a HashTable.
 *  It will get a request MQRFH2 message from an input queue and then send a reply MQRFH2 message.
 *
 * Sample Command Line Parameters
 *  -m MQA1 -h 127.0.0.1 -p 1414 -c TEST.CHL -q INPUT.Q1 -o OUTPUT.Q1 -u UserID -x Password
 *
 * @author Roger Lacroix
 */
public class MQTest77
{
   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 inputQName;
   private String outputQName;

   /**
    * The constructor
    */
   public MQTest77()
   {
      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("-o") &&
                  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");
         inputQName = (String) params.get("-q");
         outputQName = (String) params.get("-o");

         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();
      }
   }

   /**
    * This method will do the following:
    * 1. Connect 
    * 2. Open input queue, get a request message, close queue 
    * 3. Open output queue, send a reply message, close queue
    * 4. Disconnect.
    */
   private void doIt()
   {
      MQQueueManager qMgr = null;
      
      try
      {
         qMgr = new MQQueueManager(qMgrName, mqht);
         MQTest77.logger("successfully connected to "+ qMgrName);
         
         MQMessage requestMsg = getRequestMsg(qMgr);
         
         if (requestMsg != null)
         {
            ResponseStuff rs = doSomeLogic(requestMsg);
            if (rs != null)
               sendReplyMsg(qMgr, rs);
         }
      }
      catch (MQException e)
      {
         MQTest77.logger("CC=" +e.completionCode + " : RC=" + e.reasonCode);
      }
      finally
      {
         try
         {
            if (qMgr != null)
            {
               qMgr.disconnect();
               MQTest77.logger("disconnected from "+ qMgrName);
            }
         }
         catch (MQException e)
         {
            MQTest77.logger("CC=" +e.completionCode + " : RC=" + e.reasonCode);
         }
      }
   }

   /**
    * Open the input queue, get a message, close queue.
    * @param qMgr
    * @return requestMsg
    */
   private MQMessage getRequestMsg(MQQueueManager qMgr)
   {
      MQQueue queue = null;
      int openOptions = CMQC.MQOO_INPUT_AS_Q_DEF + CMQC.MQOO_INQUIRE + CMQC.MQOO_FAIL_IF_QUIESCING;
      MQGetMessageOptions gmo = new MQGetMessageOptions();
      gmo.options = CMQC.MQGMO_PROPERTIES_FORCE_MQRFH2 + CMQC.MQGMO_FAIL_IF_QUIESCING + CMQC.MQGMO_NO_WAIT;
      MQMessage requestMsg = null;
      
      try
      {
         queue = qMgr.accessQueue(inputQName, openOptions);
         MQTest77.logger("successfully opened "+ inputQName);

         requestMsg = new MQMessage();
         requestMsg.messageId = CMQC.MQMI_NONE;
         requestMsg.correlationId = CMQC.MQCI_NONE;

         // get the message on the queue
         queue.get(requestMsg, gmo);
      }
      catch (MQException e)
      {
         MQTest77.logger("CC=" +e.completionCode + " : RC=" + e.reasonCode);
         
         if ( (e.completionCode == CMQC.MQCC_FAILED) && (e.reasonCode == CMQC.MQRC_NO_MSG_AVAILABLE) )
         {
            MQTest77.logger("No messages on the queue: " + inputQName);
            requestMsg = null;  // no message on queue
         }
      }
      finally
      {
         try
         {
            if (queue != null)
            {
               queue.close();
               MQTest77.logger("closed input: "+ inputQName);
            }
         }
         catch (MQException e)
         {
            MQTest77.logger("CC=" +e.completionCode + " : RC=" + e.reasonCode);
         }
      }
      
      return requestMsg;
   }

   /**
    * This is the method where you would normally do business logic stuff.
    * This code will we will take the checksum of the request
    * message and use the value as a seed to the random number generator
    * to that will generate numbers for the next PowerBall lottery!!
    * 
    * @param requestMsg
    */
   private ResponseStuff doSomeLogic(MQMessage requestMsg)
   {
      SimpleDateFormat date = new SimpleDateFormat("yyyy/MM/dd");
      ResponseStuff rs = new ResponseStuff();
      byte[] msgInBytes;
      
      try
      {
         rs.setRequestMsgId(requestMsg.messageId);
         
         requestMsg.seek(0);  // Important: put the cursor at the beginning of the message.
         MQRFH2 requestRFH2 = new MQRFH2(requestMsg);

         // Save the RFH2 Values
         rs.setRfh2(extractRFH2Values(requestRFH2));
         
         if (CMQC.MQFMT_STRING.equals(requestRFH2.getFormat()))
         {
            String msgStr = requestMsg.readStringOfByteLength(requestMsg.getDataLength());
            msgInBytes = msgStr.getBytes();
         }
         else
         {
            msgInBytes = new byte[requestMsg.getDataLength()];
            requestMsg.readFully(msgInBytes);
         }
         
         /**
          * Now do something with the request message payload.
          * i.e. process msgStr
          * 
          * But we are going to generate PowerBall lottery numbers.
          * 
          */

         /* Apply checksum to the message payload */
         Checksum checksum = new CRC32();
         checksum.update(msgInBytes, 0, msgInBytes.length);

         // get the current checksum value & use it as a seed value.
         Random random = new Random(checksum.getValue());

         StringBuffer sb = new StringBuffer();
         sb.append("For today, ");
         sb.append(date.format(new Date()));
         sb.append(" your Powerball numbers are: ");
         for (int i=0; i < 4; i++)
            sb.append(random.nextInt(69)+", ");
         sb.append(random.nextInt(69)+" ");
         sb.append("and your power ball is ");
         sb.append(random.nextInt(26));
         
         rs.setResponseText(sb.toString());
      }
      catch (IOException e)
      {
         MQTest77.logger("IOException:" +e.getLocalizedMessage());
      }
      catch (MQDataException e)
      {
         MQTest77.logger("CC=" +e.completionCode + " : RC=" + e.reasonCode);
      }
      
      return rs;
   }

   /**
    * Extract the MQRFH2 values we will need.
    * @param requestRFH2
    * @return rfh2
    */
   private MQRFH2 extractRFH2Values(MQRFH2 requestRFH2)
   {
      MQRFH2 replyRFH2 = null;
      
      try
      {
         // Set the RFH2 Values
         replyRFH2 = new MQRFH2();
         replyRFH2.setEncoding(requestRFH2.getEncoding());
         replyRFH2.setCodedCharSetId(requestRFH2.getCodedCharSetId());
         replyRFH2.setFormat(requestRFH2.getFormat());
         replyRFH2.setFlags(requestRFH2.getFlags());
         replyRFH2.setNameValueCCSID(requestRFH2.getNameValueCCSID());

         String[] folders = requestRFH2.getFolderStrings();
         for (int i=0; i < folders.length; i++)
         {
            if ( (folders[i].startsWith("<jms>")) && (folders[i].contains("<Dst>")) && (folders[i].contains("</Dst>")) )
            {
               int prefix = folders[i].indexOf("<Dst>");
               int suffix = folders[i].indexOf("</Dst>");
               // Set the destination to the output queue name.
               folders[i] = folders[i].substring(0, prefix) + "<Dst>queue:///"+outputQName+"</Dst>" + folders[i].substring(suffix+6);
               break;
            }
         }
         replyRFH2.setFolderStrings(folders);
      }
      catch (IOException e)
      {
         MQTest77.logger("IOException:" +e.getLocalizedMessage());
      }
      
      return replyRFH2;
   }


   /**
    * Open output queue, write a message, close queue.
    * @param qMgr
    * @param requestMsg
    */
   private void sendReplyMsg(MQQueueManager qMgr, ResponseStuff rs)
   {
      MQQueue queue = null;
      int openOptions = CMQC.MQOO_OUTPUT + CMQC.MQOO_FAIL_IF_QUIESCING;
      MQPutMessageOptions pmo = new MQPutMessageOptions();

      try
      {
         queue = qMgr.accessQueue(outputQName, openOptions);
         MQTest77.logger("successfully opened "+ outputQName);

         MQMessage replymsg = new MQMessage();

         // Set the RFH2 Values
         MQRFH2 replyRFH2 = new MQRFH2();
         replyRFH2.setEncoding(rs.getRfh2().getEncoding());
         replyRFH2.setCodedCharSetId(rs.getRfh2().getCodedCharSetId());
         replyRFH2.setFormat(rs.getRfh2().getFormat());
         replyRFH2.setFlags(rs.getRfh2().getFlags());
         replyRFH2.setNameValueCCSID(rs.getRfh2().getNameValueCCSID());
         replyRFH2.setFolderStrings(rs.getRfh2().getFolderStrings());
         
         // Set the MQRFH2 structure to the message
         replyRFH2.write(replymsg);

         // Write message data
         replymsg.writeString(rs.getResponseText());

         // Set MQMD values
         replymsg.messageId = CMQC.MQMI_NONE;
         /* set the reply's Correl Id to be the request message's Msg id */  
         replymsg.correlationId = rs.getRequestMsgId();
         replymsg.messageType = CMQC.MQMT_DATAGRAM;
         
         // IMPORTANT: Set the format to MQRFH2 aka JMS Message.
         replymsg.format = CMQC.MQFMT_RF_HEADER_2;

         // put the message on the queue
         queue.put(replymsg, pmo);
         MQTest77.logger("Message Data>>>" + rs.getResponseText());
      }
      catch (MQException e)
      {
         MQTest77.logger("CC=" +e.completionCode + " : RC=" + e.reasonCode);
      }
      catch (IOException e)
      {
         MQTest77.logger("IOException:" +e.getLocalizedMessage());
      }
      finally
      {
         try
         {
            if (queue != null)
            {
               queue.close();
               MQTest77.logger("closed output: "+ outputQName);
            }
         }
         catch (MQException e)
         {
            MQTest77.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)
   {
      MQTest77 write = new MQTest77();

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

      System.exit(0);
   }
   
   /**
    * This class will hold the information to be used in the reply message.
    */
   class ResponseStuff
   {
      private MQRFH2 rfh2;
      private String responseText;
      private byte[] requestMsgId;

      /**
       * The constructor
       */
      public ResponseStuff()
      {
         super();
         rfh2 = new MQRFH2();
      }

      public MQRFH2 getRfh2()
      {
         return rfh2;
      }

      public void setRfh2(MQRFH2 rfh2)
      {
         this.rfh2 = rfh2;
      }

      public String getResponseText()
      {
         return responseText;
      }

      public void setResponseText(String responseText)
      {
         this.responseText = responseText;
      }

      public byte[] getRequestMsgId()
      {
         return requestMsgId;
      }

      public void setRequestMsgId(byte[] requestMsgId)
      {
         this.requestMsgId = requestMsgId;
      }

   }
}

Regards,
Roger Lacroix
Capitalware Inc.

HPE NonStop, IBM i (OS/400), IBM MQ, IBM MQ Appliance, Java, JMS, Linux, macOS (Mac OS X), Open Source, Programming, Raspberry Pi, Unix, Windows, z/OS Comments Off on Request & Reply JMS/MQRFH2 Messaging Scenario

IBM announces WebSphere MQ V5.3.1 Fix Pack 16 for HPE NonStop

IBM has just released Fix Pack 5.3.1.16 for WebSphere MQ for HPE NonStop:
https://www.ibm.com/support/docview.wss?uid=swg1IT29752

Regards,
Roger Lacroix
Capitalware Inc.

Fix Packs for MQ, HPE NonStop, IBM MQ Comments Off on IBM announces WebSphere MQ V5.3.1 Fix Pack 16 for HPE NonStop

Capitalware Product Advisory

A bug was discovered in Capitalware’s framework subroutine for removing trailing blanks (spaces) from a variable. If the contents of the variable contained only blanks then the code miscalculated the length by 1. This bug affects all commercial and “Licensed as Free” products.

    Commercial Products for distributed platforms affected:

  • MQ Auditor
  • MQ Authenticate User Security Exit
  • MQ Channel Connection Inspector
  • MQ Channel Encryption
  • MQ Channel Throttler
  • MQ Message Encryption
  • MQ Message Replication
  • MQ Standard Security Exit
    Commercial Products for z/OS affected:

  • MQ Authenticate User Security Exit for z/OS
  • MQ Channel Connection Inspector for z/OS
  • MQ Channel Encryption for z/OS
  • MQ Standard Security Exit for z/OS

To receive updates to the commercial products, please send an email to support@capitalware.com

    Licensed as Free Products affected:

  • MQ Channel Auto Creation Manager
  • MQ Set UserID
  • Client-side Security Exit for Depository Trust Clearing Corporation
  • MQ Channel Auto Creation Manager for z/OS
  • MQ Set UserID for z/OS
  • Client-side Security Exit for Depository Trust Clearing Corporation for z/OS

The updates for the “Licensed as Free” products have been posted to Capitalware’s web site, so you can download them immediately.

Regards,
Roger Lacroix
Capitalware Inc.

Capitalware, IBM i (OS/400), Linux, MQ Auditor, MQ Authenticate User Security Exit, MQ Channel Connection Inspector, MQ Channel Encryption, MQ Channel Throttler, MQ Enterprise Security Suite, MQ Message Encryption, MQ Message Replication, MQ Standard Security Exit, Unix, Windows, z/OS Comments Off on Capitalware Product Advisory

IBM MQ Fix Pack 8.0.0.14 Released

IBM has just released Fix Pack 8.0.0.14 for IBM MQ
https://www.ibm.com/support/pages/node/1282120

Regards,
Roger Lacroix
Capitalware Inc.

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

SQLite v3.31.0 Released

D. Richard Hipp has just released SQLite v3.31.0.
http://www.sqlite.org/news.html

SQLite is a software library that implements a self-contained, serverless, zero-configuration, transactional SQL database engine. SQLite is the most widely deployed SQL database engine in the world. The source code for SQLite is in the public domain.

Regards,
Roger Lacroix
Capitalware Inc.

C, Database, IBM i (OS/400), Linux, macOS (Mac OS X), Open Source, Programming, Unix, Windows, z/OS Comments Off on SQLite v3.31.0 Released

IBM MQ and C# .NET Compatibility

Today, I went to build (compile) and run a C# .NET/MQ sample that I have which worked perfectly before and I got the following error when I ran it:

Unhandled Exception: System.IO.FileNotFoundException: Could not load file or assembly 'amqmdnet, Version=9.1.3.0, Culture=neutral, PublicKeyToken=dd3cb1c9aae9ec97' or one of its dependencies. The system cannot find the file specified.
File name: 'amqmdnet, Version=9.1.3.0, Culture=neutral, PublicKeyToken=dd3cb1c9aae9ec97'
   at MQTest71.MQTest71.Main(String[] args)

I compiled it again. No errors and it successfully generated the executable. Weird. When I ran it, I got the above error message.

I searched the internet and didn’t find a specific hit but found several pages that talked about creating a .NET Framework 4.0 application that references a .NET Standard 2.0 library.

This got me thinking about the C# compiler I was using versus the .NET runtime that was being used. The batch file to do the build was using the “csc.exe” (compiler) from .NET v2.0.

I did a quick of my C: drive and found that I had 3 .NET frameworks installed: v2.0, v3.5. and v4.6.1.

Note: Under “Programs and Features“, it shows I have “Microsoft .NET Framework 4.6.1 SDK” installed. The path to the compiler is C:\Windows\Microsoft.NET\Framework\v4.0.30319\. If you want to read about Microsoft’s .NET odd naming standards, you can read the article here. I do not have any entries for .NET v2.0 or .NET v3.5 (more odd behavior by Microsoft).

So, I updated my build batch file to use the compiler from .NET framework v4.6.1 and compiled my simple C# .NET/MQ program. It compiled fine and of course, it ran perfectly fine.

I know I used the .NET framework v2.0 with MQ v8.0 and it worked fine, so I decided to check out IBM MQ’s system requirements for software compilers. Here’s what I found for IBM MQ and the officially supported releases of .NET:

  • IBM MQ 8.0 supports .NET 3.5, 4.0 & 4.5
  • IBM MQ 9.0 supports .NET 3.5, 4.0, 4.5 & 4.6
  • IBM MQ 9.1 supports .NET 4.5.1, 4.6 & 4.7

Regards,
Roger Lacroix
Capitalware Inc.

.NET, C#, Programming, Windows Comments Off on IBM MQ and C# .NET Compatibility

JMS/MQRFH2 Message Properties in “other” Folder

On StackOverflow, someone asked about having an “other” folder (not “usr”) with an MQRFH2 (aka JMS) message.

That day, I played around with a MQ/Java program that created various folders in an MQRFH2 message.
i.e.

MQMessage sendmsg = new MQMessage();
MQRFH2 rfh2 = new MQRFH2();
rfh2.setEncoding(CMQC.MQENC_NATIVE);
rfh2.setCodedCharSetId(CMQC.MQCCSI_INHERIT);
rfh2.setFormat(CMQC.MQFMT_STRING);
rfh2.setFlags(0);
rfh2.setNameValueCCSID(1208);
// type of JMS message
rfh2.setFieldValue("mcd", "Msd", "jms_text");
rfh2.setFieldValue("jms", "Dst", "queue:///"+outputQName);
// set named properties in the "usr" folder
rfh2.setFieldValue("usr", "SomeNum", 123);
rfh2.setFieldValue("usr", "SomeText", "TEST");
// set named properties in the "other" folder
rfh2.setFieldValue("other", "thingA", 789);
rfh2.setFieldValue("other", "thingB", "XYZ");
// Set the MQRFH2 structure to the message
rfh2.write(sendmsg);
sendmsg.writeString("This is a test message.");
// IMPORTANT: Set the format to MQRFH2 aka JMS Message.
sendmsg.format = CMQC.MQFMT_RF_HEADER_2;
// put the message on the queue
queue.put(sendmsg, pmo);

When I ran a MQ/JMS program to retrieve the messages, the MQ/JMS programs simply ignore all folders outside of ‘mcd’, ‘jms’ and ‘usr’.

QueueReceiver receiver = session.createReceiver(myQ);
Message inMessage = receiver.receive();
String thing = inMessage.getStringProperty("other.thingB");
System.out.println("   thing="+thing);

Enumeration<String> props = inMessage.getPropertyNames();
if (props != null)
{
   while (props.hasMoreElements())
   {
      String propName = props.nextElement();
      Object o = inMessage.getObjectProperty(propName);
      System.out.println("   Name="+propName+" : Value="+o);
   }
}

Any named properties in the “other” folder are simply ignored by the JMS framework.

If you are wondering about C programs, yes, they can get access named properties in the “other” folder. When using the MQINQMP API call, there is no parameter for the folder name, so you code it as a qualifier to the name keyword. i.e. “other.thingB”

MQCHARV  inqname = {(MQPTR)(char*)"other.thingB", 0, 0, 12, MQCCSI_APPL};
impo.Options = MQIMPO_INQ_NEXT;
impo.ReturnedName.VSPtr = name;
impo.ReturnedName.VSBufSize = sizeof(name)-1;
type = MQTYPE_STRING;
MQINQMP(Hcon, Hmsg, &impo, &inqname, &pd, &type, sizeof(value)-1, value, &datalen, &CompCode, &Reason );
printf("property name <%s> value <%s>\n", name, value);

So, if you want to use a folder called “other”, that is fine, so long as you don’t want the information to be used by a JMS/MQ program, otherwise, you need to put the name/value properties in the ‘usr’ folder.

Regards,
Roger Lacroix
Capitalware Inc.

C, IBM i (OS/400), IBM MQ, Java, JMS, Linux, macOS (Mac OS X), Programming, Unix, Windows, z/OS Comments Off on JMS/MQRFH2 Message Properties in “other” Folder

Merry Christmas and Happy New Year

I would like to wish everyone a Merry Christmas, Happy Hanukkah, Happy Kwanzaa, etc… and a Happy New Year. 🙂

Regards,
Roger Lacroix
Capitalware Inc.

General 1 Comment