Skip to content
This repository was archived by the owner on May 28, 2018. It is now read-only.

Commit 0239f91

Browse files
pavelbucekGerrit Code Review
authored andcommitted
Merge "JERSEY-2878: Enabled InputStream.close() on jersey client returned stream by unwrapping InputStream from UnCloseableInputStream in InputStreamProvider, if backing stream KeepAliveStream is used, throwing IOException on access if stream is closed in order to comply with InputStream contract."
2 parents 7c29f5c + 4d7ce9e commit 0239f91

File tree

9 files changed

+546
-19
lines changed

9 files changed

+546
-19
lines changed

core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlConnector.java

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -185,34 +185,71 @@ public InputStream get() throws IOException {
185185
}
186186
});
187187

188+
private volatile boolean closed = false;
189+
190+
/**
191+
* The motivation for this method is to straighten up a behaviour of {@link sun.net.www.http.KeepAliveStream} which
192+
* is used here as a backing {@link InputStream}. The problem is that its access methods (e.g., {@link
193+
* sun.net.www.http.KeepAliveStream#read()}) do not throw {@link IOException} if the stream is closed. This behaviour
194+
* contradicts with {@link InputStream} contract.
195+
* <p/>
196+
* This is a part of fix of JERSEY-2878
197+
* <p/>
198+
* Note that {@link java.io.FilterInputStream} also changes the contract of
199+
* {@link java.io.FilterInputStream#read(byte[], int, int)} as it doesn't state that closed stream causes an {@link
200+
* IOException} which might be questionable. Nevertheless, our contract is {@link InputStream} and as such, the
201+
* stream we're offering must comply with it.
202+
*
203+
* @throws IOException
204+
*/
205+
private void throwIOExceptionIfClosed() throws IOException {
206+
if (closed) {
207+
throw new IOException("Stream closed");
208+
}
209+
}
210+
188211
@Override
189212
public int read() throws IOException {
190-
return in.get().read();
213+
int result = in.get().read();
214+
throwIOExceptionIfClosed();
215+
return result;
191216
}
192217

193218
@Override
194219
public int read(byte[] b) throws IOException {
195-
return in.get().read(b);
220+
int result = in.get().read(b);
221+
throwIOExceptionIfClosed();
222+
return result;
196223
}
197224

198225
@Override
199226
public int read(byte[] b, int off, int len) throws IOException {
200-
return in.get().read(b, off, len);
227+
int result = in.get().read(b, off, len);
228+
throwIOExceptionIfClosed();
229+
return result;
201230
}
202231

203232
@Override
204233
public long skip(long n) throws IOException {
205-
return in.get().skip(n);
234+
long result = in.get().skip(n);
235+
throwIOExceptionIfClosed();
236+
return result;
206237
}
207238

208239
@Override
209240
public int available() throws IOException {
210-
return in.get().available();
241+
int result = in.get().available();
242+
throwIOExceptionIfClosed();
243+
return result;
211244
}
212245

213246
@Override
214247
public void close() throws IOException {
215-
in.get().close();
248+
try {
249+
in.get().close();
250+
} finally {
251+
closed = true;
252+
}
216253
}
217254

218255
@Override
@@ -227,6 +264,7 @@ public void mark(int readLimit) {
227264
@Override
228265
public void reset() throws IOException {
229266
in.get().reset();
267+
throwIOExceptionIfClosed();
230268
}
231269

232270
@Override

core-common/src/main/java/org/glassfish/jersey/message/internal/InputStreamProvider.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
33
*
4-
* Copyright (c) 2010-2014 Oracle and/or its affiliates. All rights reserved.
4+
* Copyright (c) 2010-2015 Oracle and/or its affiliates. All rights reserved.
55
*
66
* The contents of this file are subject to the terms of either the GNU
77
* General Public License Version 2 only ("GPL") or the Common Development
@@ -75,7 +75,7 @@ public InputStream readFrom(
7575
MediaType mediaType,
7676
MultivaluedMap<String, String> httpHeaders,
7777
InputStream entityStream) throws IOException {
78-
return entityStream;
78+
return ReaderInterceptorExecutor.closeableInputStream(entityStream);
7979
}
8080

8181
@Override

core-common/src/main/java/org/glassfish/jersey/message/internal/ReaderInterceptorExecutor.java

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -250,22 +250,17 @@ private Object invokeReadFrom(final ReaderInterceptorContext context, final Mess
250250

251251
final TracingLogger tracingLogger = getTracingLogger();
252252
final long timestamp = tracingLogger.timestamp(MsgTraceEvent.MBR_READ_FROM);
253-
final UnCloseableInputStream stream = new UnCloseableInputStream(input, reader);
253+
final InputStream stream = new UnCloseableInputStream(input, reader);
254254

255255
try {
256-
final Object entity;
256+
return reader.readFrom(context.getType(), context.getGenericType(), context.getAnnotations(),
257+
context.getMediaType(), context.getHeaders(), stream);
258+
} catch (final NoContentException ex) {
257259
if (translateNce) {
258-
try {
259-
entity = reader.readFrom(context.getType(), context.getGenericType(), context.getAnnotations(),
260-
context.getMediaType(), context.getHeaders(), stream);
261-
} catch (final NoContentException ex) {
262-
throw new BadRequestException(ex);
263-
}
260+
throw new BadRequestException(ex);
264261
} else {
265-
entity = reader.readFrom(context.getType(), context.getGenericType(), context.getAnnotations(),
266-
context.getMediaType(), context.getHeaders(), stream);
262+
throw ex;
267263
}
268-
return entity;
269264
} finally {
270265
tracingLogger.logDuration(MsgTraceEvent.MBR_READ_FROM, timestamp, reader);
271266
}
@@ -332,5 +327,25 @@ public void close() throws IOException {
332327
LOGGER.log(Level.FINE, LocalizationMessages.MBR_TRYING_TO_CLOSE_STREAM(reader.getClass()));
333328
}
334329
}
330+
331+
private InputStream unwrap() {
332+
return original;
333+
}
334+
}
335+
336+
/**
337+
* Make the {@link InputStream} able to close.
338+
* <p/>
339+
* The purpose of this utility method is to undo effect of {@link ReaderInterceptorExecutor.UnCloseableInputStream}.
340+
*
341+
* @param inputStream Potential {@link ReaderInterceptorExecutor.UnCloseableInputStream} to undo its effect
342+
* @return Input stream that is possible to close
343+
*/
344+
static InputStream closeableInputStream(InputStream inputStream) {
345+
if (inputStream instanceof ReaderInterceptorExecutor.UnCloseableInputStream) {
346+
return ((ReaderInterceptorExecutor.UnCloseableInputStream) inputStream).unwrap();
347+
} else {
348+
return inputStream;
349+
}
335350
}
336351
}

