Exception handling in Mule has classically been dealt with by using the exception strategy. This provides quite good functionality, however it is not as flexible as it could be. The new Mule flows give us a lot more flexibility when it comes to ‘Happy Paths’, so why not have the same flexibility on ‘Unhappy Paths’?
During development we typically use ‘try catch blocks’ to attempt something, and if things do not go the way we want, we can ‘catch’ the problem and take corrective action. So why not add this ability to flows as well. One way to do this is to implement your own message processor. We want this message processor to invoke the next processor in the chain, but if any of these messages throw an exception, we want our processor to pick up the exception, create an exception message (similar behaviour to the classical exception handler) and then apply a chain of processors which are configured on the message processor.
So here is our ExceptionProcessorChain:
public class ExceptionMessageProcessorChain extends DefaultMessageProcessorChain implements Lifecycle, FlowConstructAware{ public ExceptionMessageProcessorChain(String string, ArrayList<MessageProcessor> arrayList) { super(string,arrayList); } public MuleEvent process(MuleEvent event) throws MuleException { MuleEvent result=null; try{ result=processNext(event); }catch(MuleException e) { logger.info("Caught Exception"); event.getMessage().setPayload( new ExceptionMessage( event.getMessage().getPayload(),e,event.getFlowConstruct().getName(),event.getEndpoint().getEndpointURI())); logger.info("Invoking processor chain"); result=doProcess(event); logger.info("Got result from processor chain="+result.getMessageAsString()); } return result; }} |
This extends the DefaultMessageProcessorChain, however the main difference is that instead of invoking the chain of processors configured on it by calling the doProcess() method (as the parent would do) we simply call processNext() which invokes the next message processor in the flow. If any of these message processors throw a MuleException, we catch that exception and then invoke our internal Message processor chain by calling doProcess().
This gives us the freedom to do whatever we want to the exception. We can transform the exception and re-throw, or simply clean up and return a normal value. We also have the flexibility to embed these exception handlers inside each other, providing a very flexible combination. This is useful mainly with request-response flows, offering the flexibility to change the reply sent back to the original invoker when an exception occurs.
Configuring all of this can be slightly tricky. However, if you create a namespace handler for it (the one for the DefaultMessageProcessorChain is quite inspirational) you can do something like this:
<flow name="MyFlow"> <vm:inbound-endpoint path="MyFlowRequest" exchange-pattern="request-response" /> <my-module:exception-processor-chain> <append-string-transformer message="Sad Path"/> </my-module:exception-processor-chain> <append-string-transformer message="Happy Path"/> ...</flow> |
So we receive a message, then have our exception-processor-chain where we define our ‘catch’ block (the append-string-transformer) but we do not apply it yet. So the next step is to invoke the next message processor, the transformer appending ‘Happy Path’. If nothing throws an exception, then the ‘Sad Path’ processors are not invoked. However, if any message processor ‘after’ our exception-processor-chain throws an exception. Our message processor catches the exception, and invokes the ‘Sad Path’ processors, which in our case is a simple append-string-transformer.
Of course, you still can use the OOTB (out-of-the-box) exception strategy. For example; just in case the exceptional message processors end up throwing another exception. However, I hope you can see how this approach gives you much more flexibility than the classical exception strategy, since here you can decide exactly at what point you want to handle the exception…
联系客服