Decoding MQI constants

On Twitter, Michael asked: “any logic or hints on how to interpret the PCF parameter names returned as multiples from the com.ibm.mq.headers.pcf?” Which is a very good question but a proper answer is far too long to type there. There are several different ways that you can approach the problem, depending on what you are trying to do. So this post talks about decoding MQI constants.

The MQConstants.lookup() method in the MQ Java classes knows about all of the definitions in the MQI. Or rather, it discovers all the definitions for itself as it works via reflection. And when you call it, it returns all of the definitions that match a regular expression pattern. For example: MQConstants.lookup("MQRC_.*",2035) returns MQRC_NOT_AUTHORIZED.

First know your prefix

It’s important when working with decoding that you know which MQI prefix you use to get the value and where different pieces might apply. Most constants are defined such that the first part of the name defines a unique range. The original tweet asked about

Parameter : 1 MQIA_APPL_TYPE/MQIAMO_MONITOR_FLAGS_OBJNAME/MQIAMO_MONITOR_UNIT

which suggests that the program calling the lookup method was using a prefix of MQIA instead of MQIA_. The MQIA and MQIAMO types of number would be used in different circumstances, and appear in different types or areas of a message.

Some of the prefixes are managed so that even combined they have a unique set of values. The MQIA, MQCA, MQBA are part of a broad set that do not duplicate values across the entire class. That allows us to write code to find an attribute name like

