I needed to write a small C WebSphere MQ app today which made MQGET calls in a number of places. In all of these, it was likely that the initial message buffer would be too small for the message, and would need to be able to handle MQRC_TRUNCATED_MSG_FAILED.

So, I wrote a quick common function wrapping MQGET, that would free the message buffer in these cases and allocate a bigger one (using the size indicated by the DataLength field returned – see the Application Programming Guide for more detail if you’ve not done this before).

It looked something like this:

 void MQGETwithGrowingBuffer(MQHCONN Hconn, MQHOBJ Hobj,          
                             PMQVOID pMsgDesc, PMQVOID pGetMsgOpts,   
                             PMQLONG BufferLength, PMQVOID* ppBuffer, 
                             PMQLONG pDataLength, 
                             PMQLONG pCompCode, PMQLONG pReason)
 {
     MQGET(Hconn, Hobj, pMsgDesc, pGetMsgOpts, 
           *BufferLength, *ppBuffer, pDataLength, 
           pCompCode, pReason);

     if(*pReason == MQRC_TRUNCATED_MSG_FAILED)
     {
         free(*ppBuffer);

         *BufferLength = *pDataLength;
         *ppBuffer = (char*) malloc(*pDataLength);

         MQGET(Hconn, Hobj, pMsgDesc, pGetMsgOpts, 
               *BufferLength, *ppBuffer, pDataLength, 
               pCompCode, pReason);
     }
 }

If you’re writing a C++ application, you can let WMQ handle this for you – automatic buffer management is an option in the WMQ C++ API. (When you do a ‘get’ call in C++ with automatic buffer management, the WMQ C++ libraries do something fairly similar to what I’ve done above – see Using C++ for a description).

But when wouldn’t this work?

The answer is “when you are using MQGMO_CONVERT to convert the message data” as this makes things a little more complicated. Broadly speaking, this is what needs to happen:

  1. get the message from the queue
  2. convert the message data

If the message data is too large for the buffer provided, then the GET fails with MQRC_TRUNCATED_MSG_FAILED as described above.

However, if the buffer is large enough for the unconverted message data, but subsequently is found to be too small when the message is converted to a different character set, then the GET fails with MQRC_CONVERTED_MSG_TOO_BIG. This is the problem. Retrying the GET with a larger buffer is no longer an option – the message has been removed from the queue and isn’t there any more.

It is up to the application developer to handle this potential problem. Note that this is true both for self-coded C apps such as my quick program described above, and for developers using C++ with automatic buffering. Both are vulnerable to this same problem.

This leaves application developers with a number of options, including:

  1. Don’t GET the message so that it is irretrievably removed from the queue
  2. Use a big enough buffer

Don’t GET the message so that it is irretrievably removed from the queue

This is the safest approach. There are a number of ways to do this, such as using MQGMO_BROWSE for the first attempt at a MQGET. This means that the message will not be removed from the queue, allowing a second MQGET to be safely carried out with a correctly sized message buffer. (MQGMO_BROWSE_MSG_UNDER_CURSOR can be used to ensure that you get the same message the second time, and MQGMO_LOCK can be used to prevent another application from removing the message while you are inbetween MQI calls).

Another approach would be to use MQGMO_SYNCPOINT and backout any unsuccessful MQGETs.

Use a big enough buffer

This is harder to provide definitive advice for. You can start by providing a buffer that you know will be too small for the unconverted message data (e.g. 0) – the DataLength value returned by the first unsuccessful GET will give you the size of the unconverted message data. You then need to increase this value by some multiplier appropriate for your needs.

The safest approach is to assume that all characters, once converted, will be the size of the largest possible character in the encoding that the customer is going to – and allocate a buffer accordingly.

For example, when converting from an environment where the largest possible character is 2 bytes in size, to an environment where the largest possible character is 4 bytes in size, then the safest approach is to allocate a buffer that is 2x the size of the unconverted data.

The multiplier is ultimately a decision that you need to make, based on the codesets that you are using. The ‘worst-case scenario’ approach outlined above is the safest, however it is the least efficient. In practice, it is possible that in the example situation above, a buffer that is 1.5x the length of the unconverted data could be sufficient, due to the relatively small number of double-size characters in some codesets. Whether you are able to make this trade-off for efficiency is a decision that you need to make based on the sort of data that you are likely to be converting (i.e. size and frequency of multi-byte characters in variable length codesets).

About these ads