Search code examples
spring-integrationspring-integration-dsl

Routing to an error channel if fault is thrown in outbound gateway in DSL


I have the following Spring Integration JAva DSL code:

    return flow -> flow.channel(INPUT_CHANNEL)
            .transform(customMapper, "mapFrom")
            .enrichHeaders(polarisPreCompSoapActionHeader())
            .route("headers.key",
                    subflowMapping -> subflowMapping
                            .subFlowMapping("value1", subflow -> subflow
                                    .handle(webserviceOutboundGateway,
                                            e -> e.advice(skipAdvice()))
                            )
            )
            .channel(OUTPUT_CHANNEL);

@Bean
public Advice skipAdvice() {
    ExpressionEvaluatingRequestHandlerAdvice advice = new ExpressionEvaluatingRequestHandlerAdvice();
    advice.setFailureChannel(errorChannel());
    return advice;
}

The webserviceOutboundGateway is created as a bean of MarshallingWebServiceOutboundGateway.

What I am trying to achieve is to route the error message to the error channel when a SOAP fault arrives. I thought that I can add an advice to the handler and this advice should be an ExpressionEvaluatingRequestHandlerAdvice where I can set the failure channel. So whenever an error is thrown in the outbound gateway then the error message will be forwarded to the error channel.

Then problem is that now the gateway throw an exception and the flow stops

2020-05-20 22:21:48,161 INFO  com.acme.Timing Thread=qtp14486859-12 MDC=16d7cc4c-c9da-449b-8bfa-504e6d81185d REQUEST COMPLETE : time[39]ms  message[ProcessTran] endpoint[http://localhost:8081/system-integration-service/service]
2020-05-20 22:21:48,161 DEBUG org.springframework.integration.transformer.ContentEnricher$Gateway Thread=qtp14486859-12 MDC=16d7cc4c-c9da-449b-8bfa-504e6d81185d failure occurred in gateway sendAndReceive: error occurred in message handler [webserviceOutboundGateway]; nested exception is org.springframework.ws.client.WebServiceTransportException: Not Found [404]
2020-05-20 22:21:48,162 DEBUG org.springframework.integration.gateway.GatewayProxyFactoryBean$MethodInvocationGateway Thread=qtp14486859-12 MDC=16d7cc4c-c9da-449b-8bfa-504e6d81185d failure occurred in gateway sendAndReceive: error occurred in message handler [webserviceOutboundGateway]; nested exception is org.springframework.ws.client.WebServiceTransportException: Not Found [404]
2020-05-20 22:21:48,167 ERROR com.acme.webservice.OrchestrationServiceEndpoint Thread=qtp14486859-12 MDC=16d7cc4c-c9da-449b-8bfa-504e6d81185d Error
org.springframework.ws.client.WebServiceTransportException: Not Found [404]
        at org.springframework.ws.client.core.WebServiceTemplate.handleError(WebServiceTemplate.java:699)
        at org.springframework.ws.client.core.WebServiceTemplate.doSendAndReceive(WebServiceTemplate.java:609)
        at org.springframework.ws.client.core.WebServiceTemplate.sendAndReceive(WebServiceTemplate.java:555)
        at org.springframework.integration.ws.MarshallingWebServiceOutboundGateway.doHandle(MarshallingWebServiceOutboundGateway.java:87)
        at com.acme.webservice.MarshallingWebServiceOutboundGateway.doHandle(MarshallingWebServiceOutboundGateway.java:60)
        at org.springframework.integration.ws.AbstractWebServiceOutboundGateway.handleRequestMessage(AbstractWebServiceOutboundGateway.java:188)
        at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:109)
        at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:127)
        at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116)
        at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:148)
        at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:121)
        at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:89)
        at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:425)
        at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:375)
        at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:183)
        at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:162)
        at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:47)
        at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:108)
        at org.springframework.integration.router.AbstractMessageRouter.handleMessageInternal(AbstractMessageRouter.java:194)
        at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:127)
        at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116)
        at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:148)
        at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:121)
        at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:89)
        at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:425)
        at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:375)
        at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:183)
        at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:162)
        at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:47)
        at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:108)
        at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutput(AbstractMessageProducingHandler.java:360)
        at org.springframework.integration.handler.AbstractMessageProducingHandler.produceOutput(AbstractMessageProducingHandler.java:271)
        at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutputs(AbstractMessageProducingHandler.java:188)
        at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:115)
        at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:127)
        at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116)
        at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:148)
        at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:121)
        at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:89)
        at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:425)
        at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:375)
        at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:183)
        at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:162)
        at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:47)

