1

Here I'm using Spring integration's http outbound gateway to make a http call. I have also added timeout for the call. I have configured the timeout using restemplate. Here whenever it's taking more time then it's throwing an ResourceAccessException but I want to handle that exception and want to send proper msg to the user. Even though I'm using a custom error handler but the exception is not getting handled by that. Below the code where I'm using scatter gather pattern and flow2 is the http call where I want to handle the exception-

@Autowired private RestTemplateConfig resttemplateconfig.
 @Bean
  public IntegrationFlow mainFlow() {
    return flow ->
        flow.split()
            .channel(c -> c.executor(Executors.newCachedThreadPool()))
            .scatterGather(
                scatterer ->
                    scatterer
                        .applySequence(true)
                        .recipientFlow(flow1())
                        .recipientFlow(flow2()),
                gatherer ->
                    gatherer
                        .releaseLockBeforeSend(true)
                        .releaseStrategy(group -> group.size() == 1))
            .aggregate()
            .to(saveCDResponseToDB());
  }

  @Bean
  public IntegrationFlow flow2() {
    return flow ->
        flow.channel(c -> c.executor(Executors.newCachedThreadPool()))
            .handle(
                Http.outboundGateway(
                        "http://localhost:4444/test", resttemplateconfig.restTemplate())
                    .extractPayload(true)
                    .httpMethod(HttpMethod.POST)
                    .expectedResponseType(String.class));
  }

//RestTemplateConfig - The Resttemplate Config class where I'm setting the timeout and errorhandler.

 @Configuration
 public class RestTemplateConfig {
  private final int TIMEOUT = (int) TimeUnit.SECONDS.toMillis(6);
  @Autowired CustomErrorHandler errorHandler;

  @Bean
  public RestTemplate restTemplate() {

    HttpComponentsClientHttpRequestFactory requestFactory =
        new HttpComponentsClientHttpRequestFactory();
    requestFactory.setConnectTimeout(TIMEOUT);
    requestFactory.setReadTimeout(TIMEOUT);
    RestTemplate restTemplate = new RestTemplate(requestFactory);
    errorHandler.setMessageConverters(restTemplate.getMessageConverters());
    restTemplate.setErrorHandler(errorHandler);
    return restTemplate;
  }
 }

//custom error handler

@Component
public class CustomServiceErrorHandler implements ResponseErrorHandler {
  private List<HttpMessageConverter<?>> messageConverters;

  @Override
  public boolean hasError(ClientHttpResponse response) throws IOException {
    return hasError(response.getStatusCode());
  }

  protected boolean hasError(HttpStatus statusCode) {
    return (statusCode.is4xxClientError() || statusCode.is5xxServerError());
  }

  @Override
  public void handleError(ClientHttpResponse httpResponse) throws IOException {

    if (httpResponse.getStatusCode().series() == SERVER_ERROR) {
      // handle SERVER_ERROR
      System.out.println("SERVER_ERROR");
    } else if (httpResponse.getStatusCode().series() == CLIENT_ERROR) {
      // handle CLIENT_ERROR
      System.out.println("CLIENT_ERROR");
    } else {
      System.out.println("SOME_ERROR");
    }
  }

  public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
    this.messageConverters = messageConverters;
  }
}

I'm getting below error that I want to handle -

Caused by: org.springframework.web.client.ResourceAccessException: I/O error on POST request for "http://localhost:4444/test": Read timed out; nested exception is java.net.SocketTimeoutException: Read timed out

If I'm adding .errorHandler(new CustomErrorHandler()) in Http.outboundgateway then I'm getting error saying -

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'flow2' defined in class path resource [example/common/config/SpringIntegrationConfiguration.class]: Initialization of bean failed; nested exception is java.lang.RuntimeException: java.lang.IllegalArgumentException: the 'errorHandler' must be specified on the provided 'restTemplate'

1 Answers1

1

the 'errorHandler' must be specified on the provided 'restTemplate'

It's probably clearly states that such an error handler must be configured on the externally provided RestTemplate.

When an IOException is thrown in the RestTemplate, that error handler is not invoked. See the source code of its doExecute() method:

   ...
        response = request.execute();
        handleResponse(url, method, response);
        return (responseExtractor != null ? responseExtractor.extractData(response) : null);
    }
    catch (IOException ex) {
        ...
        throw new ResourceAccessException("I/O error on " + method.name() +
                " request for \"" + resource + "\": " + ex.getMessage(), ex);

This type of exceptions has to be handled around that HTTP Gateway call. See if you can set a custom errorChannel header - enrichHeaders() and handle this error in the dedicated IntegrationFlow. Since you use an ExecutorChannel, the async error handling must have an effect.

Another way (and I recall as showed you before) is to use an ExpressionEvaluatingRequestHandlerAdvice on that Http.outboundGateway() endpoint. See more in docs: https://docs.spring.io/spring-integration/reference/html/messaging-endpoints.html#message-handler-advice-chain

Artem Bilan
  • 113,505
  • 11
  • 91
  • 118
  • Thanks , I'm using ExpressionEvaluatingRequestHandlerAdvice but here I'm getting one issue i.e., I'm delegating the failure to the failure flow as shown in the official documentation. But inside the failure flow i'm doing .handle((p,h) -> throw new MyCustomException(ErrorCode.SOME_ERROR.getErrorData()););. In log I can see it's throwing the exception but I've my own handler method in project and that's not handling tht exception. What to do? Is it because of the scatter-gather pattern? – Somnath Mukherjee Jul 30 '22 at 07:29
  • So, you catch that resource exception properly , but then you re-throw some other exception from there? What error handlers are expecting to catch this one? That hHTTP error handler is out of scope already . Since you use an executor channel this new exception is landed in the default `errorChannel` and flow stops here. It is not clear what is your task from the scatter-gather perspective in this new error case… – Artem Bilan Jul 30 '22 at 12:10
  • I'll ask another question on that with code. Kindly see to that. – Somnath Mukherjee Jul 30 '22 at 14:14