Sahil Chhabra
Sahil Chhabra

Reputation: 11666

Add Custom ClientHttpRequestInterceptor in RestTemplateBuilder with HttpComponentsClientHttpRequestFactory

I need to add a Custom Header in all my RestTemplate Client requests. So I implemented ClientHttpRequestInterceptor. And I add the interceptor in my RestTemplateBuilder config like shown below. The problem is that when the RestTemplate makes the HTTP call it throws following exception:

java.lang.ClassCastException: org.springframework.http.client.InterceptingClientHttpRequestFactory cannot be cast to org.springframework.http.client.HttpComponentsClientHttpRequestFactory

RestTemplate Bean Creation :

@Bean
  public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
    PoolingHttpClientConnectionManager poolingConnectionManager = new PoolingHttpClientConnectionManager();
    poolingConnectionManager.setMaxTotal(restTemplateProps.getMaxConnectionsPerPool());
    poolingConnectionManager.setDefaultMaxPerRoute(restTemplateProps.getMaxDefaultConnectionPerRoute());
    CloseableHttpClient client = HttpClientBuilder.create().setConnectionManager(poolingConnectionManager).build();
    ClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(client);
    restTemplateBuilder = restTemplateBuilder.additionalInterceptors(new MyClientHttpRequestInterceptor());
    return restTemplateBuilder.requestFactory(clientHttpRequestFactory).build();
  }

Also, I am updating the timeouts later in below code:

  protected void setRestTemplateTimeouts() {

    HttpComponentsClientHttpRequestFactory rf =
        (HttpComponentsClientHttpRequestFactory) restTemplate.getRequestFactory();
    rf.setConnectTimeout(restTemplateProps.getConnectionTimeout());
    rf.setReadTimeout(restTemplateProps.getSocketTimeout());
  }

Can anyone help me fix this?

Upvotes: 2

Views: 10370

Answers (2)

Dev Fh
Dev Fh

Reputation: 644

This is how i manage to get the interceptor to log both request and response without throwing exception - Attempted read from closed stream.

 @Bean
 public RestTemplate getRestTemplateConfig()
            throws KeyStoreException, IOException, UnrecoverableKeyException, CertificateException, NoSuchAlgorithmException, KeyManagementException {


        SSLContext context = SSLContextBuilder
                .create()
                .loadKeyMaterial(ResourceUtils.getFile("/opt/cert/keystore.jks"), 
                        "password".toCharArray(),
                        "password".toCharArray())
                .build();

        HttpClient client = HttpClients
                .custom()
                .setSSLContext(context)
                .build();

        HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
        requestFactory.setHttpClient(client);
        RestTemplate restTemplate = new RestTemplate(requestFactory);

       //Provide a buffer for the outgoing/incoming stream, allowing the response body to be read multiple times
        // (if not configured, the interceptor reads the Response stream, and then returns body=null when responding to the data)
        restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(requestFactory));
        restTemplate.setErrorHandler(new RestTemplateResponseErrorHandler());
        restTemplate.setInterceptors(Collections.<ClientHttpRequestInterceptor>singletonList(
                new RestTemplateInterceptor()));
        restTemplate.getMessageConverters().add(jacksonSupportsMoreTypes());
        return restTemplate;
    }

    private HttpMessageConverter jacksonSupportsMoreTypes() {
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        converter.setSupportedMediaTypes(Arrays.asList( MediaType.APPLICATION_OCTET_STREAM));
        return converter;
    }

Upvotes: 1

Sahil Chhabra
Sahil Chhabra

Reputation: 11666

The problem was, I was trying to set the connect and read timeouts after setting the ClientHttpRequestInterceptor.

In my setRestTemplateTimeouts() method when I try to fetch & typecast requestFactory to HttpComponentsClientHttpRequestFactory, I get the ClassCastException exception because restTemplate.getRequestFactory() returns InterceptingClientHttpRequestFactory instead of HttpComponentsClientHttpRequestFactory. This is because I added an interceptor in my restTemplate object.

Solution is to set the timeouts before setting interceptor because you can't set timeouts after setting an interceptor. Refer the code below:

@Bean
  public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
    PoolingHttpClientConnectionManager poolingConnectionManager = new PoolingHttpClientConnectionManager();
    poolingConnectionManager.setMaxTotal(restTemplateProps.getMaxConnectionsPerPool());
    poolingConnectionManager.setDefaultMaxPerRoute(restTemplateProps.getMaxDefaultConnectionPerRoute());
    CloseableHttpClient client = HttpClientBuilder.create().setConnectionManager(poolingConnectionManager).build();
    HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(client);
    clientHttpRequestFactory.setConnectTimeout(restTemplateProps.getConnectionTimeout());
    clientHttpRequestFactory.setReadTimeout(restTemplateProps.getSocketTimeout());
    restTemplateBuilder = restTemplateBuilder.additionalInterceptors(new MyClientHttpRequestInterceptor());
    return restTemplateBuilder.requestFactory(clientHttpRequestFactory).build();
  }

Upvotes: 5

Related Questions