Bug 1014393 - Stream closed exception in resetStream on IBM jdk 16, 17 on RHEL 5, 6
Stream closed exception in resetStream on IBM jdk 16, 17 on RHEL 5, 6
Status: CLOSED CURRENTRELEASE
Product: JBoss Enterprise Application Platform 6
Classification: JBoss
Component: RESTEasy (Show other bugs)
6.3.0,6.2.0
Unspecified Unspecified
unspecified Severity urgent
: GA
: EAP 6.3.0
Assigned To: Weinan Li
Katerina Novotna
Russell Dickenson
: Reopened
Depends On:
Blocks:
  Show dependency treegraph
 
Reported: 2013-10-01 17:14 EDT by Petr Sakař
Modified: 2016-08-02 12:15 EDT (History)
5 users (show)

See Also:
Fixed In Version:
Doc Type: Bug Fix
Doc Text:
In previous releases of JBoss EAP 6, the `xercesImpl` provided by IBM JDK 16, 17 conflicted with the `jaxb` unmarshaller used by `resteasy-jaxb-provider`. This issue also occurred when the user was directly using the `xercesImpl` jar provided by EAP 6. These conflicts resulted in a `java.io.IOException: Stream closed` error when using IBM JDK 16, 17 or `xerces:xercesImpl:2.9.1-redhat-x` (provided by EAP 6) as a dependency in a resteasy 2.3.6.Final-redhat-x based project. This issue has been resolved.
Story Points: ---
Clone Of:
Environment:
Last Closed: 2014-08-06 10:35:01 EDT
Type: Bug
Regression: ---
Mount Type: ---
Documentation: ---
CRM:
Verified Versions:
Category: ---
oVirt Team: ---
RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: ---


Attachments (Terms of Use)


External Trackers
Tracker ID Priority Status Summary Last Updated
JBoss Issue Tracker RESTEASY-863 Major Closed providers/jaxb - StreamResetTest.test456() fails on IBM JDKs 2016-08-02 12:15 EDT

  None (edit)
Description Petr Sakař 2013-10-01 17:14:47 EDT
Test org.jboss.resteasy.test.StreamResetTest.test456 is failing with message 
  java.io.IOException: Stream closed

Version-Release number of selected component (if applicable):
EAP 6.2.0.ER3


Stacktrace

java.lang.RuntimeException: java.io.IOException: Stream closed
	at org.jboss.resteasy.client.core.BaseClientResponse.resetStream(BaseClientResponse.java:326)
	at org.jboss.resteasy.test.StreamResetTest.test456(StreamResetTest.java:93)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:88)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:55)
	at java.lang.reflect.Method.invoke(Method.java:613)
	at org.junit.internal.runners.TestMethodRunner.executeMethodBody(TestMethodRunner.java:99)
	at org.junit.internal.runners.TestMethodRunner.runUnprotected(TestMethodRunner.java:81)
	at org.junit.internal.runners.BeforeAndAfterRunner.runProtected(BeforeAndAfterRunner.java:34)
	at org.junit.internal.runners.TestMethodRunner.runMethod(TestMethodRunner.java:75)
	at org.junit.internal.runners.TestMethodRunner.run(TestMethodRunner.java:45)
	at org.junit.internal.runners.TestClassMethodsRunner.invokeTestMethod(TestClassMethodsRunner.java:71)
	at org.junit.internal.runners.TestClassMethodsRunner.run(TestClassMethodsRunner.java:35)
	at org.junit.internal.runners.TestClassRunner$1.runUnprotected(TestClassRunner.java:42)
	at org.junit.internal.runners.BeforeAndAfterRunner.runProtected(BeforeAndAfterRunner.java:34)
	at org.junit.internal.runners.TestClassRunner.run(TestClassRunner.java:52)
	at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:62)
	at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.executeTestSet(AbstractDirectoryTestSuite.java:140)
	at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.execute(AbstractDirectoryTestSuite.java:127)
	at org.apache.maven.surefire.Surefire.run(Surefire.java:177)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:88)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:55)
	at java.lang.reflect.Method.invoke(Method.java:613)
	at org.apache.maven.surefire.booter.SurefireBooter.runSuitesInProcess(SurefireBooter.java:345)
	at org.apache.maven.surefire.booter.SurefireBooter.main(SurefireBooter.java:1009)
