You can perform assured message delivery using synchronous message transmission, but the application must take responsibility for error handling.
The confirmID parameter of the putMessage method dictates whether a confirm flow is expected or not. A value of zero means that message transmission occurs in one flow, while a value of greater than zero means that a confirm flow is expected. The target queue manager logs the message to the destination queue as usual, but the message is locked and invisible to WebSphere MQ Everyplace applications, until a confirm flow is received. When you put messages with the confirmID, the messages are ordered by confirm time, not arrival time.
An WebSphere MQ Everyplace application can issue a put message confirmation using the confirmPutMessage method. Once the target queue manager receives the flow generated by this command, it unlocks the message, and makes it visible to WebSphere MQ Everyplace applications. You can confirm only one message at a time. It is not possible to confirm a batch of messages.
The confirmPutMessage() method requires you to specify the UniqueID of the message, not the confirmID used in the prior put message command. The confirmID is used to restore messages that remain locked after a transmission failure. This is explained in detail on page ***.
A skeleton version of the code required for an assured put is shown below:
long confirmId = MQe.uniqueValue(); try { qmgr.putMessage( "RemoteQMgr", "RemoteQueue", msg, null, confirmId ); } catch( Exception e ) { /* handle any exceptions*/ } try { qmgr.confirmPutMessage( "RemoteQMgr", "RemoteQueue", msg.getMsgUIDFields() ); } catch ( Exception e ) { /* handle any exceptions */ }
/* generate confirm Id */ MQEINT64 confirmId; rc = mqe_uniqueValue(&exceptBlock, &confirmId); /* put message to queue using this confirm Id */ if(MQERETURN_OK == rc) { rc = mqeQueueManager_putMessage(hQMgr, &exceptBlock, hQMgrName, hQName, hMsg, NULL, confirmId); /* now confirm the message put */ if(MQERETURN_OK == rc) { /* first get the message uid fields */ MQeFieldsHndl hFilter; rc = mqeFieldsHelper_getMsgUidFields(hMsg, &exceptBlock, &hFilter); if(MQERETURN_OK == rc) { rc = mqeQueueManager_confirmPutMessage(hQMgr, &exceptBlock, hQMgrName, hQName, hFilter); } } }
If a failure occurs during step 1 in Figure 5 the application should retransmit the message. There is no danger of introducing duplicate messages into the WebSphere MQ Everyplace network since the message at the target queue manager is not made visible to applications until the confirm flow has been successfully processed.
If the WebSphere MQ Everyplace application retransmits the message, it should also inform the target queue manager that this is happening. The target queue manager deletes any duplicate copy of the message that it already has. The application sets the MQe.Msg_Resend field to do this.
If a failure occurs during step 2 in Figure 5 the application should send the confirm flow again. There is no danger in doing this since the target queue manager ignores any confirm flows it receives for messages that it has already confirmed. This is shown in the following example, taken from examples.application.example6.
boolean msgPut = false; /* put successful? */ boolean msgConfirm = false; /* confirm successful? */ int maxRetry = 5; /* maximum number of retries */ long confirmId = MQe.uniqueValue(); int retry = 0; while( !msgPut && retry < maxRetry ) { try { qmgr.putMessage( "RemoteQMgr", "RemoteQueue", msg, null, confirmId ); msgPut = true; /* message put successful */ } catch( Exception e ) { /* handle any exceptions */ /* set resend flag for retransmission of message */ msg.putBoolean( MQe.Msg_Resend, true ); retry ++; } } if ( !msgPut ) /* was put message successful?*/ /* Number of retries has exceeded the maximum allowed, /*so abort the put*/ /* message attempt */ return; retry = 0; while( !msgConfirm && retry < maxRetry ) { try { qmgr.confirmPutMessage( "RenoteQMgr", "RemoteQueue", msg.getMsgUIDFields()); msgConfirm = true; /* message confirm successful*/ } catch ( Exception e ) { /* handle any exceptions*/ /* An Except_NotFound exception means */ /*that the message has already */ /* been confirmed */ if ( e instanceof MQeException && ((MQeException)e).code() == Except_NotFound ) putConfirmed = true; /* confirm successful */ /* another type of exception - need to reconfirm message */ retry ++; } }
MQEINT32 maxRetry = 5; rc = mqeQueueManager_putMessage(hQMgr, &exceptBlock, hQMgrName, hQName, hMsg, NULL, confirmId); /* if the put attempt fails, retry up to the maximum number*/ /*of retry times permitted, setting the re-send flag. */ while (MQERETURN_OK != rc && --maxRetry > 0 ) { rc = mqeFields_putBoolean(hMsg, &exceptBlock, MQE_MSG_RESEND, MQE_TRUE); if(MQERETURN_OK == rc) { rc = mqeQueueManager_putMessage(hQMgr, &exceptBlock, hQMgrName, hQName, hMsg, NULL, confirmId); } } if(MQERETURN_OK == rc) { MQeFieldsHndl hFilter; maxRetry = 5; rc = mqeFieldsHelper_getMsgUidFields(hMsg, &exceptBlock, &hFilter); if(MQERETURN_OK == rc) { rc = mqeQueueManager_confirmPutMessage(hQMgr, &exceptBlock, hQMgrName, hQName, hFilter); } while (MQERETURN_OK != rc && --maxRetry > 0 ) { rc = mqeQueueManager_confirmPutMessage(hQMgr, &exceptBlock, hQMgrName, hQName, hFilter); } }
Assured message get works in a similar way to put. If a get message command is issued with a confirmId parameter greater than zero, the message is left locked on the queue on which it resides until a confirm flow is processed by the target queue manager. When a confirm flow is received, the message is deleted from the queue. Figure 6 describes a get of synchronous messages:
The following code is taken from examples.application.example6
boolean msgGet = false; /* get successful? */ boolean msgConfirm = false; /* confirm successful? */ MQeMsgObject msg = null; int maxRetry = 5; /* maximum number of retries */ long confirmId = MQe.uniqueValue(); int retry = 0; while( !msgGet && retry < maxRetry) { try { msg = qmgr.getMessage( "RemoteQMgr", "RemoteQueue", filter, null, confirmId ); msgGet = true; /* get succeeded */ } catch ( Exception e ) { /* handle any exceptions */ /* if the exception is of type Except_Q_NoMatchingMsg, meaning that */ /* the message is unavailable then throw the exception */ if ( e instanceof MQeException ) if ( ((MQeException)e).code() == Except_Q_NoMatchingMsg ) throw e; retry ++; /* increment retry count */ } } if ( !msgGet ) /* was the get successful? */ /* Number of retry attempts has exceeded the maximum allowed, so abort */ /* get message operation */ return; while( !msgConfirm && retry < maxRetry ) { try { qmgr.confirmGetMessage( "RemoteQMgr", "RemoteQueue", msg.getMsgUIDFields() ); msgConfirm = true; /* confirm succeeded */ } catch ( Exception e ) { /* handle any exceptions */ retry ++; /* increment retry count */ } }
MQEINT32 maxRetry = 5; rc = mqeQueueManager_getMessage(hQMgr, &exceptBlock, hQMgrName, hQName, hMsg, NULL, confirmId); /* if the get attempt fails, retry up to the maximum number of*/ /*retry times permitted, setting the re-send flag. */ while (MQERETURN_OK != rc && --maxRetry > 0 ) { rc = mqeFields_getBoolean(hMsg, &exceptBlock, MQE_MSG_RESEND, MQE_TRUE); if(MQERETURN_OK == rc) { rc = mqeQueueManager_getMessage(hQMgr, &exceptBlock, hQMgrName, hQName, hMsg, NULL, confirmId); } } if(MQERETURN_OK == rc) { MQeFieldsHndl hFilter; maxRetry = 5; rc = mqeFieldsHelper_getMsgUidFields(hMsg, &exceptBlock, &hFilter); if(MQERETURN_OK == rc) { rc = mqeQueueManager_confirmGetMessage(hQMgr, &exceptBlock, hQMgrName, hQName, hFilter); } while (MQERETURN_OK != rc && --maxRetry > 0 ) { rc = mqeQueueManager_confirmPutMessage(hQMgr, &exceptBlock, hQMgrName, hQName, hFilter); } }
The value passed as the confirmId parameter also has another use. The value is used to identify the message while it is locked and awaiting confirmation. If an error occurs during a get operation, it can potentially leave the message locked on the queue. This happens if the message is locked in response to the get command, but an error occurs before the application receives the message. If the application reissues the get in response to the exception, then it will be unable to obtain the same message because it is locked and invisible to WebSphere MQ Everyplace applications.
However, the application that issued the get command can restore the messages using the undo method. The application must supply the confirmId value that it supplied to the get message command. The undo command restores messages to the state they were in before the get command.
boolean msgGet = false; /* get successful? */ boolean msgConfirm = false; /* confirm successful? */ MQeMsgObject msg = null; int maxRetry = 5; /* maximum number of retries */ long confirmId = MQe.uniqueValue(); int retry = 0; while( !msgGet && retry < maxRetry ) { try { msg = qmgr.getMessage( "RemoteQMgr", "RemoteQueue", filter, null, confirmId ); msgGet = true; /* get succeeded */ } catch ( Exception e ) { /* handle any exceptions */ /* if the exception is of type Except_Q_NoMatchingMsg, meaning that */ /* the message is unavailable then throw the exception */ if ( e instanceof MQeException ) if ( ((MQeException)e).code() == Except_Q_NoMatchingMsg ) throw e; retry ++; /* increment retry count */ /* As a precaution, undo the message on the queue. This will remove */ /* any lock that may have been put on the message prior to the */ /* exception occurring */ myQM.undo( qMgrName, queueName, confirmId ); } } if ( !msgGet ) /* was the get successful? */ /* Number of retry attempts has exceeded the maximum allowed, so abort */ /* get message operation */ return; while( !msgConfirm && retry < maxRetry ) { try { qmgr.confirmGetMessage( "RemoteQMgr", "RemoteQueue", msg.getMsgUIDFields() ); msgConfirm = true; /* confirm succeeded */ } catch ( Exception e ) { /* handle any exceptions */ retry ++; /* increment retry count */ } }
MQeFieldsHndl hMsg; rc = mqeQueueManager_getMessage(hQMgr, &exceptBlock, &hMsg, hQMgrName, hQName, hFilter, NULL, confirmId); /* if unsuccessful, undo the operation */ if(MQERETURN_OK != rc) { rc = mqeQueueManager_undo(hQMgr, &exceptBlock, hQMgrName, hQName, confirmId); }
The undo command also has relevance for the putMessage and browseMessagesAndLock commands. As with get message, the undo command restores any messages locked by the browseMessagesandLock command to their previous state.
If an application issues an undo command after a failed putMessage command, then any message locked on the target queue awaiting confirmation is deleted.
The undo command works for operations on both local and remote queues.