tests/integration/jersey-2878/pom.xml

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
4+
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
5+
6+
Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved.
7+
8+
The contents of this file are subject to the terms of either the GNU
9+
General Public License Version 2 only ("GPL") or the Common Development
10+
and Distribution License("CDDL") (collectively, the "License"). You
11+
may not use this file except in compliance with the License. You can
12+
obtain a copy of the License at
13+
http://glassfish.java.net/public/CDDL+GPL_1_1.html
14+
or packager/legal/LICENSE.txt. See the License for the specific
15+
language governing permissions and limitations under the License.
16+
17+
When distributing the software, include this License Header Notice in each
18+
file and include the License file at packager/legal/LICENSE.txt.
19+
20+
GPL Classpath Exception:
21+
Oracle designates this particular file as subject to the "Classpath"
22+
exception as provided by Oracle in the GPL Version 2 section of the License
23+
file that accompanied this code.
24+
25+
Modifications:
26+
If applicable, add the following below the License Header, with the fields
27+
enclosed by brackets [] replaced by your own identifying information:
28+
"Portions Copyright [year] [name of copyright owner]"
29+
30+
Contributor(s):
31+
If you wish your version of this file to be governed by only the CDDL or
32+
only the GPL Version 2, indicate your decision by adding "[Contributor]
33+
elects to include this software in this distribution under the [CDDL or GPL
34+
Version 2] license." If you don't indicate a single choice of license, a
35+
recipient has the option to distribute your version of this file under
36+
either the CDDL, the GPL Version 2 or to extend the choice of license to
37+
its licensees as provided above. However, if you add GPL Version 2 code
38+
and therefore, elected the GPL Version 2 license, then the option applies
39+
only if the new code is made subject to such option by the copyright
40+
holder.
41+
42+
-->
43+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
44+
<modelVersion>4.0.0</modelVersion>
45+
46+
<parent>
47+
<groupId>org.glassfish.jersey.tests.integration</groupId>
48+
<artifactId>project</artifactId>
49+
<version>2.21-SNAPSHOT</version>
50+
</parent>
51+
52+
<artifactId>jersey-2878</artifactId>
53+
<packaging>war</packaging>
54+
<name>jersey-tests-integration-jersey-2878</name>
55+
56+
<description>
57+
Reproducer of JERSEY-2878.
58+
</description>
59+
60+
<dependencies>
61+
<dependency>
62+
<groupId>org.glassfish.jersey.containers</groupId>
63+
<artifactId>jersey-container-servlet</artifactId>
64+
</dependency>
65+
66+
<dependency>
67+
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
68+
<artifactId>jersey-test-framework-provider-external</artifactId>
69+
<scope>test</scope>
70+
</dependency>
71+
72+
<dependency>
73+
<groupId>org.glassfish.jersey.connectors</groupId>
74+
<artifactId>jersey-apache-connector</artifactId>
75+
<scope>test</scope>
76+
</dependency>
77+
</dependencies>
78+
79+
<build>
80+
<plugins>
81+
<plugin>
82+
<groupId>org.apache.maven.plugins</groupId>
83+
<artifactId>maven-compiler-plugin</artifactId>
84+
</plugin>
85+
<plugin>
86+
<groupId>org.apache.maven.plugins</groupId>
87+
<artifactId>maven-failsafe-plugin</artifactId>
88+
</plugin>
89+
<plugin>
90+
<groupId>org.mortbay.jetty</groupId>
91+
<artifactId>jetty-maven-plugin</artifactId>
92+
</plugin>
93+
</plugins>
94+
</build>
95+
96+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3+
*
4+
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved.
5+
*
6+
* The contents of this file are subject to the terms of either the GNU
7+
* General Public License Version 2 only ("GPL") or the Common Development
8+
* and Distribution License("CDDL") (collectively, the "License"). You
9+
* may not use this file except in compliance with the License. You can
10+
* obtain a copy of the License at
11+
* http://glassfish.java.net/public/CDDL+GPL_1_1.html
12+
* or packager/legal/LICENSE.txt. See the License for the specific
13+
* language governing permissions and limitations under the License.
14+
*
15+
* When distributing the software, include this License Header Notice in each
16+
* file and include the License file at packager/legal/LICENSE.txt.
17+
*
18+
* GPL Classpath Exception:
19+
* Oracle designates this particular file as subject to the "Classpath"
20+
* exception as provided by Oracle in the GPL Version 2 section of the License
21+
* file that accompanied this code.
22+
*
23+
* Modifications:
24+
* If applicable, add the following below the License Header, with the fields
25+
* enclosed by brackets [] replaced by your own identifying information:
26+
* "Portions Copyright [year] [name of copyright owner]"
27+
*
28+
* Contributor(s):
29+
* If you wish your version of this file to be governed by only the CDDL or
30+
* only the GPL Version 2, indicate your decision by adding "[Contributor]
31+
* elects to include this software in this distribution under the [CDDL or GPL
32+
* Version 2] license." If you don't indicate a single choice of license, a
33+
* recipient has the option to distribute your version of this file under
34+
* either the CDDL, the GPL Version 2 or to extend the choice of license to
35+
* its licensees as provided above. However, if you add GPL Version 2 code
36+
* and therefore, elected the GPL Version 2 license, then the option applies
37+
* only if the new code is made subject to such option by the copyright
38+
* holder.
39+
*/
40+
41+
package org.glassfish.jersey.tests.integration.jersey2878;
42+
43+
import javax.ws.rs.ApplicationPath;
44+
45+
import org.glassfish.jersey.server.ResourceConfig;
46+
47+
/**
48+
* Jersey application for JERSEY-2878.
49+
*
50+
* @author Stepan Vavra (stepan.vavra at oracle.com)
51+
*/
52+
@ApplicationPath("/")
53+
public class TestApplication extends ResourceConfig {
54+
55+
public TestApplication() {
56+
register(TestResource.class);
57+
}
58+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3+
*
4+
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved.
5+
*
6+
* The contents of this file are subject to the terms of either the GNU
7+
* General Public License Version 2 only ("GPL") or the Common Development
8+
* and Distribution License("CDDL") (collectively, the "License"). You
9+
* may not use this file except in compliance with the License. You can
10+
* obtain a copy of the License at
11+
* http://glassfish.java.net/public/CDDL+GPL_1_1.html
12+
* or packager/legal/LICENSE.txt. See the License for the specific
13+
* language governing permissions and limitations under the License.
14+
*
15+
* When distributing the software, include this License Header Notice in each
16+
* file and include the License file at packager/legal/LICENSE.txt.
17+
*
18+
* GPL Classpath Exception:
19+
* Oracle designates this particular file as subject to the "Classpath"
20+
* exception as provided by Oracle in the GPL Version 2 section of the License
21+
* file that accompanied this code.
22+
*
23+
* Modifications:
24+
* If applicable, add the following below the License Header, with the fields
25+
* enclosed by brackets [] replaced by your own identifying information:
26+
* "Portions Copyright [year] [name of copyright owner]"
27+
*
28+
* Contributor(s):
29+
* If you wish your version of this file to be governed by only the CDDL or
30+
* only the GPL Version 2, indicate your decision by adding "[Contributor]
31+
* elects to include this software in this distribution under the [CDDL or GPL
32+
* Version 2] license." If you don't indicate a single choice of license, a
33+
* recipient has the option to distribute your version of this file under
34+
* either the CDDL, the GPL Version 2 or to extend the choice of license to
35+
* its licensees as provided above. However, if you add GPL Version 2 code
36+
* and therefore, elected the GPL Version 2 license, then the option applies
37+
* only if the new code is made subject to such option by the copyright
38+
* holder.
39+
*/
40+
package org.glassfish.jersey.tests.integration.jersey2878;
41+
42+
import javax.ws.rs.GET;
43+
import javax.ws.rs.Path;
44+
45+
/**
46+
* A simple resource that returns a string.
47+
*
48+
* @author Stepan Vavra (stepan.vavra at oracle.com)
49+
*/
50+
@Path("string")
51+
public class TestResource {
52+
53+
@GET
54+
public String string() {
55+
return "Hello, World! "
56+
+ "In production this response would be large, "
57+
+ "and unsuitable for buffering in memory";
58+
}
59+
}

0 commit comments

Comments
 (0)