Caused by: java.io.IOException: Stream closed
	at java.io.BufferedInputStream.getBufIfOpen(BufferedInputStream.java:173)
	at java.io.BufferedInputStream.reset(BufferedInputStream.java:446)
	at org.jboss.resteasy.client.core.BaseClientResponse.resetStream(BaseClientResponse.java:322)
	... 25 more
Comment 2 Weinan Li 2013-10-16 09:47:39 EDT
Thanks for the investigation Petr! As the root cause is clear, would you like provide a patch for this?
Comment 3 Weinan Li 2013-10-22 00:20:31 EDT
Just notice this is a bug in test codes. We don't ship test code in EAP, so this issue should reported upstream JIRA. I'll close this one and report it to JIRA and start to work on it.
Comment 4 Weinan Li 2013-10-22 00:23:56 EDT
This is a bug in test codes that we don't ship EAP, will fix it in upstream.
Comment 5 Weinan Li 2013-10-22 00:28:20 EDT
After some investigation on this problem seems more fixes need to be done outside the test code. Reopen it and work on it. Sorry for the chaos.
Comment 6 JBoss JIRA Server 2013-10-22 06:52:52 EDT
Weinan Li <weli@redhat.com> made a comment on jira RESTEASY-863

The problem is in closeReaders() method of XMLEntityManager. The good jdk one is:

{code}
 /**
     * Close all opened InputStreams and Readers opened by this parser.
     */
    public void closeReaders() {
        /** this call actually does nothing, readers are closed in the endEntity method
         * through the current entity.
         * The change seems to have happened during the jdk6 development with the
         * addition of StAX
        **/
    }
{code}

The problem one is:

{code}
    /**
     * Close all opened InputStreams and Readers opened by this parser.
     */
    public void closeReaders() {
        // close all readers
        for (int i = fReaderStack.size()-1; i >= 0; i--) {
            try {
                ((Reader)fReaderStack.pop()).close();
            } catch (IOException e) {
                // ignore
            }
        }
    }
{code}
Comment 7 Petr Sakař 2013-10-22 07:04:59 EDT
see comment#1 solution recommended in [1]
Comment 11 JBoss JIRA Server 2013-10-22 07:21:54 EDT
Weinan Li <weli@redhat.com> made a comment on jira RESTEASY-863

We need to override the close() method of "SelfExpandingBufferedInputStream", not sure whether this will affect other part. I'll do more investigation.
Comment 12 JBoss JIRA Server 2013-10-22 09:14:04 EDT
Weinan Li <weli@redhat.com> made a comment on jira RESTEASY-863

Here is the execute method in ApacheHttpClient4Executor:

{code}
 @SuppressWarnings("unchecked")
   public ClientResponse execute(ClientRequest request) throws Exception
   {
      String uri = request.getUri();
      final HttpRequestBase httpMethod = createHttpMethod(uri, request.getHttpMethod());
      try
      {
         loadHttpMethod(request, httpMethod);
   
         final HttpResponse res = httpClient.execute(httpMethod, httpContext);
   
         BaseClientResponse response = new BaseClientResponse(new BaseClientResponseStreamFactory()
         {
            InputStream stream;
   
            public InputStream getInputStream() throws IOException
            {
               if (stream == null)
               {
                  HttpEntity entity = res.getEntity();
                  if (entity == null) return null;
                  stream = new SelfExpandingBufferredInputStream(entity.getContent());
               }
               return stream;
            }
   
            public void performReleaseConnection()
            {
               // Apache Client 4 is stupid, You have to get the InputStream and close it if there is an entity
               // otherwise the connection is never released. There is, of course, no close() method on response
               // to make this easier.
               try
               {
                  if (stream != null)
                  {
                     stream.close();
                  }
                  else
                  {
                     InputStream is = getInputStream();
                     if (is != null)
                     {
                        is.close();
                     }
                  }
               }
               catch (Exception ignore)
               {
               }
            }
         }, this);
         response.setAttributes(request.getAttributes());
         response.setStatus(res.getStatusLine().getStatusCode());
         response.setHeaders(extractHeaders(res));
         response.setProviderFactory(request.getProviderFactory());
         return response;
      }
      finally
      {
         cleanUpAfterExecute(httpMethod);
      }
   }
{code}

