8585 * @author Marek Potociar (marek.potociar at oracle.com)
8686 */
8787public abstract class InboundMessageContext {
88+
8889 private static final InputStream EMPTY = new InputStream () {
8990
9091 @ Override
@@ -121,6 +122,7 @@ public boolean markSupported() {
121122 * is used to control the execution of interceptors.
122123 */
123124 private static class EntityContent extends EntityInputStream {
125+
124126 private boolean buffered ;
125127
126128 EntityContent () {
@@ -266,7 +268,7 @@ private static List<String> iterableToList(final Iterable<?> values) {
266268
267269 /**
268270 * Get a message header as a single string value.
269- *
271+ * <p/>
270272 * Each single header value is converted to String using a
271273 * {@link javax.ws.rs.ext.RuntimeDelegate.HeaderDelegate} if one is available
272274 * via {@link javax.ws.rs.ext.RuntimeDelegate#createHeaderDelegate(java.lang.Class)}
@@ -275,10 +277,10 @@ private static List<String> iterableToList(final Iterable<?> values) {
275277 *
276278 * @param name the message header.
277279 * @return the message header value. If the message header is not present then
278- * {@code null} is returned. If the message header is present but has no
279- * value then the empty string is returned. If the message header is present
280- * more than once then the values of joined together and separated by a ','
281- * character.
280+ * {@code null} is returned. If the message header is present but has no
281+ * value then the empty string is returned. If the message header is present
282+ * more than once then the values of joined together and separated by a ','
283+ * character.
282284 */
283285 public String getHeaderString (String name ) {
284286 List <String > values = this .headers .get (name );
@@ -400,7 +402,7 @@ public Set<MatchingEntityTag> getIfNoneMatch() {
400402 /**
401403 * Get the language of the entity.
402404 *
403- * @return the language of the entity or {@code null} if not specified
405+ * @return the language of the entity or {@code null} if not specified.
404406 */
405407 public Locale getLanguage () {
406408 return singleHeader (HttpHeaders .CONTENT_LANGUAGE , new Function <String , Locale >() {
@@ -418,8 +420,7 @@ public Locale apply(String input) {
418420 /**
419421 * Get Content-Length value.
420422 *
421- * @return Content-Length as integer if present and valid number. In other
422- * cases returns -1.
423+ * @return Content-Length as integer if present and valid number. In other cases returns -1.
423424 */
424425 public int getLength () {
425426 return singleHeader (HttpHeaders .CONTENT_LENGTH , new Function <String , Integer >() {
@@ -438,7 +439,7 @@ public Integer apply(String input) {
438439 * Get the media type of the entity.
439440 *
440441 * @return the media type or {@code null} if not specified (e.g. there's no
441- * message entity).
442+ * message entity).
442443 */
443444 public MediaType getMediaType () {
444445 return singleHeader (HttpHeaders .CONTENT_TYPE , new Function <String , MediaType >() {
@@ -457,7 +458,7 @@ public MediaType apply(String input) {
457458 * Get a list of media types that are acceptable for a request.
458459 *
459460 * @return a read-only list of requested response media types sorted according
460- * to their q-value, with highest preference first.
461+ * to their q-value, with highest preference first.
461462 */
462463 public List <AcceptableMediaType > getQualifiedAcceptableMediaTypes () {
463464 final String value = getHeaderString (HttpHeaders .ACCEPT );
@@ -477,7 +478,7 @@ public List<AcceptableMediaType> getQualifiedAcceptableMediaTypes() {
477478 * Get a list of languages that are acceptable for the message.
478479 *
479480 * @return a read-only list of acceptable languages sorted according
480- * to their q-value, with highest preference first.
481+ * to their q-value, with highest preference first.
481482 */
482483 public List <AcceptableLanguageTag > getQualifiedAcceptableLanguages () {
483484 final String value = getHeaderString (HttpHeaders .ACCEPT_LANGUAGE );
@@ -497,7 +498,7 @@ public List<AcceptableLanguageTag> getQualifiedAcceptableLanguages() {
497498 * Get the list of language tag from the "Accept-Charset" of an HTTP request.
498499 *
499500 * @return The list of AcceptableToken. This list
500- * is ordered with the highest quality acceptable charset occurring first.
501+ * is ordered with the highest quality acceptable charset occurring first.
501502 */
502503 public List <AcceptableToken > getQualifiedAcceptCharset () {
503504 final String acceptCharset = getHeaderString (HttpHeaders .ACCEPT_CHARSET );
@@ -515,7 +516,7 @@ public List<AcceptableToken> getQualifiedAcceptCharset() {
515516 * Get the list of language tag from the "Accept-Charset" of an HTTP request.
516517 *
517518 * @return The list of AcceptableToken. This list
518- * is ordered with the highest quality acceptable charset occurring first.
519+ * is ordered with the highest quality acceptable charset occurring first.
519520 */
520521 public List <AcceptableToken > getQualifiedAcceptEncoding () {
521522 final String acceptEncoding = getHeaderString (HttpHeaders .ACCEPT_ENCODING );
@@ -553,7 +554,7 @@ public Map<String, Cookie> getRequestCookies() {
553554 * Get the allowed HTTP methods from the Allow HTTP header.
554555 *
555556 * @return the allowed HTTP methods, all methods will returned as upper case
556- * strings.
557+ * strings.
557558 */
558559 public Set <String > getAllowedMethods () {
559560 final String allowed = getHeaderString (HttpHeaders .ALLOW );
@@ -642,7 +643,7 @@ public URI apply(String value) {
642643 * Get the links attached to the message as header.
643644 *
644645 * @return links, may return empty {@link java.util.Set} if no links are present. Never
645- * returns {@code null}.
646+ * returns {@code null}.
646647 */
647648 public Set <Link > getLinks () {
648649 List <String > links = this .headers .get (HttpHeaders .LINK );
@@ -666,7 +667,7 @@ public Set<Link> getLinks() {
666667 *
667668 * @param relation link relation.
668669 * @return {@code true} if the for the relation link exists, {@code false}
669- * otherwise.
670+ * otherwise.
670671 */
671672 public boolean hasLink (String relation ) {
672673 for (Link link : getLinks ()) {
@@ -701,7 +702,7 @@ public Link getLink(String relation) {
701702 *
702703 * @param relation link relation.
703704 * @return the link builder for the relation, otherwise {@code null} if not
704- * present.
705+ * present.
705706 */
706707 public Link .Builder getLinkBuilder (String relation ) {
707708 Link link = getLink (relation );
@@ -735,12 +736,12 @@ public void setWorkers(MessageBodyWorkers workers) {
735736 /**
736737 * Check if there is a non-empty entity input stream is available in the
737738 * message.
738- *
739+ * <p/>
739740 * The method returns {@code true} if the entity is present, returns
740741 * {@code false} otherwise.
741742 *
742743 * @return {@code true} if there is an entity present in the message,
743- * {@code false} otherwise.
744+ * {@code false} otherwise.
744745 */
745746 public boolean hasEntity () {
746747 entityContent .ensureNotClosed ();
@@ -785,7 +786,6 @@ public <T> T readEntity(Class<T> rawType, PropertiesDelegate propertiesDelegate)
785786 return readEntity (rawType , rawType , EMPTY_ANNOTATIONS , propertiesDelegate );
786787 }
787788
788-
789789 /**
790790 * Read entity from a context entity input stream.
791791 *
@@ -831,14 +831,14 @@ public <T> T readEntity(Class<T> rawType, Type type, Annotation[] annotations, P
831831
832832 entityContent .ensureNotClosed ();
833833
834- // TODO: revise if we need to re-introduce the check for performance reasons or once non-blocking I/O is supported.
835- // The code has been commended out because in case of streaming input (e.g. SSE) the call might block until a first
836- // byte is available, which would make e.g. the SSE EventSource construction or EventSource.open() method to block
837- // until a first event is received, which is undesirable.
838- //
839- // if (entityContent.isEmpty()) {
840- // return null;
841- // }
834+ // TODO: revise if we need to re-introduce the check for performance reasons or once non-blocking I/O is supported.
835+ // The code has been commended out because in case of streaming input (e.g. SSE) the call might block until a first
836+ // byte is available, which would make e.g. the SSE EventSource construction or EventSource.open() method to block
837+ // until a first event is received, which is undesirable.
838+ //
839+ // if (entityContent.isEmpty()) {
840+ // return null;
841+ // }
842842
843843 if (workers == null ) {
844844 return null ;
@@ -847,7 +847,6 @@ public <T> T readEntity(Class<T> rawType, Type type, Annotation[] annotations, P
847847 MediaType mediaType = getMediaType ();
848848 mediaType = mediaType == null ? MediaType .APPLICATION_OCTET_STREAM_TYPE : mediaType ;
849849
850-
851850 boolean shouldClose = !buffered ;
852851 try {
853852 T t = (T ) workers .readFrom (
@@ -868,7 +867,11 @@ public <T> T readEntity(Class<T> rawType, Type type, Annotation[] annotations, P
868867 throw new ProcessingException (LocalizationMessages .ERROR_READING_ENTITY_FROM_INPUT_STREAM (), ex );
869868 } finally {
870869 if (shouldClose ) {
871- entityContent .close ();
870+ // Workaround for JRFCAF-1344: the underlying stream close() implementation may be thread-unsafe
871+ // and as such the close() may result in an IOException at the socket input stream level,
872+ // if the close() gets called at once from multiple threads somehow.
873+ // We want to ignore these exceptions in the readEntity/bufferEntity operations though.
874+ ReaderWriter .safelyClose (entityContent );
872875 }
873876 }
874877 }
@@ -877,8 +880,7 @@ public <T> T readEntity(Class<T> rawType, Type type, Annotation[] annotations, P
877880 * Buffer the entity stream (if not empty).
878881 *
879882 * @return {@code true} if the entity input stream was successfully buffered.
880- * @throws javax.ws.rs.ProcessingException
881- * in case of an IO error.
883+ * @throws javax.ws.rs.ProcessingException in case of an IO error.
882884 */
883885 public boolean bufferEntity () throws ProcessingException {
884886 entityContent .ensureNotClosed ();
@@ -893,7 +895,11 @@ public boolean bufferEntity() throws ProcessingException {
893895 try {
894896 ReaderWriter .writeTo (entityStream , baos );
895897 } finally {
896- entityStream .close ();
898+ // Workaround for JRFCAF-1344: the underlying stream close() implementation may be thread-unsafe
899+ // and as such the close() may result in an IOException at the socket input stream level,
900+ // if the close() gets called at once from multiple threads somehow.
901+ // We want to ignore these exceptions in the readEntity/bufferEntity operations though.
902+ ReaderWriter .safelyClose (entityStream );
897903 }
898904
899905 entityContent .setContent (new ByteArrayInputStream (baos .toByteArray ()), true );
0 commit comments