Occasionally I'm getting NPEs wehn using RestTemplate.postForObject(). I could not manually reproduce the issues, as I don't know the case when the headers "seem" to be null.
All I know is that the ClientHttpResponse is not null in those cases and correctly filled with header+body data. The next request with the exact same parameters then works again.
Maybe this has to do with https://github.com/spring-projects/spring-framework/issues/22821, because new HttpHeaders() is initialized with LinkedCaseInsensitiveMap?
Anyways I think adding (or getting) the HttpHeaders should never result in an NPE, this should be caught somewhere down the stack.
java.util.concurrent.CompletionException: java.lang.NullPointerException
at java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:314) ~[?:?]
at java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:319) ~[?:?]
at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1702) ~[?:?]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[?:?]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[?:?]
at java.lang.Thread.run(Thread.java:834) [?:?]
Caused by: java.lang.NullPointerException
at org.springframework.util.CollectionUtils$MultiValueMapAdapter.add(CollectionUtils.java:460) ~[spring-core-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.http.HttpHeaders.add(HttpHeaders.java:1565) ~[spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.http.client.HttpComponentsClientHttpResponse.getHeaders(HttpComponentsClientHttpResponse.java:71) ~[spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.http.client.BufferingClientHttpResponseWrapper.getHeaders(BufferingClientHttpResponseWrapper.java:65) ~[spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.web.client.MessageBodyClientHttpResponseWrapper.getHeaders(MessageBodyClientHttpResponseWrapper.java:115) ~[spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.web.client.MessageBodyClientHttpResponseWrapper.hasMessageBody(MessageBodyClientHttpResponseWrapper.java:66) ~[spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:87) ~[spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:737) ~[spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:670) ~[spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.web.client.RestTemplate.postForObject(RestTemplate.java:414) ~[spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1700) ~[?:?]
Comment From: rstoyanchev
Yes it should not result in an NPE but the cause is likely a bug that should be addressed. What is the header data in such a case? Maybe that can help to reproduce it. Also any chance you could retry with 5.2.0.BULID-SNAPSHOT to confirm if it is related to #22821.
Comment From: membersound
The problem is: it only happens every other day, by chance. So if I'd be using the snapshot (which is not that easy as I'm totally inheriting from spring-boot release), I still can't be sure if it is fixed.
Unfortunately I did not find a way to reproduce the issue at will.
The headers in those cases are nothing special:
Headers: [X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY", Content-Type:"application/json;charset=UTF-8", Content-Length:"1430", Date:"Wed, 08 May 2019 11:55:22 GMT"]
Maybe it has to do with the fact the I'm sending and processing my requests in an async future?
@Autowired
private ThreadPoolExecutor executor;
List<Request> requests;
//the basic idea is to prepare all requests required, then dispatch all req async and collect the responses also async. then wait until all responses have been received.
List<CompletableFuture<Response>> futures = requests
.stream()
.map(req -> CompletableFuture.supplyAsync(
() -> restTemplate.postForEntity(URL, req, Response.class).getBody(), executor)
.collect(Collectors.toList());
List<Response> responses = futures.stream().map(CompletableFuture::join).collect(Collectors.toList());
I also found another log entry, that interestingly throws the NPE also inside an async process. It comes from the following code:
@Service
@Async
public class MyHeaderInterceptor() {
public void traceHeaders(ClientHttpResponse rsp) {
LOGGER.info(message.getHeaders().toString());
}
}
Same issue: occasionally getting NPEs like:
java.lang.NullPointerException: null
at org.springframework.util.CollectionUtils$MultiValueMapAdapter.add(CollectionUtils.java:460) ~[spring-core-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.http.HttpHeaders.add(HttpHeaders.java:1565) ~[spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.http.client.HttpComponentsClientHttpResponse.getHeaders(HttpComponentsClientHttpResponse.java:71) ~[spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.http.client.BufferingClientHttpResponseWrapper.getHeaders(BufferingClientHttpResponseWrapper.java:65) ~[spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at my.logging.MyHeaderInterceptor.traceHeaders(MyHeaderInterceptor.java:116) ~[classes/:?]
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:749) ~[spring-aop-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.aop.interceptor.AsyncExecutionInterceptor.lambda$invoke$0(AsyncExecutionInterceptor.java:115) ~[spring-aop-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[?:?]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[?:?]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[?:?]
at java.lang.Thread.run(Thread.java:834) [?:?]
To be complete, my RestTemplate is build with apache HttpClient as follows:
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
HttpClient httpClient = HttpClientBuilder.create()
.setMaxConnTotal(200)
.setMaxConnPerRoute(100)
.build();
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
factory.setConnectTimeout(2000);
factory.setReadTimeout(10000);
return builder.requestFactory(() -> new BufferingClientHttpRequestFactory(factory)).build();
}
Comment From: bclozel
We didn't get any new reports for this, I'm assuming this has been fixed with #22821.