The above method explicitly needs the close method of input stream:

{code}
try
{
   if (stream != null)
   {
      stream.close();
   }
   else
   {
      InputStream is = getInputStream();
      if (is != null)
      {
         is.close();
      }
   }
}
{code}

That means we cannot override the 'close' method of "SelfExpandingBufferedInputStream"

I've also checked the possible places that could configure "XMLEntityManager" not to close the stream, seems no configure could alter this behaviour.
Comment 13 Weinan Li 2013-10-22 09:18:02 EDT
(In reply to Petr Sakař from comment #7)
> see comment#1 solution recommended in [1]

Hi Petr, can we explicitly set the xercesImpl dependency used by RESTEasy in pom.xml to solve this problem?
Comment 14 JBoss JIRA Server 2013-10-23 03:33:43 EDT
Petr Sakař <psakar@redhat.com> made a comment on jira RESTEASY-863

Weinan,
have you tried solution proposed in https://bugzilla.redhat.com/show_bug.cgi?id=1014393#c1 or do you have some reasons why not to do it this way ?
Petr
Comment 15 Weinan Li 2013-10-23 03:48:06 EDT
(In reply to JBoss JIRA Server from comment #14)
> Petr Sakař <psakar@redhat.com> made a comment on jira RESTEASY-863
> 
> Weinan,
> have you tried solution proposed in
> https://bugzilla.redhat.com/show_bug.cgi?id=1014393#c1 or do you have some
> reasons why not to do it this way ?
> Petr

Could you please checke my investigations in above? I've explained the reason why we couldn't override the 'close' method of input stream. If you have any good suggestions, please let me know. I'm stilling trying to find a way to solve this...
Comment 16 JBoss JIRA Server 2013-10-23 05:52:07 EDT
Petr Sakař <psakar@redhat.com> made a comment on jira RESTEASY-863

Weinan,
very nice pictures. Can you please try this patch ?
Petr
{code:none}
diff --git a/resteasy-jaxrs/src/main/java/org/jboss/resteasy/core/interception/MessageBodyReaderContextImpl.java b/resteasy-jaxrs/src/main/java/org/jboss/resteasy/core/interception/MessageBodyReaderContextImpl.java
index 943c56c..4e12dae 100644
--- a/resteasy-jaxrs/src/main/java/org/jboss/resteasy/core/interception/MessageBodyReaderContextImpl.java
+++ b/resteasy-jaxrs/src/main/java/org/jboss/resteasy/core/interception/MessageBodyReaderContextImpl.java
@@ -9,6 +9,7 @@ import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.ext.MessageBodyReader;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.FilterInputStream;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
 
@@ -102,7 +103,7 @@ public abstract class MessageBodyReaderContextImpl implements MessageBodyReaderC
    public Object proceed() throws IOException, WebApplicationException
    {
       if (interceptors == null || index >= interceptors.length)
-         return reader.readFrom(type, genericType, annotations, mediaType, headers, inputStream);
+         return reader.readFrom(type, genericType, annotations, mediaType, headers, new InputStreamWrapper(inputStream));
       try
       {
          return interceptors[index++].read(this);
@@ -112,4 +113,16 @@ public abstract class MessageBodyReaderContextImpl implements MessageBodyReaderC
          index--;
       }
    }
+   
+  private static class InputStreamWrapper extends FilterInputStream {
+
+    protected InputStreamWrapper(InputStream in) {
+      super(in);
+    }
+
+    @Override
+    public void close() throws IOException {
+    }
+
+  }
 }
{code}
Comment 17 JBoss JIRA Server 2013-10-23 14:08:25 EDT
Ron Sigal <ron.sigal@jboss.com> made a comment on jira RESTEASY-863

Off the top of my head, I'm wondering why not just catch the IOException if the close() fails and log a DEBUG message.
Comment 18 JBoss JIRA Server 2013-10-23 14:19:39 EDT
Ron Sigal <ron.sigal@jboss.com> made a comment on jira RESTEASY-863

I see.  I just found RESTEASY-456.
Comment 19 JBoss JIRA Server 2013-10-23 23:53:25 EDT
Ron Sigal <ron.sigal@jboss.com> made a comment on jira RESTEASY-863

Two comments.

1. According to http://mail-archives.apache.org/mod_mbox/xerces-j-users/200810.mbox/%3COFB0CA5E4A.8A179EB4-ON852574E4.0040887D-852574E4.004A0F5E@ca.ibm.com%3E, java.io.FilterInputStream.close() "does nothing", but I see

       public void close() throws IOException {
	in.close();
    }

in both the Oracle and IBM JDKs.  ???

2. Even though resetStream() is defined in org.jboss.resteasy.client.ClientResponse in the resteasy-jaxrs module, this issue seems to be specific to the JAXB provider.  In fact, resetStream() was added specifically in the fix for RESTEASY-456 "ClientResponse.getEntity(*) JaxB failure prevents future calls of getEntity(*) with other parameters: org.xml.sax.SAXParseException: Premature end of file."  So, rather than change some global behavior in the resteasy-jaxrs module, doesn't it make sense to fix the problem in the JAXB provider?  For example, the entityStream parameter in org.jboss.resteasy.plugins.providers.jaxb.AbstractJAXBProvider.readFrom() could be wrapped to make close() a NOP.  Actually, I'm wondering about the effect of doing that in the case of a normal (not caused by an exception) close.

Thoughts?
Comment 20 JBoss JIRA Server 2013-10-24 00:09:38 EDT
Weinan Li <weli@redhat.com> made a comment on jira RESTEASY-863

Thanks Petr & Ron! Things look clear now,  I'll try test your advices today :-)
Comment 21 JBoss JIRA Server 2013-10-24 08:42:42 EDT
Weinan Li <weli@redhat.com> made a comment on jira RESTEASY-863

Hi Petr, your patch is best :-)

