1

We have a spring boot Application which makes RESTFul calls to a bunch of backends, one of them returns null reponses at times, and we are observing the connections are not released during these instances because of this code in RestTemplate class:

protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback,
            ResponseExtractor<T> responseExtractor) throws RestClientException {

        Assert.notNull(url, "'url' must not be null");
        Assert.notNull(method, "'method' must not be null");
        ClientHttpResponse response = null;
        try {
            ClientHttpRequest request = createRequest(url, method);
            if (requestCallback != null) {
                requestCallback.doWithRequest(request);
            }
            response = request.execute();
            handleResponse(url, method, response);
            if (responseExtractor != null) {
                return responseExtractor.extractData(response);
            }
            else {
                return null;
            }
        }
        catch (IOException ex) {
            String resource = url.toString();
            String query = url.getRawQuery();
            resource = (query != null ? resource.substring(0, resource.indexOf('?')) : resource);
            throw new ResourceAccessException("I/O error on " + method.name() +
                    " request for \"" + resource + "\": " + ex.getMessage(), ex);
        }
        finally {
            if (response != null) {
                response.close();
            }
        }
    }

Is there a way we can release the connection or consume the contents for when response is null or erring out?

Edited to add code causing errors:

MyHttpClientClass{ 
private X getResponseBody(RestClient client, URI uri, HttpMethod method, HttpEntity<T> entity, Class<R> responseType, MyErrorHandler errorHandler) {        
  try 
   {            String host = this.getHost();                        
      ResponseEntity<X> resp = client.exchange(uri, method, entity, responseType);          
    return resp.getBody();      
    } catch (HttpServerErrorException | HttpClientErrorException e) 
{           handleHttpException(e, errorHandler);           
throw e;        
} catch (Exception e) {             
log(e);             
throw e;        }   }   }

-----------

Class1 implements Callable<T>
{ 
 @Override  public T doCall() throws Exception { 
try 
{   return this.getRestClient().exchange(this.getUri(),                      
    this.getHttpMethod(), this.getEntity(), getResponseType()).getBody();       } catch (HttpClientErrorException ex) {                         throw ex; } catch (HttpStatusCodeException ex) {                        if(this.isNeededRetry(ex)) {                throw ex;           }else {                 return generateErrorResponse(ex).getBody();     }                   } catch (RestClientException ex) {              throw ex;       } catch (Exception ex) {            throw ex; } } }

