Reputation: 158
I have a JMS messaging application that has messages inbound from multiple JMS destinations. The message payloads are varying JSON representations with some common headers. I am relying on Spring's dynamic Jackson type conversion at the ServiceActivators
to convert into the actual POJOs. At present the routing is trivial because the channels are essentially "datatype" channels split out by the JSON payload type (they are all JSON String payloads but the JSON represents very different object types).
I would like to apply global validation logic to all inbound messages across several pattern matched channels e.g. "*input*"
and divert invalid messages to a validation error channel for review. Regardless of whether the message is valid or invalid, the local JMS transaction should be committed; if the message is invalid, I don't want the invalid message to be resent later.
My initial thought was to implement a ChannelInterceptor
that matches all the channels where this logic should be applied, but it does not appear that the capability to divert a message can be implemented in a ChannelInterceptor
. It appears that my two options with the ChannelInterceptor
are:
preSend
, ORNeither of these are the desired behavior. The JMS local transaction should always be committed (provided no other error) and the message either sent to the original destination, or diverted to the invalid message channel.
A Router
might be a good choice, but it doesn't appear that there is a way to apply a router to a pattern matched set of channels, so I believe I would have to apply it to each channel individually. That kind of duplication is something that I am hoping to avoid.
Another option I've thought of is to break out AspectJ and implement @Around
advice on the AbstractMessageSendingTemplate.convertAndSend(destination, payload, postProcessor)
method. This seems intrusive, but appears as if it may work. If there is an option better supported directly by the framework I would be happy to hear it.
If I can't find a way to globally apply this type of routing logic, then another option may be to route all inbound JMS messages through a single channel. A custom Router
could be applied to that inbound channel that uses payload type headers to direct messages to their proper "datatype" channels and routes invalid messages to the validation error channel.
Many Thanks!
Upvotes: 0
Views: 384
Reputation: 121262
My position do not do that in the global ChannelInterceptor
because it is easy to fall into the pattern with a channel which should not be affected with such a filtering logic.
You always can send all the messages to the same channel for a common logic. The routing behavior you can control via replyChannel
header populated before sending to the validation channel. So, for me the logic looks like:
Each flow performs a HeaderEnricher
to populate a replyChannel
header with the desired next step in the flow.
After that enricher all the flows send messages to the Filter
component with the validation logic.
There, on failure, you send message to the discardChannel
of the Filter
as you explained in your question.
On success you just don't send anywhere unless the replyChannel
header. So, you valid messages are going come back to their original flows.
Does it make sense for you?
Upvotes: 1
Reputation: 174554
My initial thought was to implement a ChannelInterceptor that matches all the channels where this logic should be applied, but it does not appear that the capability to divert a message can be implemented in a ChannelInterceptor
What leads you to believe that? preSend()
can return null
which effectively terminates the operation; simply send the failed validation to the common channel and return null
.
/**
* Invoked before the Message is actually sent to the channel.
* This allows for modification of the Message if necessary.
* If this method returns {@code null} then the actual
* send invocation will not occur.
*/
@Nullable
default Message<?> preSend(Message<?> message, MessageChannel channel) {
return message;
}
This will cause a MessageDeliveryException
at the inbound adapter, but you can simply absorb that in an error channel flow.
Upvotes: 1