2

I want my service gateway to return any error in its immediate flow (up to the nearest async channel) to service caller as a valid message (not exception), in sync fashion.

Entire flow is on a single thread. My minimal example refuses to work as desired and I can't figure out proper way to use framework to achieve my goal. Please, see code below.

@RunWith(SpringRunner.class)
public class ErrorHandlingTests {

    @Autowired
    ErrorsHandlingService errorsHandlingService;

    @EnableIntegration
    @Configuration
    static class Config {

        @Bean
        IntegrationFlow errorHandler() {
            return IntegrationFlows.from("errorChannel").
                    handle(errorMessage -> {
                        Message<?> failedMessage = ((MessagingException) errorMessage.getPayload()).getFailedMessage();
                        MessageChannel replyChannel = (MessageChannel) failedMessage.getHeaders().getReplyChannel();
                        replyChannel.send(new GenericMessage<>("Failure for " + failedMessage.getPayload()));
                    })
                    .get();
        }

        @Bean
        IntegrationFlow errorsHandlingFlow1() {
            return IntegrationFlows.from(ErrorsHandlingService.class, gws -> gws.errorChannel("errorChannel"))
                    .transform(new AbstractPayloadTransformer<String, String>() {
                        @Override
                        protected String transformPayload(String s) {
                            if (s.contains("oops"))
                                throw new IllegalArgumentException("Bad value");
                            return "R: " + s;
                        }
                    })
                    .get();
        }
    }


    @Test
    public void testErrorHandlingInFlow1() {
        assertEquals("R: a", errorsHandlingService.process("a"));
        assertEquals("Failure for oops", errorsHandlingService.process("oops"));
    }
}

This test hangs on 2nd assert and log prints:

W 210124 161538.597 [] [main] GenericMessagingTemplate$TemporaryReplyChannel - Reply message received but the receiving thread has exited due to an exception while sending the request message: GenericMessage [payload=Failure for oops, headers={id=57f79307-0778-88b4-9261-9040633cfc03, timestamp=1611494138597}]

All of this happens on the latest 5.3.4 release.

Victor Sorokin
  • 11,878
  • 2
  • 35
  • 51

1 Answers1

1

You don't need to use that replyChannel.send() manually. For such an error handling and compensation reply producing you just need to do like this:

<MessagingException>handle((ex, h) -> "Failure for " + ex.getFailedMessage().getPayload())

The logic in the gateway is like this:

  1. send-n-receive - sync or async - via replyChannel header.
  2. If failure with the previous, catch that exception and process it in the handleSendAndReceiveError()
  3. It takes an errorChannel configured for this gateway and performs send-n-receive of ErrorMessage
  4. The reply is expected as regular one from that sub-flow.

Well, the actual problem is that you use a replyChannel from the failedMessage, which is not valid any more since the main flow has failed already. At this point we deal with new error message flow and we really should rely on its headers already. Or just let the framework to do reply correlation for us!

Artem Bilan
  • 113,505
  • 11
  • 91
  • 118
  • Wow! Thanks! What I was missing is pt. 4 -- expecting that error handler can post converted error back to error channel and that'll be a sync answer to original client call is a bit surprising for me. – Victor Sorokin Jan 25 '21 at 15:12
  • Right. From the messaging perspective the error flow is just a message processor with the same send-n-receive capabilities. It is really just our business logic that it is an `ErrorMessage` - for the framework it is just a message. – Artem Bilan Jan 25 '21 at 15:15
  • I have a bit more complex test where service is `List process(List input)` with splitter and aggregator behind that. When I post `[a, oops]` there I receive reply list with single element: `[Failure for oops]`, how does this magic work? I was more expecting `[R: a, Failure for oops]`. – Victor Sorokin Jan 25 '21 at 15:16
  • Because there was no an aggregation yet. You failed on a single message. You can aggregate with errors, too, but that's going to be a different story. Please, don't raise GH issues in the future if you have already SO thread. – Artem Bilan Jan 25 '21 at 15:25
  • Sorry for pre-mature issue, but I've created there first, then posted here, since I felt SO post would increase my chances for help. Anyways, thanks for help! I've created another SO post about second setup: https://stackoverflow.com/questions/65887787 – Victor Sorokin Jan 25 '21 at 15:37