Description
Gary Russell opened INT-2116 and commented
Currently, ErrorMessage.payload.failedMessage is the message that caused an exception to be thrown. In a complex flow, the error flow may have to have complex logic to deal with the Error message. Email thread follows...
Consider
JMS.mda->Jaxb.transformer->validate.sa->store.in.db.sa
->errorChannel->JMS.to.deadletterQ.oba
Let's say the service invoked by the validator <sa/> throws an exception.
The failed message in the ErrorMessage sent to the errorChannel is the one containing the jaxb object out of the transformer. If this object is not Serializable, KABOOM! We're in Loop City.
While I am sure many (most?) use cases would want to be able to access the actual message that caused the failure, in this case, we'd like to get the original message that came in to the inbound adapter.
We could, of course, put another JAXB transformer in the error flow to convert back to XML but, I am sure this could get very messy if the main flow has lots of transformations etc, in that the error flow would have to figure out where the failure occurred in order to do the appropriate transformation.
I can see a couple of ways out of this.
- Add an option to the inbound adapters/gateways to select which message we want in the E.M.
- Along with failedMessage, add originalMessage to the E.M.
Seems to me that #2 would be easiest, and most useful, perhaps.
Mark Fisher wrote:
Gary, how do you envision adding the original Message as well considering it's out of scope at the time the ErrorMessage is created? I can see two options:
- proactive - always record it (e.g. as a header) even though we don't always expect a downstream failure
- reactive - as we bubble back up through each catch block, if catching a MessagingException, we could add to it
With #2 we could actually keep track of all Message snapshots in the "flow" (which is really just a stack). The downside is that it might be excessive in terms of memory usage for "normal" cases - so somehow it would need to be configurable.
-Mark
I hadn't really thought it through, but was thinking of something like the following in MessagingGatewaySupport...
try {
Message<?> originalMessage = this.messagingTemplate.convertAndSend(this.requestChannel, object, this.historyWritingPostProcessor);
}
catch (Exception e) {
if (this.errorChannel != null) {
this.messagingTemplate.send(this.errorChannel, new ErrorMessage(e, originalMessage));
}
else {
this.rethrow(e, "failed to send message");
}
}
(Change convertAndSend to return the original message).
This would be inexpensive, both in implementation and memory (convertAndSend already holds a reference to the original message until the send() returns anyway).
(MessageProducerSupport already has a reference to the original message).
The downside is it would only work for direct channels, but I am not sure we want the overhead of carrying the original message around in the headers.
Affects: 2.1 M1
Issue Links:
-
Make ErrorMessageSendingRecoverer more generic and introduce ErrorMessageStrategy for customization [INT-4257] #8198 Make ErrorMessageSendingRecoverer more generic and introduce ErrorMessageStrategy for customization
-
SPR-15459 Consider adding originalMessage to ErrorMessage