Open glassfishrobot opened 7 years ago
Reported by mkull
predatorvi said: [Edit] Using Jersey 2.25.1 (client, core, apache-connector, entity-filtering, guava, media-json-jackson, media-multipart)
Summary of my experience with the same exception as noted above:
Detail: I recently made updates in my code to use PoolingHttpClientConnectionManager along with the ApacheConnectorProvider to improve scalability for test automation. My initialization code:
**RestClientJersey.RestClientJersey()** public RestClientJersey(int timeout, int chunkSize) {
SSLContext ctx = initSSLTrustFactory();
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", new SSLConnectionSocketFactory(ctx, NoopHostnameVerifier.INSTANCE))
.build();
ClientConfig cfg = new ClientConfig();
PoolingHttpClientConnectionManager connMan = new PoolingHttpClientConnectionManager(registry);
connMan.setMaxTotal(200);
connMan.setDefaultMaxPerRoute(50);
if (timeout != -1) {
SocketConfig sc = SocketConfig.custom()
.setSoTimeout(timeout)
.setTcpNoDelay(true)
.setSoReuseAddress(true)
.build();
connMan.setDefaultSocketConfig(sc);
cfg.property(ApacheClientProperties.REQUEST_CONFIG, RequestConfig
.custom()
.setConnectTimeout(timeout)
.setSocketTimeout(timeout)
.build()
);
}
cfg.property(ApacheClientProperties.CONNECTION_MANAGER, connMan);
ApacheConnectorProvider acp = new ApacheConnectorProvider();
cfg.connectorProvider(acp);
ClientBuilder builder = ClientBuilder.newBuilder();
client = builder
.hostnameVerifier((String hostname, SSLSession session) -> true)
.withConfig(cfg)
.register(JacksonFeature.class)
.register(MultiPartWriter.class)
.build();
}
My cleanup code below is called before attempting another request. I store the last Response on a per-thread basis so my tests don't have to worry about cleaning up and I can make sure it is closed, regardless of whether the entity was consumed or not.
**myJerseyClient.java:responseCleanup()** /**
* If previous request didn't need/want the entity, make sure previous request
* is closed to free up resources.
*/
private void responseCleanup() {
Response r = getLastResponse();
if (r != null) {
r.close(); //Should be idempotent and can be called again w/out issue
}
}
This worked fine for HTTPS requests, presumably using the SSLConnectionSocketFactory. However, when switching to HTTP (via the PlainConnectionSocketFactory), the exception occurs.
I changed my responseCleanup() method to be:
**myJerseyClient.java:fresponseCleanup()** private void responseCleanup() {
Response r = getLastResponse();
if (r != null) {
r.close();
lastResponse.put(Thread.currentThread().getId(), null); //Works now if I prevent more than one call to close()
}
}
An additional scenario I noticed has issues is after calling readEntity(Class
**myJerseyClient.java:bufferEntitySnippet** T retInstance = null;
if (returnType != null) {
r.bufferEntity();
retInstance = r.readEntity(returnType);
if (retInstance instanceof InjectRawPayload) {
((InjectRawPayload) retInstance).setRawPayload(r.readEntity(String.class));
}
}
return retInstance;
However, when I call readEntity(String.class) to retrieve the raw JSON, an exception is thrown.
java.lang.IllegalStateException: Entity input stream has already been closed.
at org.glassfish.jersey.message.internal.EntityInputStream.ensureNotClosed(EntityInputStream.java:228)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:854)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:808)
at org.glassfish.jersey.client.ClientResponse.readEntity(ClientResponse.java:326)
at org.glassfish.jersey.client.InboundJaxrsResponse$1.call(InboundJaxrsResponse.java:115)
at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
at org.glassfish.jersey.internal.Errors.process(Errors.java:228)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:419)
at org.glassfish.jersey.client.InboundJaxrsResponse.runInScopeIfPossible(InboundJaxrsResponse.java:267)
at org.glassfish.jersey.client.InboundJaxrsResponse.readEntity(InboundJaxrsResponse.java:112)
...
Issue-Links: is cloned by JERSEY-3233
This issue was imported from java.net JIRA JERSEY-3214
3124 seems to have introduced a bug, with response.close() throwing an unexpected ProcessingException if the response has not been consumed. That is quite unfortunate because this ProcessingException overrides any original exception if close is called inside a finally-block. (And try-with-resources is not yet supported by JAX-RS 2.0).
Seems to affect Jersey 2.22.2 upwards; experienced it in jersey 2.23.1
Reproducable by this UnitTest:
The thrown exception seems like a bug to me because #3124 fully intended to prematurely close the connection instead of consuming it. An Exception would not make sense. There is also the javadoc of Response#close():
The bug seems to be caused by Jersey closing both HttpResponse and response-Stream. First closing the HttpResponse closes the socket-connection. The second close of the response-stream tries to consume the remaining body, but the socket is already closed, which is considered a protocol error by apache httpclient. Not closing the stream probably circumvents the exception.
I may prepare a PullRequest if i get the OK from my higher-ups to sign OCA (real PITA).