People usually treat MSMQ as if it provided, on it's own, magical reliable messaging features, when the reality is that all MSMQ does is provide a best effort mechanism when sending a message to a [remote] queue (albeit a really good one, in general). So, people don't usually bother implementing mechanisms to deal with the [rare] cases when MSMQ is indeed incapable of sending a message, but in an integration scenario, with a tool like BizTalk, this might actually be required sometimes.
Craig Neuwirt recently asked in the public BizTalk newsgroups the following question: "Is it possible to get DeliveryNotificationExceptions when using the MSMQ
Adapter? If so how?". I had been pondering about this for a while, so I decided to try to respond it in more detail here, as the answer turned out to be far more complicated than I originally thought.
The basic answer is Yes, and no. Yes, it works, but not as one would normally expect, particularly if one has experience using this feature with the MSMQT adapter.
The problem here is that all the MSMQ[C] adapter does is send the message over to a local send queue handled by the local MSMQ queue manager (particularly if the queue you're sending to is remote, since this is how MSMQ works). As far as I know, if the adapter cannot do that, a delivery notification exception should be raised.
However, as it turns out, that rarely happens. The reason is that the MSMQ queue manager will gladly take a message from the MSMQ Adapter, and the adapter will think that the submission was succesfull. It's only later on in time that the MSMQ queue manager might realize the message cannot be sent, because, for example, it specifies a non existant queue as the destination, or because the machine it was to be sent to never came online or doesn't exist. By the time this happens, BizTalk will have already continued happily on it's way running the orchestration instance, thinking the message was indeed succesfully sent.
So, the way to get around this, unfortunately means doing a lot more MSMQ stuff. MSMQ has mechanisms in place to deal with this kind of situation; namely: admin queues, message expiration and send acks/nacks. You can ask the MSMQ adapter to use these features by setting the appropriate properties in the send port configuration, or through the appropriate message context properties.
Basically, what you would do is ask MSMQ to, say, notify you if the message is not delivered to the destination queue (or even if the message has not been read from the queue by the receiving application) before a certain ammount of time has passed (i.e. by the time the message expiration is reached). This notification is done by the MSMQ queue manager by just resending the original message to the admin queue you specified when the message was sent. A property in the message on the admin queue will tell you the reason it ended up there. You can then just, for example, have biztalk pick up that message from there by just using an MSMQ receive location and then do either correlations or separate orchestrations to deal with the error.
Obviously, if you are looking at delivery notifications, you might be wondering how this helps you achieve what you wanted from the start: Pausing the orchestration instance until the message was correctly delivered! You can still do that, by just doing the same thing, but also asking MSMQ to give you Positive ACK messages that tell you that the message was indeed sucessfully delivered to the destination queue before it expired.
The implementation
Given this, I set out to build a sample that illustrated how to send a message to an Message Queue and request both positive and negative acknowledgements to check that the message reached the queue... or not. It turned out more complicated than I expected it to be, though nothing 100% impossible to achieve.
This basic sample just does this:
- Receive an XML message through a File Receive location.
- An orchestration receives the message and sends it to a Send Port configured to use the MSMQ adapter. The Send port is configured to ask MSMQ for both positive and negative acknowledgements that the message reached the queue (or not), but not for receive acks (in other words, we care that the message reaches the destination queue, not that it is read by an application). The port also is configured with a message timeout of 10 seconds, so the NACK will be generated if the message cannot be delivered before that (this is critical for it to work).
The exact port configuration is like this:
- Acknowledgement Type: This is the kind of acknowledgement you want MSMQ to give you, and maps to the AcknowledgementTypes enumeration in the System.Messaging namespace. We configure it to FullReachQueue.
- Administration Queue: This is the path of the queue we want MSMQ to send the ACK/NACK messages, in our case ".\Private$\MSMQDNADMINQUEUE".
- Timeout and Timeout Unit: This is the time to live of the message (the time to message expiration). We configure it as 10 seconds.
- The orchestration waits for a corresponding ACK/NACK message to be routed to it from the corresponding MSMQ Administrative queue via a correlation.
- When the ACK/NACK notification is received from the admin queue, the message prints a message to the Debug Stream (which you can read using DebugView or whatever) telling if the message was sent or not.
This sounds much simpler than what it really is. The first problem that appears is how to correlate the ACK/NACK message back to the orchestration instance that sent the message. MSMQ provides a mechanism for this, in the form of the CorrelationId property of the message: Basically, what happens is that the ACK/NACK message will have in the CorrelationId property the ID MSMQ (not biztalk) assigned to the original message when you sent it. Unfortunately, doing this is complicated, because of the way biztalk works, or, at least, I didn't find a clean way of doing it using standard correlations sets.
The easiest way to workaround this (and the one the samples shows) is to, instead, correlate on another unique and shared property of the messages, and the simplest one is to use the message label. We do this by simply creating a new message to send to MSMQ and setting the MSMQ.Label property to a unique value (a guid) for the orchestration instance. We then initialize a correlation set (created on top of the MSMQ.Label property) when sending the message to MSMQ and then following it when receiving the ACK/NACK message from the administration queue.
There is a problem with this, however, and is that the MSMQ adapter doesn't promote the MSMQ.Label property, it just writes it to the message context. Because of this, you can't use it for routing, and thus the correlation set we just configured will not work. To workaround this, we need to create a new custom pipeline component that promotes the property, and then use this component in a custom receive pipeline associated with the Receive Location we configured for the Administration Queue. I provide a sample component to do this in the MsmqPropertyPromotionComponent project. All it actually does is this:
public IBaseMessage Execute(IPipelineContext context, IBaseMessage msg)
{
MSMQ.Label labelProp = new MSMQ.Label();
object obj = msg.Context.Read(labelProp.Name.Name, labelProp.Name.Namespace);
string label = obj as string;
if ( label != null )
{
msg.Context.Promote(labelProp.Name.Name, labelProp.Name.Namespace, label);
}
return msg;
}
With that out of the way, a second problem appears: When MSMQ generates a Negative Acknowledgement message, the body of the message will actually contain the body of the original message it couldn't sent. This is great. However, when it generates a Positive Acknowledgement message, the body is 0 bytes long (i.e. empty). This means that if you receive the ACK/NACK message using the standard XmlDisassembler component you'll fault in the ACK case, which is certainly not what we intended!
To work around this we configure the orchestration to receive just an XmlDocument from the administration queue, instead of a typed message from a given schema. This allows us to handle the case where the message body is empty correctly!
The third, and though issue, once you receive the ACK/NACK message, is how to know if it meant the message was succesfully sent or not. MSMQ provides a mechanism for this based on the Class (PROPID_M_CLASS) property of the message. Basically, you can check this property to see if it meant the message is a positive acknowledgement (in which case it will be either MQMSG_CLASS_ACK_REACH_QUEUE or MQMSG_CLASS_ACK_RECEIVE) or a negative one (every other value).
Unfortunately, the MSMQ adapter doesn't expose this message property as part of the message context in BizTalk, which is a pity. What I do in the sample is use a fairly ugly hack: I take advantage of the fact that ACK messages have a 0-byte length body! For this, I created a helper component (in the MessageHelperComponent project) that simply checks the length of the body:
public class MessageHelper
{
public static bool IsEmpty(XLANGPart part)
{
Stream stream = (Stream)part.RetrieveAs(typeof(Stream));
bool isEmpty = stream.Length == 0;
stream.Close();
return isEmpty;
}
}
If the message is a NACK notification, you can further dig into the MSMQ.Acknowledge property in the message's Context to see what kind of error actually happened.
I hope this information can be useful to someone, and, if you find a better option, please do let me know! You can download the sample meanwhile here.