s = MQConstants.lookup("MQIA_.*, v);
if (s == null) s = MQConstants.lookup("MQCA_.*,v);
if (s == null) s = MQConstants.lookup("MQBA_.*,v);
...

On the other hand, there are some groups of constants where the uniqueness is applied to subsets. The prefix in these cases comes from the combination of the first two elements of the definition. For example, the MQTA_PUB range overlaps with the MQTA_SUB range. There’s no reliable way to know how these ranges have been allocated, you can only deal with it by testing and reviewing output.

Simplest answer

The mqidecode program that’s part of SupportPac MS0P is a very simple wrapper around that method. It automatically adds _ and the essential .* to the given prefix parameter:

$ mqidecode -p MQPL -v 1 -e
MQPL_MVS/MQPL_OS390/MQPL_ZOS

You can see that here we have three “Platform” definitions for the same value, added as the branding changed. The Java method returns all of them in a single string but with ‘/’ separators. This particular program does show one way of answering Michael’s question. It prints out all of the values returned, with just one additional filter. If there is a multi-value response, then it removes any element that I thought would not be particularly helpful:

if (e.contains("_VERSION_") || e.endsWith("_VERSION"))
continue;
else if (e.endsWith("_LAST") || e.endsWith("_FIRST"))
continue;
else { ...
Bitfields

The mqidecode program can also deal with converting a value into its constituent bitfields. Typically an Options field in one of the MQI structures will be an MQLONG (32-bit) integer, with several flags combined into a single number. For example, we can interpret OpenOptions (MQOO) values, including some unused bits. The number (not this specific example as it’s got conflicting settings) might have come from looking at Activity Trace events:

$ mqidecode -p MQOO -v 0xffffff -m
 MQOO_INPUT_AS_Q_DEF (0x00000001)
 MQOO_INPUT_SHARED (0x00000002)
 MQOO_INPUT_EXCLUSIVE (0x00000004)
 MQOO_BROWSE (0x00000008)
 MQOO_OUTPUT (0x00000010)
 MQOO_INQUIRE (0x00000020)
 MQOO_SET (0x00000040)
 MQOO_SAVE_ALL_CONTEXT (0x00000080)
 MQOO_PASS_IDENTITY_CONTEXT (0x00000100)
 MQOO_PASS_ALL_CONTEXT (0x00000200)
 MQOO_SET_IDENTITY_CONTEXT (0x00000400)
 MQOO_SET_ALL_CONTEXT (0x00000800)
 MQOO_ALTERNATE_USER_AUTHORITY (0x00001000)
 MQOO_FAIL_IF_QUIESCING (0x00002000)
 MQOO_BIND_ON_OPEN (0x00004000)
 MQOO_BIND_NOT_FIXED (0x00008000)
 MQOO_RESOLVE_NAMES (0x00010000)
 MQOO_CO_OP (0x00020000)
 MQOO_RESOLVE_LOCAL_Q/MQOO_RESOLVE_LOCAL_TOPIC (0x00040000)
 MQOO_NO_READ_AHEAD (0x00080000)
 MQOO_READ_AHEAD (0x00100000)
 MQOO_NO_MULTICAST (0x00200000)
 MQOO_BIND_ON_GROUP (0x00400000)
  (0x00800000)

A slightly more sophisticated approach

Also in SupportPac MS0P are a bunch of plugins for Explorer that try to format most MQ events. Here, the intention is to make the values more readable, rather than correspond exactly to the spelling of the original definition. I do the same kind of removal as in mqidecode, but also have some explicit replacements. For example, _Q gets replaced by _QUEUE. Oddly enough, I can see that I had done something for the exact example given in the question:

if (p.startsWith("MQIAMO") && p.contains("MONITOR"))
  pieces[i] = null;

I guess that I knew that those definitions were irrelevant in the context where they might be returned. I can also see another replacement to aid readability (the mixture of spaces and underscores was deliberate, based on the phase of decoding I’d reached during the formatting):

if (s.equals("Mqca_Base Object Name/mqca Base Q Name"))
  s = "Mqca_Base Object Name";

The C solution

For non-Java programmers, there is an alternative way of getting the decoded values which I wrote about back in 2015. The cmqstrc.h header file contains mapping functions corresponding to the Java lookup method, one function per prefix.

Generating the definItions

Probably the most pertinent part of this header file in regard to the twitter question is how it gets created. The MQ product source code has all the definitions in a single set of files that is used during the build process to create all the header file or copybook variations for different languages (C, Cobol, RPG etc) and different platforms. So we only have to update a single place when a new constant is added to the specification, and it ensures consistency.

When I added the decoder header file to MQ in version 8, I did it by adding a new component to the generation tool. From the master files, it knows what data to print and in what format. I had to ensure there were no duplicates in the various generated blocks as it uses switch statements and those require uniqueness. Anything using the header file will simply not compile if there are two branches that have the same value. So I made choices – where there were multiple definitions for the same value in a given range, I picked my favourite. What I thought would be most useful or relevant. For the mqidecode example earlier, which tries to print the platform value, you will not find MQPL_MVS in the MQPL_STR function in the published C decoder file. There is only MQPL_ZOS to match that number. Though the other variations do appear in the full list structures MQI_BY_VALUE_STR and MQI_BY_NAME_STR later in the file.

The generation tool has quite a few of these hardcoded choices, along with some other rules. Such as where to use two parts of the definition as the set (prefix) name. Or some definitions that you would never expect to see in program output. Or which cannot be usefully decoded. This is a fragment from the generator program (false means that the constant will not appear in cmqstrc.h):

if (s.equals("MQTYPE_LONG")) {rc = false;}
if (s.startsWith("MQXR2_") && !s.equals("MQXR2_DEFAULT_CONTINUATION") && (v == 0)) {rc = false;}
if (s.equals("MQZCI_DEFAULT")) {rc = false;}

A lot of these rules came from iteratively trying them and looking at the output until the code compiled and I was happy with the output. There are other things I’d like to do with this header file, but it already fulfills its primary requirement.

Using the definitions

One example of where the C definitions are used is in the amqsevt sample program. It knows from the context which prefixes are relevant and will do the decoding. It still does some additional reformatting on the definition for readability, such as changing Q to QUEUE. You can see this processing in the source file. When attributes need to be converted to a JSON format, then this is done very simply after those “English” replacements are done – remove underscores and convert the first letter of the elements so it comes out in camelCase. This simple heuristic means that the JSON names are not guaranteed to be identical to the names used by the REST Admin API but they are fairly similar.

The C cmqstrc.h header file is also used by my tools that build the MQI interfaces for some other languages – it constructs the equivalent of the definition files for the Go and NodeJS bindings. Maybe one day I’ll add those languages to the core generator program but since those language projects work on different schedules than the real product releases, it can be difficult to synchronise the updates.

Warning: amqsrua events are not so easily decodable

One warning about decoding the MQI constants. You can’t use amqsevt or equivalent approaches to work with the queue manager metrics generated through the publish/subscribe mechanism shown by the amqsrua sample.

When you try using amqsevt, you get something that appears to work but actually makes no real sense. The CCSID is obviously not a metric, so that gives a clue that it might be wrong.

$ amqsevt -t "\$SYS/MQ/INFO/QMGR/QM1/Monitor/STATMQI/PUT" -m QM1
Sample AMQSEVT start
Press ENTER to end

**** Message #1 (380 Bytes) on Topic $SYS/MQ/INFO/QMGR/QM1/Monitor/STATMQI/PUT ****
Event Type : None [0]
Message Type : Statistics [21]
Reason : None [0]
Event created : 2021/12/02 10:32:24.51 GMT
Queue Mgr Name : QM1
Monitor Class : 2
Monitor Type : 3
Monitor Interval : 2989229
Monitor Flags None : 182
Appl Type : 57916
Coded Char Set Id   : 182
Current Queue Depth : 0
...

That’s because the “numbers” in the generated events are not MQI constants representing attributes, but keys to data maps returned by metadata queries (see the amqsrua source code to understand how that works).

Summary

This is a lot longer than twitter would allow to answer what looks like a simple question.

And a short answer could be a) live with it b) do some very simple eliminations c) do a lot more handcrafted work. But I hope it gives interesting background and some ideas on how you might work with MQI decoding.

Update History
  • 2021-12-10: Initial publication
  • 2021-12-15: Add section about bitfield decoding

This post was last updated on December 15th, 2021 at 11:57 am

Leave a Reply

Your email address will not be published. Required fields are marked *