4242import java .io .IOException ;
4343import java .io .InputStream ;
4444import java .io .PushbackInputStream ;
45+ import java .util .concurrent .atomic .AtomicBoolean ;
46+ import java .util .logging .Level ;
47+ import java .util .logging .Logger ;
4548
4649import javax .ws .rs .ProcessingException ;
4750
5760 * @author Marek Potociar (marek.potociar at oracle.com)
5861 */
5962public 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