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.
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.