I've done the local testings and finished the analysis. I'll create PR and submit it to github.
Comment 22 JBoss JIRA Server 2013-10-25 04:46:51 EDT
Weinan Li <weli@redhat.com> updated the status of jira RESTEASY-863 to Coding In Progress
Comment 23 JBoss JIRA Server 2013-10-25 04:49:53 EDT
Weinan Li <weli@redhat.com> updated the status of jira RESTEASY-863 to Open
Comment 25 JBoss JIRA Server 2013-10-29 05:55:30 EDT
Weinan Li <weli@redhat.com> updated the status of jira RESTEASY-863 to Resolved
Comment 26 Scott Mumford 2013-12-02 23:02:09 EST
Hi Weinan,

Since we're about to hit the 6.2 GA, to save time I'm hoping you can provide a draft release note in the Doc Text field above, as I'm unable to ascertain what the issue/resolution was in this ticket.

I think I understand that there was a problem when xercesImpl wasn't explicitly defined as a dependency, but as to how the issue manifested to users and how it was fixed, I'm at a loss.

Any help you can provide on this would be appreciated.
Comment 27 Russell Dickenson 2013-12-03 08:32:57 EST
`Requires doc text` cleared as it is too late to include this ticket in the JBoss EAp 6.2.0 Release Notes.
Comment 28 Weinan Li 2013-12-03 09:19:24 EST
This fix is not included in 6.2.0 yet.
Comment 29 Petr Sakař 2013-12-04 01:55:23 EST
should be in known issues, so requires documentation
Comment 30 Weinan Li 2013-12-04 03:51:59 EST
Hi Scott, I have proposed a "Known Issue" doc in "Doc Text" in field if you need :-)
Comment 33 Weinan Li 2014-06-18 03:52:14 EDT
Fixed in RESTEasy 2.3.8
Comment 35 Katerina Novotna 2014-06-20 03:49:17 EDT
Verified in EAP 6.3.0.ER7.
Comment 36 JBoss JIRA Server 2016-08-02 12:15:25 EDT
Alessio Soldano <asoldano@redhat.com> updated the status of jira RESTEASY-863 to Closed

Note You need to log in before you can comment on or make changes to this bug.