Snippet 1 – Building an MQRFH2

Over the last week I’ve been working on some questions sent to me, and several have resulted in writing some test programs and scripts. This post shows the results of one of those exercises, building an MQRFH2 structure from scratch.

What is the MQRFH2?

The MQRFH2 structure design gives a way of passing arbitrary name/value pairs between MQ applications. There is a fixed header, followed by XML-like strings to describe these pairs. The JMS implementation uses these attributes as the mechanism for passing the properties defined by the JMS API, and for filtering.

When the MQI added message handles and properties in MQ V7, the MQRFH2 structure became the way to serialise the properties and provide compatibility. (As always, it’s a little more complicated than that internally, but that is close enough.) For MQI programs, it is usually easier to use the properties API calls like MQSETMP, as you do not need to directly manipulate buffers and lengths. But that was not an option here.

The MQRFH2 structure in a message
The MQRFH2 strucure in a message

What was the point?

The question sent to me was where a developer had added some properties to the message with an RFH2 but the consuming application did not see the expected content. I suspected a user error but needed to confirm.

I then ended up writing some code for this exploration for several reasons.

One was that I couldn’t find a good, existing sample that built this structure. The only shipped sample approaching the requirement had too much contamination with RFH1 processing, built around the original MQ pub/sub implementation. Another program I found via a search claimed to do the job, but when I looked at it properly, it didn’t.

Another reason for doing this was that the now-preferred mechansism for creating the RFH2 structure (using message properties) always does the right thing! I needed enough control to be able to set incorrect or unusual formats. Doing it in C gives direct access to the raw contents of the message. If I ever deal with more questions around API Exits and Message Properties, this will likely be a good starting point for further testing.

The product documentation does describe how to build the structure, but I still prefer working code as a starting point. It’s part of my “what happens when you try it” mantra.

The code

I’ve attached the complete program and associated material as a zip file here. But this is the main part:

printf("Enter folder strings (empty string ends):\n");
offset = MQRFH_STRUC_LENGTH_FIXED_2; // 36

  folder = fgets(inputLine,sizeof(inputLine)-1,stdin);
  // Remove trailing LF
  if (folder[strlen(folder)-1] == '\n')
     folder[strlen(folder) -1] = 0;

  folderLen = RoundTo4(strlen(folder)); // Length must be multiple of 4.
  if (folderLen)
    // NameValueDataLength - how long is the adjacent folder
    offset += sizeof(folderLen);
    // NameValueData - copy in actual length from input, not rounded value.
    // But bump offset by rounded amount, ready for next element. 
    // Gaps caused by rounding up are ignored.
    offset += folderLen;
} while(folderLen);

// Fill out RFH2 header knowing complete length, including fixed hdr
memcpy(rfh2Header.Format, MQFMT_STRING,(size_t)MQ_FORMAT_LENGTH);
rfh2Header.CodedCharSetId = MQCCSI_INHERIT;
rfh2Header.StrucLength = offset; // Total with fixed + folder lengths

// Copy header to start of buffer 

// And tell the MQMD that there is an RFH2
memcpy(md.Format,  MQFMT_RF_HEADER_2, (size_t)MQ_FORMAT_LENGTH);

// Message body is a single input string
printf("Enter body string:\n");

body = fgets(inputLine,sizeof(inputLine)-1,stdin);
s = strlen(body);
if (body[s-1] == '\n')
   body[s-1] = 0;

messageLen = offset + s;

MQPUT(hConn, hObj, &md, &pmo, messageLen, buffer, &CC,&RC);

Folder lengths must be multiples of 4, so each folder starts on a word boundary regardless of the actual length. Though that documented requirement doesn’t appear to be policed. Every folder ought to be valid XML, so the queue manager code knows how to parse it and knows when it reaches the end of the block. (It’s not really full XML, but close enough for this limited use.)

The tests

Here’s an extract from the output of a couple of tests, executed by the script. The first test shows how a C program can read properties from a user-defined folder while the JMS program ignores them even though validly formatted.

****Message properties****

  myfolder.colour : 'red'
  colour : 'green'
  size : 'large'
****   Message      ****
 length - 37 of 37 bytes
00000000: 48656C6C 6F206174 20547565 20203420 'Hello at Tue  4 '
00000010: 4F637420 31343A34 303A3531 20425354 'Oct 14:40:51 BST'
00000020: 20323032 32                         ' 2022           '
JmsBrowser - starts here

Message 1:

  Prop: colour               Val:green
  Prop: size                 Val:large
  <Hello at Tue  4 Oct 14:40:51 BST 2022>

This second test replaced the first folder with something identical, except for lacking the content='properties' piece (the f1a string in the test script). The C sample program for browsing messages parses and displays the properties from the <usr> folder but leaves the rest of the RFH2 in the body.

****Message properties****

  colour : 'green'
  size : 'large'
****   Message      ****
 length - 125 of 125 bytes
00000000: 52464820 02000000 58000000 22020000 'RFH ....X..."...'
00000010: 33030000 4D515354 52202020 00000000 '3...MQSTR   ....'
00000020: B8040000 30000000 3C6D7966 6F6C6465 '....0...<myfolde'
00000030: 7232273E 3C636F6C 6F75723E 79656C6C 'r2'><colour>yell'
00000040: 6F773C2F 636F6C6F 75723E3C 2F6D7966 'ow</colour></myf'
00000050: 6F6C6465 72323E00 48656C6C 6F206174 'older2>.Hello at'
00000060: 20547565 20203420 4F637420 31343A34 ' Tue  4 Oct 14:4'
00000070: 323A3430 20425354 20323032 32       '2:40 BST 2022   '

The JMS program simply strips the unrecognised material from the RFH2:

JmsBrowser - starts here

Message 1:

  Prop: colour               Val:green
  Prop: size                 Val:large
  <Hello at Tue  4 Oct 14:42:40 BST 2022>

And I did some additional experiments, including not using the RoundTo4 function on the folder lengths. But by that point, I’d already proven that the original question was indeed likely related to user error when constructing the contents.

Lessons learned or relearned

Some of this I already knew, but some of it was behaviour I hadn’t fully appreciated before. It’s a good demonstration of how trying things out can be so helpful.

  1. Using non-standard folders (the myfolder in this test) works fine for C-based programs provided you set the content='properties' annotation on the folder. Without that extra piece of information, the application program does not see the elements as properties. The queue manager does not fully strip the MQRFH2 from the body of the message. Though it does remove or display the folders that it does recognise.
  2. JMS programs do not recognise non-standard folders at all. All user-defined properties that are going to be passed to a JMS program must be in the <usr> folder. Other parts of the RFH2 like the myfolder region are silently discarded.
  3. The queue manager does not enforce all the rules related to the structure. It makes it possible to submit invalid strings that cannot be parsed or which are misaligned. While I didn’t experience it in my tests, I could theorise about Bad Things happening when messages go through the data conversion process. In particular if you do not stick to the multiple-of-4 rule and have structured data that is no longer word-aligned. For some scenarios, perhaps if you are using custom data conversion routines, you might even want/need your data to start on an 8-byte boundary rather than a 4.

File Download: Also available from this gist.

This post was last updated on October 6th, 2022 at 08:59 am

One thought on “Snippet 1 – Building an MQRFH2”

Leave a Reply

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