Reading messages in C++
A buffer can be supplied by the application or the system. Data can be accessed directly from the buffer or read sequentially. There is a class equivalent to each message type. Sample code is given.
When receiving data, the application or the system can supply a suitable message buffer. The same buffer can be used for both multiple transmission and multiple receipt for a particular ImqMessage object. If the message buffer is supplied automatically, it grows to accommodate whatever length of data is received. However, a message buffer supplied by the application might not be big enough to hold the data received. Then either truncation or failure might occur, depending on the options used for message receipt.
Incoming data can be accessed directly from the message buffer, in which case the data length indicates the total amount of incoming data. Alternatively, incoming data can be read sequentially from the message buffer. In this case, the data pointer addresses the next byte of incoming data, and the data pointer and data length are updated each time data is read.
Items are pieces of a message, all in the user area of the message buffer, that need to be processed sequentially and separately. Apart from regular user data, an item might be a dead-letter header or a trigger message. Items are always associated with message formats; message formats are not always associated with items.
There is a class of object for each item that corresponds to a recognizable IBM MQ message format. There is one for a dead-letter header and one for a trigger message. There is no class of object for user data. That is, once the recognizable formats have been exhausted, processing the remainder is left to the application program. Classes for user data can be written by specializing the ImqItem class.
The following example shows a message receipt that takes account of a number of potential items that can precede the user data, in an imaginary situation. Non-item user data is defined as anything that occurs after items that can be identified. An automatic buffer (the default) is used to hold an arbitrary amount of message data.ImqQueue queue ; ImqMessage msg ; if ( queue.get( msg ) ) { /* Process all items of data in the message buffer. */ do while ( msg.dataLength( ) ) { ImqBoolean bFormatKnown = FALSE ; /* There remains unprocessed data in the message buffer. */ /* Determine what kind of item is next. */ if ( msg.formatIs( MQFMT_DEAD_LETTER_HEADER ) ) { ImqDeadLetterHeader header ; /* The next item is a dead-letter header. */ /* For the next statement to work and return TRUE, */ /* the correct class of object pointer must be supplied. */ bFormatKnown = TRUE ; if ( msg.readItem( header ) ) { /* The dead-letter header has been extricated from the */ /* buffer and transformed into a dead-letter object. */ /* The encoding and character set of the dead-letter */ /* object itself are MQENC_NATIVE and MQCCSI_Q_MGR. */ /* The encoding and character set from the dead-letter */ /* header have been copied to the message attributes */ /* to reflect any remaining data in the buffer. */ /* Process the information in the dead-letter object. */ /* Note that the encoding and character set have */ /* already been processed. */ ... } /* There might be another item after this, */ /* or just the user data. */ } if ( msg.formatIs( MQFMT_TRIGGER ) ) { ImqTrigger trigger ; /* The next item is a trigger message. */ /* For the next statement to work and return TRUE, */ /* the correct class of object pointer must be supplied. */ bFormatKnown = TRUE ; if ( msg.readItem( trigger ) ) { /* The trigger message has been extricated from the */ /* buffer and transformed into a trigger object. */ /* Process the information in the trigger object. */ ... } /* There is usually nothing after a trigger message. */ } if ( msg.formatIs( FMT_USERCLASS ) ) { UserClass object ; /* The next item is an item of a user-defined class. */ /* For the next statement to work and return TRUE, */ /* the correct class of object pointer must be supplied. */ bFormatKnown = TRUE ; if ( msg.readItem( object ) ) { /* The user-defined data has been extricated from the */ /* buffer and transformed into a user-defined object. */ /* Process the information in the user-defined object. */ ... } /* Continue looking for further items. */ } if ( ! bFormatKnown ) { /* There remains data that is not associated with a specific*/ /* item class. */ char * pszDataPointer = msg.dataPointer( ); /* Address.*/ int iDataLength = msg.dataLength( ); /* Length. */ /* The encoding and character set for the remaining data are */ /* reflected in the attributes of the message object, even */ /* if a dead-letter header was present. */ ... } } }
In this example, FMT_USERCLASS is a constant representing the 8-character format name associated with an object of class UserClass, and is defined by the application.
UserClass is derived from the ImqItem class (see ImqItem C++ class ), and implements the virtual copyOut and pasteIn methods from that class.
The next two examples show code from the ImqDeadLetterHeader class (see ImqDeadLetterHeader C++ class ). The first example shows custom-encapsulated message- writing code.// Insert a dead-letter header. // Return TRUE if successful. ImqBoolean ImqDeadLetterHeader :: copyOut ( ImqMessage & msg ) { ImqBoolean bSuccess ; if ( msg.moreBytes( sizeof( omqdlh ) ) ) { ImqCache cacheData( msg ); // Preserve original message content. // Note original message attributes in the dead-letter header. setEncoding( msg.encoding( ) ); setCharacterSet( msg.characterSet( ) ); setFormat( msg.format( ) ); // Set the message attributes to reflect the dead-letter header. msg.setEncoding( MQENC_NATIVE ); msg.setCharacterSet( MQCCSI_Q_MGR ); msg.setFormat( MQFMT_DEAD_LETTER_HEADER ); // Replace the existing data with the dead-letter header. msg.clearMessage( ); if ( msg.write( sizeof( omqdlh ), (char *) & omqdlh ) ) { // Append the original message data. bSuccess = msg.write( cacheData.messageLength( ), cacheData.bufferPointer( ) ); } else { bSuccess = FALSE ; } } else { bSuccess = FALSE ; } // Reflect and cache error in this object. if ( ! bSuccess ) { setReasonCode( msg.reasonCode( ) ); setCompletionCode( msg.completionCode( ) ); } return bSuccess ; }The second example shows custom-encapsulated message- reading code.
// Read a dead-letter header. // Return TRUE if successful. ImqBoolean ImqDeadLetterHeader :: pasteIn ( ImqMessage & msg ) { ImqBoolean bSuccess = FALSE ; // First check that the eye-catcher is correct. // This is also our guarantee that the "character set" is correct. if ( ImqItem::structureIdIs( MQDLH_STRUC_ID, msg ) ) { // Next check that the "encoding" is correct, as the MQDLH // contains numeric data. if ( msg.encoding( ) == MQENC_NATIVE ) { // Finally check that the "format" is correct. if ( msg.formatIs( MQFMT_DEAD_LETTER_HEADER ) ) { char * pszBuffer = (char *) & omqdlh ; // Transfer the MQDLH from the message and move pointer on. if ( bSuccess = msg.read( sizeof( omdlh ), pszBuffer ) ) { // Update the encoding, character set and format of the // message to reflect the remaining data. msg.setEncoding( encoding( ) ); msg.setCharacterSet( characterSet( ) ); msg.setFormat( format( ) ); } else { // Reflect the cache error in this object. setReasonCode( msg.reasonCode( ) ); setCompletionCode( msg.completionCode( ) ); } } else { setReasonCode( MQRC_INCONSISTENT_FORMAT ); setCompletionCode( MQCC_FAILED ); } } else { setReasonCode( MQRC_ENCODING_ERROR ); setCompletionCode( MQCC_FAILED ); { } else { setReasonCode( MQRC_STRUC_ID_ERROR ); setCompletionCode( MQCC_FAILED ); } return bSuccess ; }
With an automatic buffer, the buffer storage is volatile. That is, buffer data might be held at a different physical location after each get method invocation. Therefore, each time buffer data is referenced, use the bufferPointer or dataPointer methods to access message data.
We might want a program to set aside a fixed area for receiving message data. In this case, invoke the useEmptyBuffer method before using the get method.
Use a fixed, nonautomatic area limits messages to a maximum size, so it is important to consider the MQGMO_ACCEPT_TRUNCATED_MSG option of the ImqGetMessageOptions object. If this option is not specified (the default), the MQRC_TRUNCATED_MSG_FAILED reason code can be expected. If this option is specified, the MQRC_TRUNCATED_MSG_ACCEPTED reason code might be expected depending on the design of the application.
The next example shows how a fixed area of storage can be used to receive messages:char * pszBuffer = new char[ 100 ]; msg.useEmptyBuffer( pszBuffer, 100 ); gmo.setOptions( MQGMO_ACCEPT_TRUNCATED_MSG ); queue.get( msg, gmo ); delete [ ] pszBuffer ;
In this code fragment, the buffer can always be addressed directly, with pszBuffer, as opposed to using the bufferPointer method. However, it is better to use the dataPointer method for general-purpose access. The application (not the ImqCache class object) must discard a user-defined (nonautomatic) buffer.
Attention: Specifying a null pointer and zero length with useEmptyBuffer does not nominate a fixed-length buffer of length zero as might be expected. This combination is interpreted as a request to ignore any previous user-defined buffer, and instead revert to the use of an automatic buffer.
Parent topic: Messaging in C++