4
votes

I'm using the mq library of IBM to read messages from MQ queues. Now I need to retrieve the messageid of the message. I now it is in the message header under the name messageId. But this returns a byte[]. Now I need to change it to a readable string.

How can I transform the messageId from byte[] to string?

I tried a couple of conversions but non of them works:

new String(theMessage.messageId)
new String(theMessage.messageId, "UTF-8")
new String(theMessage.messageId, "UTF-16")
theMessage.messageId.toString()
3

3 Answers

7
votes

The messageId in the MQMD is represented as 24 bytes. If you know what platform these were generated on you may be able to find some meaning to portions of them by converting the bytes to characters in the character set of the queue manager where they were produced but is not recommended to rely on any data being conveyed in the messageID as character data since I have seen statements from IBM similar to "MsgId is generated by MQ in an IBM proprietary format and it may change at any time."

If you want represent them as a string you should represent them as a 48 character HEX string representing the 24 bytes.

Below is a sample function getHexString IBM has provided in a Technote that will perform this conversion for you. You would use it like this:

getHexString(theMessage.messageId)

The sample function below is from IBM MQ Technote "How to match correlation id's when request is made via JMS application and reply generated from base Java API"

public static String getHexString(byte[] b) throws Exception {
    String result = "";
    for (int i=0; i < b.length; i++) {
        result += Integer.toString( ( b[i] & 0xff ) + 0x100, 16).substring( 1 );
    }
    return result;
}

IBM Documents the format and uniqueness of the Queue Manager generated message IDs at the bottom of the Knowledge Center page "Reference>Developing applications reference>MQI applications reference>Data types used in the MQI>MQMD - Message descriptor>Fields>MsgId (MQBYTE24)"

A MsgId generated by the queue manager consists of a 4-byte product identifier (AMQ¬ or CSQ¬ in either ASCII or EBCDIC, where ¬ represents a blank character), followed by a product-specific implementation of a unique string. In IBM® MQ this contains the first 12 characters of the queue-manager name, and a value derived from the system clock. All queue managers that can intercommunicate must therefore have names that differ in the first 12 characters, in order to ensure that message identifiers are unique. The ability to generate a unique string also depends on the system clock not being changed backward. To eliminate the possibility of a message identifier generated by the queue manager duplicating one generated by the application, the application must avoid generating identifiers with initial characters in the range A through I in ASCII or EBCDIC (X'41' through X'49' and X'C1' through X'C9'). However, the application is not prevented from generating identifiers with initial characters in these ranges.

4
votes

The MQMD MessageId field is made up of both character and binary values. Therefore, the only way to properly represent the MessageId field as a String is by converting it to HEX representation.

You need to use my bytesToHex method:

String s = bytesToHex(theMessage.messageId);

Hence, s would look something like '414D51204D5141312020202020202020134CCD4020000B01'.

And the Java code for bytesToHex method is:

public static final String HEX_CHARS = "0123456789ABCDEF";

public static String bytesToHex(byte[] data)
{
   StringBuffer buf = new StringBuffer();
   for (int i = 0; i < data.length; i++)
      buf.append(byteToHex(data[i]));

   return buf.toString();
}

public static String byteToHex(byte data)
{
   int hi = (data & 0xF0) >> 4;
   int lo = (data & 0x0F);
   return "" + HEX_CHARS.charAt(hi) + HEX_CHARS.charAt(lo);
}

Updated July 23, 2020.

Here is a method to convert a HEX string to a byte array:

public static byte[] hexStringToByteArray(String inString)
{
   byte[] outBuffer = new byte[inString.length()/2];
   String hex;
   int xx;

   for (int i=0, j=0; j < (inString.length() - 1); i++, j+=2)
   {
      hex = inString.substring(j,j+2);
      xx = Integer.parseInt(hex.trim(), 16);
      if (xx < 0)
         xx += 256;

      outBuffer[i] = (byte)xx;
   }

   return outBuffer;
}
1
votes

I have successfully implemented the cycle of getting the MQ MessageId and converting it to a hexadecimal string for local storage and then converting it back to a byte[] when using it to query MQ again later, using the Apache Commons Codec thus:

import org.apache.commons.codec.binary.Hex;

String mqMessageId = Hex.encodeHexString(message.messageId);
message.messageId = Hex.decodeHex(mqMessageId.toCharArray());

I like this approach because it depends on a well maintained library from a well known and respected organization, and I do not have to maintain any additional methods.

None the less I would like to acknowledge Roger's comments above because they put me on the trail that lead to Apache Commons Codec.