The following example works with trivial message processing taking place. It uses simple one-fail-all-fail error handling semantics. This means that any errors occurring in one or both services will result in an exception being thrown back to the invoker.
Having worked on projects that require a far more robust solution following partial failure, I've written another post that includes details regarding a design strategy to support that - A Robust Splitter Aggregator Strategy <URL>.
Let's explore how this context configuration is working and what happens in circumstances where and exception is thrown by one of the services.
Regarding the splitter and aggregator, the interesting configuration starts at line 25 with the splitter specification. The construct has been customised with input-channel, output-channel and ref attributes. The channel configuration for this construct is obvious, the other attribute allows association of a bean reference that is able to perform the split function. It's generally true that a splitter bean with appropriate arguments and return types will be invoked if it's unambiguous.
The next stop for messages is the router that's defined on line 35, the recipient list router. This message endpoint is able to forward route messages into appropriate channels given an expression to invoke on the payload of the message. This router will examine the payload and route each message to one of two services - these are located on lines 48 and 62. The service(s) that are invoked are entirely dependent on what is returned from the splitter. Either or both of these services may be invoked for a given splitter input message.
Finally, results of one or two service invocations are routed towards the aggregator, the configuration starts for this at the chain defined at line 74. Finally the result of the aggregation is input to a transformer where some further processing takes place. Notice that there is no output channel on this chain, the implicitly created default output channel is relied upon here - it goes back through the gateway to the gateway invoker.
There are some interesting aspects to this service:
- If the splitter at line 25 receives a message but does not generate a list of one or more response messages, then an empty list will result on the router not getting called. This can be overridden by using the requires-reply attribute in which case an empty list will result in a message handling exception being thrown.
- Strong typing has been used on the data channels in an attempt to enforce strict processing rules and make the configuration easier to follow and understand.
- The chain construct has been used in an attempt to keep configuration compact where useful. It should be noticed that chain definition and strong typing are often two sides of the same coin. By grouping the aggregator and transformer in a chain I have been unable to control and hence document message type input to the transformer endpoint.
- Spring beans referenced from within this context have been loaded from an external file. Whilst they could have been component-scanned or defined in the same file I have chosen to keep them distinct in order that they are not loaded if not necessary for operation - I'd usually create mock and spy objects around these message endpoints.
- Any exception in Service Activator invocation at lines 48 and 62 would result in aggregation not completing. In this example, I have not created an error handler on the gateway in line 18 and so any exceptions thrown by SAs would result in an exception being thrown to the invoker of the gateway. A more robust solution, in the face of exception handling, would require a different design approach.
- I have documented, albeit briefly, intent for each section of the configuration specifically in order to help readers understand intentions of my design.
In the case that they're useful, Spring bean definitions are as follows:
The splitter code:
and the integration test: