Bug 1014393

Summary: Stream closed exception in resetStream on IBM jdk 16, 17 on RHEL 5, 6
Product: [JBoss] JBoss Enterprise Application Platform 6 Reporter: Petr Sakař <psakar>
Component: RESTEasyAssignee: Weinan Li <weli>
Status: CLOSED CURRENTRELEASE QA Contact: Katerina Odabasi <kanovotn>
Severity: urgent Docs Contact:
Priority: unspecified    
Version: 6.3.0, 6.2.0CC: kanovotn, myarboro, nobody, smumford, weli
Target Milestone: GAKeywords: Reopened
Target Release: EAP 6.3.0   
Hardware: Unspecified   
OS: Unspecified   
Whiteboard:
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 14:35:01 UTC Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:

Description Petr Sakař 2013-10-01 21:14:47 UTC
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 13:47:39 UTC
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 04:20:31 UTC
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 04:23:56 UTC
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 04:28:20 UTC
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 10:52:52 UTC
Weinan Li <weli> 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 11:04:59 UTC
see comment#1 solution recommended in [1]

Comment 8 JBoss JIRA Server 2013-10-22 11:18:39 UTC
Weinan Li <weli> made a comment on jira RESTEASY-863

Sequence diagram:

!https://issues.jboss.org/secure/attachment/12366704/RESTEASY-863-seq.png!

Class diagram:

!https://issues.jboss.org/secure/attachment/12366704/RESTEASY-863-class.png!

Comment 9 JBoss JIRA Server 2013-10-22 11:19:53 UTC
Weinan Li <weli> made a comment on jira RESTEASY-863

Sequence diagram:

!https://issues.jboss.org/secure/attachment/12366704/RESTEASY-863-seq.png!

Class diagram:

https://issues.jboss.org/secure/attachment/12366705/RESTEASY-863-class.png

Comment 10 JBoss JIRA Server 2013-10-22 11:20:10 UTC
Weinan Li <weli> made a comment on jira RESTEASY-863

Sequence diagram:

!https://issues.jboss.org/secure/attachment/12366704/RESTEASY-863-seq.png!

Class diagram:

!https://issues.jboss.org/secure/attachment/12366705/RESTEASY-863-class.png!

Comment 11 JBoss JIRA Server 2013-10-22 11:21:54 UTC
Weinan Li <weli> 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 13:14:04 UTC
Weinan Li <weli> 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 13:18:02 UTC
(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 07:33:43 UTC
Petr Sakař <psakar> 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 07:48:06 UTC
(In reply to JBoss JIRA Server from comment #14)
> Petr Sakař <psakar> 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 09:52:07 UTC
Petr Sakař <psakar> 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 18:08:25 UTC
Ron Sigal <ron.sigal> 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 18:19:39 UTC
Ron Sigal <ron.sigal> made a comment on jira RESTEASY-863

I see.  I just found RESTEASY-456.

Comment 19 JBoss JIRA Server 2013-10-24 03:53:25 UTC
Ron Sigal <ron.sigal> 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 04:09:38 UTC
Weinan Li <weli> 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 12:42:42 UTC
Weinan Li <weli> 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 08:46:51 UTC
Weinan Li <weli> updated the status of jira RESTEASY-863 to Coding In Progress

Comment 23 JBoss JIRA Server 2013-10-25 08:49:53 UTC
Weinan Li <weli> updated the status of jira RESTEASY-863 to Open

Comment 25 JBoss JIRA Server 2013-10-29 09:55:30 UTC
Weinan Li <weli> updated the status of jira RESTEASY-863 to Resolved

Comment 26 Scott Mumford 2013-12-03 04:02:09 UTC
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 13:32:57 UTC
`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 14:19:24 UTC
This fix is not included in 6.2.0 yet.

Comment 29 Petr Sakař 2013-12-04 06:55:23 UTC
should be in known issues, so requires documentation

Comment 30 Weinan Li 2013-12-04 08:51:59 UTC
Hi Scott, I have proposed a "Known Issue" doc in "Doc Text" in field if you need :-)

Comment 33 Weinan Li 2014-06-18 07:52:14 UTC
Fixed in RESTEasy 2.3.8

Comment 35 Katerina Odabasi 2014-06-20 07:49:17 UTC
Verified in EAP 6.3.0.ER7.

Comment 36 JBoss JIRA Server 2016-08-02 16:15:25 UTC
Alessio Soldano <asoldano> updated the status of jira RESTEASY-863 to Closed