Search code examples
javaspringhttp-headersapache-httpclient-4.xresttemplate

What is the potential issue with below mentioned style of using org.springframework.web.client.RestTemplate RestTemplate?


this is main class of my application

@SpringBootApplication (scanBasePackages = { "com.xyz.*" })
@EnableAsync
@EnableAspectJAutoProxy (proxyTargetClass=true)
@EnableScheduling
public class XyzApplication {

    public static void main(String[] args) {
        SpringApplication.run(XyzApplication.class, args);
    }

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) {
        builder.requestFactory(new HttpComponentsClientHttpRequestFactory());
        return builder.build();
    }
}

In multiple services and components, this RestTemplate is getting autowired.

Like in a controller this is getting used like

@RestController
@RequestMapping({ "my-api" })
public class CommonController {

    @Autowired
    AppConfig appConfig;

    @Autowired
    RestTemplate restTemplate;

    @RequestMapping("check")
    public String pwa() {
        ResponseEntity<String> response = restTemplate.getForEntity(appConfig.getApiConfig().get("ApiURL"), String.class);
        if (HttpStatus.OK == response.getStatusCode()) {
            return response.getBody().toString();
        } else {
            Logger.error(this.getClass(), "Api is not working");
        }

        return null;

    }
}

And in a different service like

@Service
public class DetailsQuery {

    @Autowired
    private AppConfig appConfig;

    @Autowired
    private RestTemplate restTemplate;

    @Async
    public Future<ConcurrentHashMap<String, Object>> getDetails(JSONObject object) throws InterruptedException, RestClientException {

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());

        HttpEntity<String> entity = new HttpEntity<String>(object.toString(), headers);

        Map<String, Object> jsonObject = restTemplate.postForObject(new URI(appConfig.getApiConfig().get("searchApi")), entity, Map.class);

        ConcurrentHashMap<String, Object> response = new ConcurrentHashMap<String, Object>();
        response.putAll(jsonObject);

        return new AsyncResult<ConcurrentHashMap<String,Object>>(new ConcurrentHashMap<>(response));

    }

}

Problem is this implementation throws

There was an unexpected error (type=Internal Server Error, status=500). I/O error on GET request for "http://xx.xxxxxx.xxx/xxxx/xxxx/xxxxxx":

This is getting produced intermitently, eventhough curl request for same works.


Solution

  • Something you could take a look to is you are auto wiring the RestTemplate singleton object and, however, every time the method is called it is adding the same message converter to the resttemplate.

    Bear in mind that resttemplate is thread-safe once constructed, but deal with message converters may not be thread safe after construction. Have a look for example to this thread:

    Is RestTemplate thread safe?

    You could try to do something like below to have a resttemplate instance only for your service

    @Service
    public class DetailsQuery {
    
      private final RestTemplate restTemplate;
    
      @Autowired
      public DetailsQuery (RestTemplateBuilder restTemplateBuilder) {
        this.restTemplate = restTemplateBuilder.additionalMessageConverters(new MappingJackson2HttpMessageConverter()) build();
      }
    
      ....
    }
    

    Or do the same within a @Config class creating the singleton Resttemplate object.