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:
- get the message from the queue
- 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:
- Don’t GET the message so that it is irretrievably removed from the queue
- 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).

4 comments
Comments feed for this article
February 22, 2007 at 12:09 am
Dale Lane
It’s worth pointing out that the problem described above is not unique to C/C++. The same will be true for other clients, such as Java and C#.
February 22, 2007 at 1:53 pm
peterbroadhurst
There are some other options here too.
I think a common solution is placing the message back on a queue…
The queue it was retrieved from, then do a get-by-MsgId (note that this can be inefficient when there are multiple instances processing messages from the same queue)
A temporary queue (note it would need to permanent dynamic if using persistent messages)
A dedicated queue on the queue manager for this purpose. All apps for the qmgr can use it, as they all do put then get-by-MsgId.
February 22, 2007 at 2:16 pm
Dale Lane
Good point - there are lots of ways to handle this, and that’s quite a nice one.
I didn’t mean to imply that my two approaches were the only ones, and have tweaked my post a bit to make that clearer
Thanks, Pete!
It’d be interesting to hear if anyone else has any different approaches
December 18, 2007 at 10:46 pm
Diagnosing problems with Data Conversion « a Hursley view on WebSphere MQ
[...] from the message descriptor returned by the failed browse. Similar to an earlier post of mine on handling truncated messages, care needs to be taken to ensure that the second MQGET gets the same message that is got by the [...]