Any advice / help would be highly appreciated!
Thanks,
V.


UPDATE1

I set the trapExecution to false, added the advice to the gateway directly and I had to use setOnFailureExpressionString otherwise the flow was hanging. So how it looks now as follows:

@Bean
@Autowired
public MarshallingWebServiceOutboundGateway webserviceOutboundGateway(...) {
    MarshallingWebServiceOutboundGateway gateway = new ...;
    gateway.setAdviceChain(Arrays.asList(skipAdvice()));
    return gateway;
}

@Autowired
@Qualifier("skipCallChannel")
private MessageChannel skipCallChannel;

@Bean
public Advice skipAdvice() {
    ExpressionEvaluatingRequestHandlerAdvice advice = new ExpressionEvaluatingRequestHandlerAdvice();
    advice.setFailureChannel(endChannel);
    advice.setOnFailureExpressionString("payload + ' was bad, with reason: ' + #exception");
    advice.setTrapException(true);
    return advice;
}

and I get the following error:

2020-05-21 14:45:13,921 DEBUG org.springframework.integration.handler.BridgeHandler Thread=qtp14486859-19 MDC=16d7cc4c-c9da-449b-8bfa-504e6d81185d org.springframework.integration.handler.BridgeHandler@cdf55 received message: ErrorMessage [payload=org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice$MessageHandlingExpressionEvaluatingAdviceException: Handler Failed; nested exception is org.springframework.ws.client.WebServiceTransportException: Not Found [404], failedMessage=GenericMessage [payload=uk.co.acme._2009._03.ProcessTran@1759824, headers={replyChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel@1ddbf77, errorChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel@1ddbf77, header.cqs.model=com.acme.model.FlowModel@cc006f, ws_soapAction=http://www.acme.co.uk/XRTEService/2009/03/ProcessTran, id=59e305ee-1672-d5ba-db4b-672198f25ab8, timestamp=1590068713891}], headers={id=14aeb6ed-77af-52bf-6b53-72bc0a4bb5c1, timestamp=1590068713921}]
2020-05-21 14:45:13,922 DEBUG org.springframework.integration.transformer.ContentEnricher$Gateway Thread=qtp14486859-19 MDC=16d7cc4c-c9da-449b-8bfa-504e6d81185d failure occurred in gateway sendAndReceive: Dispatcher failed to deliver Message; nested exception is org.springframework.messaging.core.DestinationResolutionException: no output-channel or replyChannel header available
2020-05-21 14:45:13,922 DEBUG org.springframework.integration.gateway.GatewayProxyFactoryBean$MethodInvocationGateway Thread=qtp14486859-19 MDC=16d7cc4c-c9da-449b-8bfa-504e6d81185d failure occurred in gateway sendAndReceive: Dispatcher failed to deliver Message; nested exception is org.springframework.messaging.core.DestinationResolutionException: no output-channel or replyChannel header available
2020-05-21 14:45:13,927 ERROR com.acme.webservice.OrchestrationServiceEndpoint Thread=qtp14486859-19 MDC=16d7cc4c-c9da-449b-8bfa-504e6d81185d Error
org.springframework.messaging.MessagingException: Dispatcher failed to deliver Message; nested exception is org.springframework.messaging.core.DestinationResolutionException: no output-channel or replyChannel header available
        at org.springframework.integration.dispatcher.AbstractDispatcher.wrapExceptionIfNecessary(AbstractDispatcher.java:133)
        at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:120)
        at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:148)
        at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:121)
        at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:89)
        at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:425)
        at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:375)
        at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:183)
        at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:162)
        at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:47)
        at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:108)
        at org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice.evaluateFailureExpression(ExpressionEvaluatingRequestHandlerAdvice.java:271)
        at org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice.doInvoke(ExpressionEvaluatingRequestHandlerAdvice.java:221)
        at org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice.invoke(AbstractRequestHandlerAdvice.java:70)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
        at com.sun.proxy.$Proxy190.handleRequestMessage(Unknown Source)
        at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.doInvokeAdvisedRequestHandler(AbstractReplyProducingMessageHandler.java:127)
        at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:112)
        at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:127)
        at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116)
        at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:148)
        at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:121)
        at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:89)
        at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:425)
        at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:375)
        at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:183)
        at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:162)
        at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:47)
        at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:108)
        at org.springframework.integration.router.AbstractMessageRouter.handleMessageInternal(AbstractMessageRouter.java:194)
        at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:127)
        at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116)
        at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:148)
        at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:121)
        at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:89)
        at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:425)
        at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:375)
        at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:183)
        at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:162)
        at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:47)
        at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:108)

