3

I have a requirement where i need to make a rest call when it fails i have to retry 3 times and depending on the status code received i need perform different action, i couldn't find a proper spring integration dsl example. How to configure the error handler and retry

@Bean
public IntegrationFlow performCreate() {
    return IntegrationFlows.from("createFlow")
            .handle(Http.outboundGateway("http://localhost:8080/create")
                    .httpMethod(HttpMethod.GET)
                    .expectedResponseType(String.class)
                    .requestFactory(simpleClientHttpRequestFactory())
                    .errorHandler(??)
            )

            .log(LoggingHandler.Level.DEBUG, "response", m -> m.getPayload())
            .log(LoggingHandler.Level.DEBUG, "response", m -> m.getHeaders())
            .get();
}

private SimpleClientHttpRequestFactory simpleClientHttpRequestFactory() {
    SimpleClientHttpRequestFactory simpleClientHttpRequestFactory = new SimpleClientHttpRequestFactory();
    simpleClientHttpRequestFactory.setReadTimeout(5000);
    simpleClientHttpRequestFactory.setConnectTimeout(5000);
    return simpleClientHttpRequestFactory;
}
nagendra
  • 593
  • 1
  • 9
  • 29

1 Answers1

7

The Java DSL .handle() has a second argument - Consumer<GenericEndpointSpec<?>> and that one can be configured with the:

/**
 * Configure a list of {@link Advice} objects to be applied, in nested order, to the
 * endpoint's handler. The advice objects are applied only to the handler.
 * @param advice the advice chain.
 * @return the endpoint spec.
 */
public S advice(Advice... advice) {

One of those advises is in the Spring Integration box - RequestHandlerRetryAdvice: https://docs.spring.io/spring-integration/docs/5.0.4.RELEASE/reference/html/messaging-endpoints-chapter.html#retry-advice

https://docs.spring.io/spring-integration/docs/5.0.4.RELEASE/reference/html/java-dsl.html#java-dsl-endpoints

.handle(Http.outboundGateway("http://localhost:8080/create")
                .httpMethod(HttpMethod.GET)
                .expectedResponseType(String.class)
                .requestFactory(simpleClientHttpRequestFactory()),
           e -> e.advice(retryAdvice())

...

@Bean
public RequestHandlerRetryAdvice retryAdvice() {
    RequestHandlerRetryAdvice requestHandlerRetryAdvice = new RequestHandlerRetryAdvice();
    requestHandlerRetryAdvice.setRecoveryCallback(errorMessageSendingRecoverer());
    return requestHandlerRetryAdvice;
}

@Bean
public ErrorMessageSendingRecoverer errorMessageSendingRecoverer() {
    return new ErrorMessageSendingRecoverer(recoveryChannel())
}

@Bean
public MessageChannel recoveryChannel() {
    return new DirectChannel();
}

@Bean
public IntegrationFlow handleRecovery() { 
     return IntegrationFlows.from("recoveryChannel")
                   .log(LoggingHandler.Level.ERROR, "error", 
                        m -> m.getPayload())
                   .get(); 
}
Artem Bilan
  • 113,505
  • 11
  • 91
  • 118
  • Thanks what about error handler ? is there a way to route based on http response status code – nagendra Apr 12 '18 at 17:07
  • 1
    That's what can be done in the flow on that `recoveryChannel`. You get there an `ErrorMessage` with an exception as a `payload` and you can extract from there `HttpClientErrorException` with its `statusCode` to route. – Artem Bilan Apr 12 '18 at 17:17
  • I tried with `requestHandlerRetryAdvice.setRecoveryCallback(new ErrorMessageSendingRecoverer(MessageChannels.direct("recoveryChannel").get()));` and `@Bean public IntegrationFlow handleRecovery() { return IntegrationFlows.from("recoveryChannel") .log(LoggingHandler.Level.ERROR, "error", m -> m.getPayload()) .get(); }` but i am seeing the following error Caused by: `org.springframework.integration.MessageDispatchingException: Dispatcher has no subscribers` – nagendra Apr 13 '18 at 11:16
  • That's because the `ErrorMessageSendingRecoverer` must accept a `bean reference`, but you use now a factory result, which is not a bean and doesn't have any connection with the `IntegrationFlow` defined below. So, why don't declare a `@Bean` for that `recoveryChannel` `MessageChannel` and don't use it from the `ErrorMessageSendingRecoverer`? – Artem Bilan Apr 13 '18 at 16:47
  • I added a `recoveryChannel()` bean definition to the same in my answer. – Artem Bilan Apr 13 '18 at 16:48
  • But how do i subscribe to `recoveryChannel()` in order to process the error message – nagendra Apr 16 '18 at 08:52
  • The same way as you show in that your comment: `IntegrationFlows.from(“recoveryChannel”)` – Artem Bilan Apr 16 '18 at 11:12
  • I tried with that as well, i am seeing the following error `org.springframework.messaging.MessageDeliveryException: Dispatcher has no subscribers for channel 'unknown.channel.name'.;` `Caused by: org.springframework.integration.MessageDispatchingException: Dispatcher has no subscribers Caused by: org.springframework.integration.MessageDispatchingException: Dispatcher has no subscribers` – nagendra Apr 16 '18 at 11:51
  • I have edited my answer with the mentioned `IntegrationFlow` `@Bean` – Artem Bilan Apr 16 '18 at 11:55
  • still see at runtime `...MessageDeliveryException: Dispatcher has no subscribers for channel 'unknown.channel.name'.; nested exception is org...MessageDispatchingException: Dispatcher has no subscribers, failedMessage=ErrorMessage [payload=org...MessageHandlingException: HTTP request execution failed for URI; nested exception is org..ResourceAccessException: I/O error on POST request for "/create":nested exception is java.net.SocketTimeoutException: Read timed out, failedMessage=GenericMessage [payload=E@1b,headers{replyChannel=org...GenericMessagingTemplate$TemporaryReplyChannel@` – nagendra Apr 16 '18 at 22:39
  • That `unknown.channel.name` means that you send a message (`ErrorMessage` in this case) to not a `@Bean`. Would you mind to share a project I can reproduce and play? – Artem Bilan Apr 17 '18 at 00:20
  • 1
    Uh! That's great that you have shared the project. Your problem that `@Bean` methods and their references to each other are not declared in the class marked with the `@Configuration`. The `@Component` on the `FlowConfiguration` is not enough for whole Dependency Injection lifecycle. What you have so far is just *lite bean mode*, but with that you can't use bean method references: https://docs.spring.io/spring/docs/5.0.5.RELEASE/spring-framework-reference/core.html#beans-java – Artem Bilan Apr 17 '18 at 23:24