0

I am trying to handle exceptions using ExpressionEvaluatingRequestHandlerAdvice, have a transformer for a fail channel,

<int:transformer input-channel="afterFailureChannel" output-channel="validateOutputChannel" ref="testExceptionTransformer" method="handleLockServiceResponse"/>

In testExceptionTransformer, I am forming user defined exception and sending it in http response entity which I want to send as a rest api response, Even though transformer has outputChannel, application throws

org.springframework.messaging.core.DestinationResolutionException: no output-channel or replyChannel header available
    at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutput(AbstractMessageProducingHandler.java:452) ~[spring-integration-core-5.5.13.jar:5.5.13]

Could you please help?

Edit:

Transformer looks like this,

public ResponseEntity<Object> handleLockServiceResponse(Message<MessagingException> message) throws Exception {
        ResponseEntity<Object> response = null;
    LOGGER.error(message.getPayload().getFailedMessage().toString());
        LOGGER.error(message.getPayload().getCause().toString());

        try {
            Throwable exception = message.getPayload().getCause();
            if (exception.getCause() instanceof HttpClientErrorException) {
                throw new handleValidationException(exception.getCause().getMessage());
            }
        }catch(handleValidationException ex){
            return adapterErrorHandler.handleCustomValidationException(ex);
        }
        return response;
    }

1 Answers1

0

It indeed doesn't fail in your transformer since you have that output-channel it fails in the initial gateway when it tries to correlate the reply message into a TemporaryReplyChannel from headers. We need to see what your transformer does, but the rule of thumb is if you return a Message from the transformer, you have to coyp headers from request message. However with an ExpressionEvaluatingRequestHandlerAdvice and its failureChannel it is a bit tricky.

The logic there is like this:

    if (evalResult != null && this.failureChannel != null) {
        MessagingException messagingException =
                new MessageHandlingExpressionEvaluatingAdviceException(message, "Handler Failed",
                        unwrapThrowableIfNecessary(exception), evalResult);
        ErrorMessage errorMessage = new ErrorMessage(messagingException);
        this.messagingTemplate.send(this.failureChannel, errorMessage);
    }

It becomes obvious that ErrorMessage doesn't have a request message headers. So, you need to extract them from that exception via getFailedMessage() and that's the one is sent to your service instrumented with that ExpressionEvaluatingRequestHandlerAdvice.

We probably need to improve the doc on the matter: https://docs.spring.io/spring-integration/docs/current/reference/html/messaging-endpoints.html#expression-advice

UPDATE

So, now you return a ResponseEntity from your transformer method and headers for the reply message is copied from that ErrorMessage we send from the ExpressionEvaluatingRequestHandlerAdvice. To preserve original message headers in the reply message you must do something like this:

public Message<ResponseEntity<Object>> handleLockServiceResponse(Message<MessagingException> message) throws Exception {
    ResponseEntity<Object> response = null;
LOGGER.error(message.getPayload().getFailedMessage().toString());
    LOGGER.error(message.getPayload().getCause().toString());

    try {
        Throwable exception = message.getPayload().getCause();
        if (exception.getCause() instanceof HttpClientErrorException) {
            throw new handleValidationException(exception.getCause().getMessage());
        }
    }catch(handleValidationException ex){
        response = adapterErrorHandler.handleCustomValidationException(ex);
    }
    return MessageBuilder.withPayload(response).copyHeaders(message.getPayload().getFailedMessage().getHeaders()).build();
}
Artem Bilan
  • 113,505
  • 11
  • 91
  • 118
  • I am new to integration so having difficulty figuring out your comment! I have simple transformer which I added as a part of the question. can you please take a look and suggest how can I extract and pass on request headers? – KeepItSimple Nov 30 '22 at 17:47
  • See an UPDATE in my answer. – Artem Bilan Nov 30 '22 at 18:18
  • thank you so much for your response, that fixed the issue though its still throwing HttpClientErrorException instead of custom exception(handleValidationException). It might be something wrong with how the ResponseEntity is working! I tried with following but still same issue, MessageBuilder.withPayload(new ResponseEntity<>(response.getBody(),response.getHeaders(),response.getStatusCode())).copyHeaders(message.getPayload().getFailedMessage().getHeaders()).build(); – KeepItSimple Nov 30 '22 at 20:08
  • I think this is already a different story in context of how you call this service, what you send as that `ResponseEntity`and what result you got on your HTTP client for this request. – Artem Bilan Nov 30 '22 at 20:50