I presume I have to add channel for successful execution but I don't want to do that as it is already defined in the flow where the handler is (see .channel(OUTPUT_CHANNEL)). Can I set only the fault channel in the advice?
Thanks!


UPDATE2

I did what Gary recommended (i.e. added an error channel to the upstream flow) and the error message is delivered now to the error channel. However now I have another problem which vaguely relates to my original problem. So now I have the following exception:

2020-05-22 10:10:48,023 ERROR com.acme.webservice.OrchestrationServiceEndpoint Thread=qtp14486859-13 MDC=16d7cc4c-c9da-449b-8bfa-504e6d81185d Error
org.springframework.messaging.MessagingException: failure occurred in error-handling flow; nested exception is org.springframework.messaging.MessageDeliveryException: Dispatcher has no subscribers for channel 'enrich.acmeRequest.output'.; nested exception is org.springframework.integration.MessageDispatchingException: Dispatcher has no subscribers, failedMessage=GenericMessage [payload=uk.co.acme.payload.request._2017._06.Message@4a5e6c, headers={replyChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel@19d4520, errorChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel@19d4520, ws_soapAction=http://www.acme.co.uk/XRTEService/ProcessTran, id=902bd270-89d8-62e9-b00f-b69399241bd1, timestamp=1590138648017}], ...}]
    at org.springframework.integration.gateway.MessagingGatewaySupport.doSendAndReceive(MessagingGatewaySupport.java:489)
    at org.springframework.integration.gateway.MessagingGatewaySupport.sendAndReceiveMessage(MessagingGatewaySupport.java:426)
    at org.springframework.integration.transformer.ContentEnricher$Gateway.sendAndReceiveMessage(ContentEnricher.java:481)
    at org.springframework.integration.transformer.ContentEnricher.handleRequestMessage(ContentEnricher.java:383)
    at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:109)
    at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:127)
    at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116)
    at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:148)
    at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:121)
    at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:89)

In the main flow I have the following:

    return flow -> flow.channel(ORCH_REQUEST_INPUT)
            .<HomeRequest, HomeModel>transform(requestToModelTransformer)
            ...
            // 
            .enrich(this::acmePreCompRequestEnricher)
            .enrich(this::acmePreCompEnricher)
            .handle(this.acmePreCompResponseValidator())
            // 
            .enrich(this::mlRequestEnricher)
            .enrich(this::mlEnricher)
            // 
            .enrich(this::acmeRequestEnricher)
            .enrich(this::acmeEnricher)
            ...

The SOAP fault is thrown in acmePreCompEnricher where the outbound gateway is used. The error channel is set to skip.ml.input then I have to following flow which handles the fault:

    return flow -> flow.channel("skip.ml.input")
            .transform(ErrorMessage.class, (ErrorMessage m) -> {
                        Message originalMessage = ((MessageHandlingException)m.getPayload()).getFailedMessage();
                        return MessageBuilder.withPayload(originalMessage.getHeaders().get(HEADER_CQS_MODEL, HomeQuoteModel.class))
                                .copyHeaders(originalMessage.getHeaders())
                                .build();
                    })
            .channel("enrich.acmeRequest.output");

The enrich.acmeRequest.output channel is the reply channel of acmeRequestEnricher. My intention is if there is no error then execute all the tasks in the flow but if there is a SOAP fault in acmePreCompEnricher then skip the mlRequestEnricher and mlEnricher and go straight to acmeRequestEnricher.
I presume the problem is that in the faul scenario the SI cannot see what subscribed to the channel of acmeRequestEnricher...? What can I do in this case?


Solution

  • As Gary Russell recommended in one of his comments I set an error channel in the upstream flow (see UPDATE2).