85
85
* @author Marek Potociar (marek.potociar at oracle.com)
86
86
*/
87
87
public abstract class InboundMessageContext {
88
+
88
89
private static final InputStream EMPTY = new InputStream () {
89
90
90
91
@ Override
@@ -121,6 +122,7 @@ public boolean markSupported() {
121
122
* is used to control the execution of interceptors.
122
123
*/
123
124
private static class EntityContent extends EntityInputStream {
125
+
124
126
private boolean buffered ;
125
127
126
128
EntityContent () {
@@ -266,7 +268,7 @@ private static List<String> iterableToList(final Iterable<?> values) {
266
268
267
269
/**
268
270
* Get a message header as a single string value.
269
- *
271
+ * <p/>
270
272
* Each single header value is converted to String using a
271
273
* {@link javax.ws.rs.ext.RuntimeDelegate.HeaderDelegate} if one is available
272
274
* via {@link javax.ws.rs.ext.RuntimeDelegate#createHeaderDelegate(java.lang.Class)}
@@ -275,10 +277,10 @@ private static List<String> iterableToList(final Iterable<?> values) {
275
277
*
276
278
* @param name the message header.
277
279
* @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.
282
284
*/
283
285
public String getHeaderString (String name ) {
284
286
List <String > values = this .headers .get (name );
@@ -400,7 +402,7 @@ public Set<MatchingEntityTag> getIfNoneMatch() {
400
402
/**
401
403
* Get the language of the entity.
402
404
*
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.
404
406
*/
405
407
public Locale getLanguage () {
406
408
return singleHeader (HttpHeaders .CONTENT_LANGUAGE , new Function <String , Locale >() {
@@ -418,8 +420,7 @@ public Locale apply(String input) {
418
420
/**
419
421
* Get Content-Length value.
420
422
*
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.
423
424
*/
424
425
public int getLength () {
425
426
return singleHeader (HttpHeaders .CONTENT_LENGTH , new Function <String , Integer >() {
@@ -438,7 +439,7 @@ public Integer apply(String input) {
438
439
* Get the media type of the entity.
439
440
*
440
441
* @return the media type or {@code null} if not specified (e.g. there's no
441
- * message entity).
442
+ * message entity).
442
443
*/
443
444
public MediaType getMediaType () {
444
445
return singleHeader (HttpHeaders .CONTENT_TYPE , new Function <String , MediaType >() {
@@ -457,7 +458,7 @@ public MediaType apply(String input) {
457
458
* Get a list of media types that are acceptable for a request.
458
459
*
459
460
* @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.
461
462
*/
462
463
public List <AcceptableMediaType > getQualifiedAcceptableMediaTypes () {
463
464
final String value = getHeaderString (HttpHeaders .ACCEPT );
@@ -477,7 +478,7 @@ public List<AcceptableMediaType> getQualifiedAcceptableMediaTypes() {
477
478
* Get a list of languages that are acceptable for the message.
478
479
*
479
480
* @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.
481
482
*/
482
483
public List <AcceptableLanguageTag > getQualifiedAcceptableLanguages () {
483
484
final String value = getHeaderString (HttpHeaders .ACCEPT_LANGUAGE );
@@ -497,7 +498,7 @@ public List<AcceptableLanguageTag> getQualifiedAcceptableLanguages() {
497
498
* Get the list of language tag from the "Accept-Charset" of an HTTP request.
498
499
*
499
500
* @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.
501
502
*/
502
503
public List <AcceptableToken > getQualifiedAcceptCharset () {
503
504
final String acceptCharset = getHeaderString (HttpHeaders .ACCEPT_CHARSET );
@@ -515,7 +516,7 @@ public List<AcceptableToken> getQualifiedAcceptCharset() {
515
516
* Get the list of language tag from the "Accept-Charset" of an HTTP request.
516
517
*
517
518
* @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.
519
520
*/
520
521
public List <AcceptableToken > getQualifiedAcceptEncoding () {
521
522
final String acceptEncoding = getHeaderString (HttpHeaders .ACCEPT_ENCODING );
@@ -553,7 +554,7 @@ public Map<String, Cookie> getRequestCookies() {
553
554
* Get the allowed HTTP methods from the Allow HTTP header.
554
555
*
555
556
* @return the allowed HTTP methods, all methods will returned as upper case
556
- * strings.
557
+ * strings.
557
558
*/
558
559
public Set <String > getAllowedMethods () {
559
560
final String allowed = getHeaderString (HttpHeaders .ALLOW );
@@ -642,7 +643,7 @@ public URI apply(String value) {
642
643
* Get the links attached to the message as header.
643
644
*
644
645
* @return links, may return empty {@link java.util.Set} if no links are present. Never
645
- * returns {@code null}.
646
+ * returns {@code null}.
646
647
*/
647
648
public Set <Link > getLinks () {
648
649
List <String > links = this .headers .get (HttpHeaders .LINK );
@@ -666,7 +667,7 @@ public Set<Link> getLinks() {
666
667
*
667
668
* @param relation link relation.
668
669
* @return {@code true} if the for the relation link exists, {@code false}
669
- * otherwise.
670
+ * otherwise.
670
671
*/
671
672
public boolean hasLink (String relation ) {
672
673
for (Link link : getLinks ()) {
@@ -701,7 +702,7 @@ public Link getLink(String relation) {
701
702
*
702
703
* @param relation link relation.
703
704
* @return the link builder for the relation, otherwise {@code null} if not
704
- * present.
705
+ * present.
705
706
*/
706
707
public Link .Builder getLinkBuilder (String relation ) {
707
708
Link link = getLink (relation );
@@ -735,12 +736,12 @@ public void setWorkers(MessageBodyWorkers workers) {
735
736
/**
736
737
* Check if there is a non-empty entity input stream is available in the
737
738
* message.
738
- *
739
+ * <p/>
739
740
* The method returns {@code true} if the entity is present, returns
740
741
* {@code false} otherwise.
741
742
*
742
743
* @return {@code true} if there is an entity present in the message,
743
- * {@code false} otherwise.
744
+ * {@code false} otherwise.
744
745
*/
745
746
public boolean hasEntity () {
746
747
entityContent .ensureNotClosed ();
@@ -785,7 +786,6 @@ public <T> T readEntity(Class<T> rawType, PropertiesDelegate propertiesDelegate)
785
786
return readEntity (rawType , rawType , EMPTY_ANNOTATIONS , propertiesDelegate );
786
787
}
787
788
788
-
789
789
/**
790
790
* Read entity from a context entity input stream.
791
791
*
@@ -831,14 +831,14 @@ public <T> T readEntity(Class<T> rawType, Type type, Annotation[] annotations, P
831
831
832
832
entityContent .ensureNotClosed ();
833
833
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
+ // }
842
842
843
843
if (workers == null ) {
844
844
return null ;
@@ -847,7 +847,6 @@ public <T> T readEntity(Class<T> rawType, Type type, Annotation[] annotations, P
847
847
MediaType mediaType = getMediaType ();
848
848
mediaType = mediaType == null ? MediaType .APPLICATION_OCTET_STREAM_TYPE : mediaType ;
849
849
850
-
851
850
boolean shouldClose = !buffered ;
852
851
try {
853
852
T t = (T ) workers .readFrom (
@@ -868,7 +867,11 @@ public <T> T readEntity(Class<T> rawType, Type type, Annotation[] annotations, P
868
867
throw new ProcessingException (LocalizationMessages .ERROR_READING_ENTITY_FROM_INPUT_STREAM (), ex );
869
868
} finally {
870
869
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 );
872
875
}
873
876
}
874
877
}
@@ -877,8 +880,7 @@ public <T> T readEntity(Class<T> rawType, Type type, Annotation[] annotations, P
877
880
* Buffer the entity stream (if not empty).
878
881
*
879
882
* @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.
882
884
*/
883
885
public boolean bufferEntity () throws ProcessingException {
884
886
entityContent .ensureNotClosed ();
@@ -893,7 +895,11 @@ public boolean bufferEntity() throws ProcessingException {
893
895
try {
894
896
ReaderWriter .writeTo (entityStream , baos );
895
897
} 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 );
897
903
}
898
904
899
905
entityContent .setContent (new ByteArrayInputStream (baos .toByteArray ()), true );
0 commit comments