Search code examples
spring-integration

Exception thrown from ExpressionEvaluatingRequestHandlerAdvice triggers error handler handler on Adapter


I set an advice on my `MessageHandler'

@ServiceActivator(inputChannel = "outbound",adviceChain = "expressionAdvice")
public MessageHandler...

and configured it as:

@Bean
public ExpressionEvaluatingRequestHandlerAdvicer expressionAdvice() {
    ExpressionEvaluatingRequestHandlerAdvice advice = new ExpressionEvaluatingRequestHandlerAdvice();
    advice.setFailureChannelName("failure");          
    return advice;
}

in failure handler I parse and detect the errors

 @ServiceActivator(inputChannel = "failure")
    public void handleFailures(Message<?> message)  {
        ExpressionEvaluatingRequestHandlerAdvice.MessageHandlingExpressionEvaluatingAdviceException adviceException = (ExpressionEvaluatingRequestHandlerAdvice.MessageHandlingExpressionEvaluatingAdviceException) message.getPayload();
        Throwable cause = adviceException.getCause().getCause().getCause();

for specific errors I am doing some operations and flow is resumed.
But for specific error type I just log the error and continue, for other types I am rethrowing exception to get a retry.

This works, but there is a side affect, this throw Exception triggers ServiceActivator that was set on MessageProducerSupport.setErrorChannelName on the adapter.

@ServiceActivator(inputChannel = "onerror")

It does the job but I would like to avoid calling it, just to do the retries without going to this handler.

I do need this handler to catch other types of errors coming from source-channel.


Solution

  • See this option on that ExpressionEvaluatingRequestHandlerAdvice:

    /**
     * If true, any exception will be caught and null returned.
     * Default false.
     * @param trapException true to trap Exceptions.
     */
    public void setTrapException(boolean trapException) {
    

    More info in docs: https://docs.spring.io/spring-integration/docs/current/reference/html/messaging-endpoints.html#message-handler-advice-chain

    UPDATE

    For conditional exceptions "trapping", you need consider to implement a logic in your failure channel sub-flow. However trapException is still must be true.

    This is the logic we have so far in the ExpressionEvaluatingRequestHandlerAdvice:

    protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) {
        try {
            Object result = callback.execute();
            if (this.onSuccessExpression != null) {
                evaluateSuccessExpression(message);
            }
            return result;
        }
        catch (RuntimeException e) {
            Exception actualException = unwrapExceptionIfNecessary(e);
            if (this.onFailureExpression != null) {
                Object evalResult = evaluateFailureExpression(message, actualException);
                if (this.returnFailureExpressionResult) {
                    return evalResult;
                }
            }
            if (!this.trapException) {
                if (e instanceof ThrowableHolderException) { // NOSONAR
                    throw (ThrowableHolderException) e;
                }
                else {
                    throw new ThrowableHolderException(actualException); // NOSONAR lost stack trace
                }
            }
            return null;
        }
    }
    

    So, we catch an exception for a callback.execute() and process it in the evaluateFailureExpression() (which may just send an ErrorMessage to the mentioned failureChannel). Such a this.messagingTemplate.send(this.failureChannel, errorMessage); is not wrapped into a try..catch, so if you re-throw an exception from your error handling flow, it is going to be bubbled to the main flow.