----------
MySpringApplicationClass{  
   public X get(String api, String params, Class<R> responseType, String path, List<String> pathVariables, MyErrorHandler errorHandler) 
{ 
    return getResponseBody(...);
}}
jaywal
  • 21
  • 6
  • How are you going to consume response when it's null? – Hasan Can Saral Aug 17 '20 at 13:47
  • Yes, not sure. How should we handle the connection releases in such instances? We cannot control what the backend returns. – jaywal Aug 17 '20 at 14:00
  • Sorry I got confused. I thought the body of the response is null, wrote my answer accordingly. And since this is `RestTemplate` code, we cannot actually modify the underlying logic. Can you show **your** code? – Hasan Can Saral Aug 17 '20 at 14:10
  • This how we are using the restClient object (which extends RestTemplate) ` public T myExceute throws Exception { try { return getRestClient().exchange(request.getUri(), request.getHttpMethod(), request.getEntity(), request.getResponseType()).getBody(); } catch (HttpClientErrorException ex) { throw ex; } catch (HttpStatusCodeException ex) { if(this.isNeededRetry(ex)) { throw ex; ........ } catch (RestClientException ex) { throw ex; } catch (Exception ex) { throw ex; } } ` (sorry cannot format it) – jaywal Aug 17 '20 at 14:17
  • `try { return getRestClient().exchange(request.getUri(), request.getHttpMethod(), request.getEntity(), request.getResponseType()).getBody(); } catch (HttpClientErrorException ex) { throw ex; } catch (HttpStatusCodeException ex) { throw ex; }.... } catch (RestClientException ex) { throw ex; } catch (Exception ex) { throw ex; }` – jaywal Aug 17 '20 at 14:38
  • @HasanCanSaral Does this code help? – jaywal Aug 17 '20 at 16:07
  • @jaywal If you are using java 8, try to use **try with resources block**. If not, try to GC(garbage collect) all the objects in finally block. You are only nullify response object. – Dinesh Dontha Aug 17 '20 at 17:55
  • @DineshDontha We are using the traditional try-catch block and dont have the `ClientHttpResponse` response object with us to explicitly close the connection. The spring RestTemplate class though does have a finally to close the connection as above, but does it only for non null responses. – jaywal Aug 17 '20 at 18:11
  • @jaywal Where this code is? In a legacy jar? Why do you have to use this legacy jar/code. when you are already developing in Spring Boot App? you can autowire RestTemplate right. – Dinesh Dontha Aug 17 '20 at 18:23
  • @DineshDontha It is not a legacy jar, it already autoWires all the restTemplate components (restClient in this case which extends restTemplate) please see this code here: `return getRestClient().exchange(request.getUri(),` `request.getHttpMethod(),` `request.getEntity(),` `request.getResponseType()).getBody(); ` Just posting a small section of the whole class, We are enclosing it in a try-catch block, even if we did autowire RestClient directly, it will be same issue unless I am missing something? – jaywal Aug 17 '20 at 18:44
  • Even if I did write a custom try with resources block, I will still have to put in the same condition which RestTemplate class has for example `@Override public void close() throws Exception { if(response != null) response.close(); }` which is essentially what Spring is doing – jaywal Aug 17 '20 at 19:00
  • You have added only class. Please put all the code. Class which is using this method. And please let me, how you are deciding connections are opened. How are seeing them ? – Dinesh Dontha Aug 17 '20 at 19:06
  • Adding my classes code here: `MyHttpClientClass{ private X getResponseBody(RestClient client, URI uri, HttpMethod method, HttpEntity entity, Class responseType, MyErrorHandler errorHandler) { try { String host = this.getHost(); ResponseEntity resp = client.exchange(uri, method, entity, responseType); return resp.getBody(); } catch (HttpServerErrorException | HttpClientErrorException e) { handleHttpException(e, errorHandler); throw e; } catch (Exception e) { log(e); throw e; } } }` – jaywal Aug 17 '20 at 19:45
  • client.exchange above calls doCall below `Class1 implements implements Callable{ @Override public T doCall() throws Exception { try { return this.getRestClient().exchange(this.getUri(), this.getHttpMethod(), this.getEntity(), getResponseType()).getBody(); } catch (HttpClientErrorException ex) { throw ex; } catch (HttpStatusCodeException ex) { if(this.isNeededRetry(ex)) { throw ex; }else { return generateErrorResponse(ex).getBody(); } } catch (RestClientException ex) { throw ex; } catch (Exception ex) { throw ex; } } }` – jaywal Aug 17 '20 at 19:46
  • `MySpringApplicationClass{ public X get(String api, String params, Class responseType, String path, List pathVariables, MyErrorHandler errorHandler) { return getResponseBody(...);}}` I am assuming connections are open because I am seeing ConnectionPoolTimeoutExceptions in production environment and I can see from logs that threads to above client are not always released. It reached the connectionPool limit (I have tried 20 to 200 and my API is not even that heavily used) and when it reaches my-thread-200, the poolException start coming up. @DineshDontha Hope this helps! – jaywal Aug 17 '20 at 19:48
  • Also, checking other post and see someone here posted a similar issue and the last comment does say that they added a finally block, but no more details: https://stackoverflow.com/questions/31597696/resttemplate-connectionpooltimeoutexception-timeout-waiting-for-connection-fr – jaywal Aug 17 '20 at 20:43
  • @DineshDontha Did you get a chance to look at our code? – jaywal Aug 18 '20 at 13:28
  • @Jaywal I am assuming, if you can change the code in the above block, once try to nullify all the created objects (the request objects) like response objects in finally block. For Example: `request =null`. otherwise, I think someone else might help here. – Dinesh Dontha Aug 18 '20 at 13:56
  • @Jaywal update the title with exact exception pls. – Dinesh Dontha Aug 18 '20 at 14:01
  • @DineshDontha Done. Thanks! – jaywal Aug 18 '20 at 14:22

0 Answers0