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

Commit 9fe1a89

Browse files
author
Marek Potociar
committed
Implemented a workaround for JRFCAF-1344.
- Underlying entity stream close() implementation may be thread-unsafe and as such the close() may result in an IOException at the socket input stream level, if the close() gets called at once from multiple threads somehow. Change-Id: I6b3d05504d182515d001cc6244e8f0d8eee9ce36 Signed-off-by: Marek Potociar <[email protected]>
1 parent f1975b3 commit 9fe1a89

File tree

1 file changed

+32
-23
lines changed

1 file changed

+32
-23
lines changed

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

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@
4242
import java.io.IOException;
4343
import java.io.InputStream;
4444
import java.io.PushbackInputStream;
45+
import java.util.concurrent.atomic.AtomicBoolean;
46+
import java.util.logging.Level;
47+
import java.util.logging.Logger;
4548

4649
import javax.ws.rs.ProcessingException;
4750

@@ -57,8 +60,10 @@
5760
* @author Marek Potociar (marek.potociar at oracle.com)
5861
*/
5962
public class EntityInputStream extends InputStream {
60-
private InputStream input;
61-
private boolean closed;
63+
private static final Logger LOGGER = Logger.getLogger(EntityInputStream.class.getName());
64+
65+
private volatile InputStream input;
66+
private final AtomicBoolean closed = new AtomicBoolean(false);
6267

6368
/**
6469
* Create an entity input stream instance wrapping the original input stream.
@@ -144,21 +149,24 @@ public void reset() {
144149
* {@inheritDoc}
145150
* <p>
146151
* The method is customized to not throw an {@link IOException} if the close operation fails. Instead,
147-
* a runtime {@link javax.ws.rs.ProcessingException} is thrown.
152+
* a warning message is logged.
148153
* </p>
149-
*
150-
* @throws javax.ws.rs.ProcessingException
151-
* in case the close operation on the underlying entity input stream failed.
152154
*/
153155
@Override
154156
public void close() throws ProcessingException {
155-
if (!closed && input != null) {
157+
final InputStream in = input;
158+
if (in == null) {
159+
return;
160+
}
161+
if (closed.compareAndSet(false, true)) {
162+
// Workaround for JRFCAF-1344: Underlying stream close() may be thread-unsafe
163+
// and as such the close() may result in an IOException at the socket input stream level,
164+
// if the close() gets called at once from multiple threads somehow.
156165
try {
157-
input.close();
166+
in.close();
158167
} catch (IOException ex) {
159-
throw new ProcessingException(LocalizationMessages.MESSAGE_CONTENT_INPUT_STREAM_CLOSE_FAILED(), ex);
160-
} finally {
161-
closed = true;
168+
// This means that the underlying socket stream got closed by other thread somehow
169+
LOGGER.log(Level.FINE, LocalizationMessages.MESSAGE_CONTENT_INPUT_STREAM_CLOSE_FAILED(), ex);
162170
}
163171
}
164172
}
@@ -174,36 +182,37 @@ public void close() throws ProcessingException {
174182
public boolean isEmpty() {
175183
ensureNotClosed();
176184

177-
if (input == null) {
185+
final InputStream in = input;
186+
if (in == null) {
178187
return true;
179188
}
180189

181190
try {
182191
// Try #markSupported first - #available on WLS waits until socked timeout is reached when chunked encoding is used.
183-
if (input.markSupported()) {
184-
input.mark(1);
185-
int i = input.read();
186-
input.reset();
192+
if (in.markSupported()) {
193+
in.mark(1);
194+
int i = in.read();
195+
in.reset();
187196
return i == -1;
188197
} else {
189198
try {
190-
if (input.available() > 0) {
199+
if (in.available() > 0) {
191200
return false;
192201
}
193202
} catch (IOException ioe) {
194203
// NOOP. Try other approaches as this can fail on WLS.
195204
}
196205

197-
int b = input.read();
206+
int b = in.read();
198207
if (b == -1) {
199208
return true;
200209
}
201210

202211
PushbackInputStream pbis;
203-
if (input instanceof PushbackInputStream) {
204-
pbis = (PushbackInputStream) input;
212+
if (in instanceof PushbackInputStream) {
213+
pbis = (PushbackInputStream) in;
205214
} else {
206-
pbis = new PushbackInputStream(input, 1);
215+
pbis = new PushbackInputStream(in, 1);
207216
input = pbis;
208217
}
209218
pbis.unread(b);
@@ -221,7 +230,7 @@ public boolean isEmpty() {
221230
* @throws IllegalStateException in case the entity input stream has been closed.
222231
*/
223232
public void ensureNotClosed() throws IllegalStateException {
224-
if (closed) {
233+
if (closed.get()) {
225234
throw new IllegalStateException(LocalizationMessages.ERROR_ENTITY_STREAM_CLOSED());
226235
}
227236
}
@@ -232,7 +241,7 @@ public void ensureNotClosed() throws IllegalStateException {
232241
* @return {@code true} if the stream has been closed, {@code false} otherwise.
233242
*/
234243
public boolean isClosed() {
235-
return closed;
244+
return closed.get();
236245
}
237246

238247
/**

0 commit comments

Comments
 (0)