Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8041746

FileCacheImageOutputStream does not clean up resources in close() if an exceptio

    XMLWordPrintable

    Details

    • Type: Bug
    • Status: Closed
    • Priority: P3
    • Resolution: Duplicate
    • Affects Version/s: 6u22, 7
    • Fix Version/s: 9
    • Component/s: client-libs
    • Labels:

      Description

      FULL PRODUCT VERSION :
      $ java -version
      java version " 1.6.0_22 "
      Java(TM) SE Runtime Environment (build 1.6.0_22-b04)
      Java HotSpot(TM) 64-Bit Server VM (build 17.1-b03, mixed mode)


      ADDITIONAL OS VERSION INFORMATION :
      $ uname -a
      Linux xxxxxxx #1 SMP Thu Mar 17 11:45:06 UTC 2011 x86_64 x86_64 x86_64 GNU/Linux

      $ cat /etc/SuSE-release
      SUSE Linux Enterprise Server 10 (x86_64)
      VERSION = 10
      PATCHLEVEL = 4


      EXTRA RELEVANT SYSTEM CONFIGURATION :
      Sun GlassFish Communications Server 2.0 ((v2.1 Patch18)(9.1_02 Patch24)) (build b02-p12)


      A DESCRIPTION OF THE PROBLEM :
      It is impossible to use ImageIO in a servlet environment to write an image to a HTTP response. In case of an exception in close(), it causes later requests to other servlets to fail randomly, and the temporary file is not deleted.


      This is some example code, from an example servlet that tries to write an image to an HttpServletResponse output stream.

      ImageOutputStream ios = ImageIO.createImageOutputStream(response.getOutputStream());

      try {
          Iterator<ImageWriter> it = ImageIO.getImageWritersByMIMEType(imageMimeType);

          if (it.hasNext()) {
              ImageWriter writer = it.next();

              try {
                  writer.setOutput(ios);
                  writer.write(null, new IIOImage(image, null, null), null);
              } finally {
                  writer.dispose();
              }
          }
      } finally {
          ios.close();
      }

      The ios reference will get an instance of FileCacheImageOutputStream or MemoryCacheImageOutputStream. Both classes have this problem.

      If the client is closing the socket, the write operation will throw an IOException.

      The ios.close() statement will then try to close the image output stream. The close() method tries to flush all remaining data. This generates an IOException again, since the client closed the socket and it is not possible to write to it.

      This exception interrupts the close() method before it is done. The temporary file is not deleted and the image output stream is not set to closed.

      Later the FileCacheImageOutputStream will be garbage collected. It's finalize() method will then be run. Since the image output stream is not properly closed, the finalize() method will attempt to close again.

      By this time the servlet container might have reused the HttpServletResponse object for serving another request. This other request will experience that the response is unexpectedly closed, and will fail.


      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Use Servlet A to trigger the problem.

      Use servlet B to see the problem.

      Hammer the server with request to servlet B, and look for the error log.
      At the same time, send a few requests per second to servlet A, but close the connection before the response is received. For each of these requests, the servlet will report that an IOException occured when writing the response.

      Wait for servlet B to report an error, that the response is already committed. It might take a while, because it happens when the finalizer is run on the image output stream. Not all requests to servlet A might trigger an error in servlet B.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      Any exceptions in servlet A should not affect servlet B.
      Temporary files should be removed when closing the image output stream.
      ACTUAL -
      Now and then servlet B reports that the response is already committed, even before it has started to process it. The response is sent out empty, unaffected by the servlet code.

      ERROR MESSAGES/STACK TRACES THAT OCCUR :
      This is the exception that is thrown in servlet A when trying to close the image output stream:

      ClientAbortException: java.io.IOException: Broken pipe
              at org.apache.coyote.tomcat5.OutputBuffer.doFlush(OutputBuffer.java:385)
              at org.apache.coyote.tomcat5.OutputBuffer.flush(OutputBuffer.java:351)
              at org.apache.coyote.tomcat5.CoyoteOutputStream.flush(CoyoteOutputStream.java:176)
              at javax.imageio.stream.FileCacheImageOutputStream.flushBefore(FileCacheImageOutputStream.java:239)
              at javax.imageio.stream.FileCacheImageOutputStream.close(FileCacheImageOutputStream.java:213)
              at com.ericsson.iptv.portal.common.imagescaler.ImageScalerServlet.doScale(ImageScalerServlet.java:156)
              at com.ericsson.iptv.portal.common.imagescaler.ImageScalerServlet.doGet(ImageScalerServlet.java:115)
              at javax.servlet.http.HttpServlet.service(HttpServlet.java:734)
              at javax.servlet.http.HttpServlet.service(HttpServlet.java:847)
              at org.apache.catalina.core.ApplicationFilterChain.servletService(ApplicationFilterChain.java:427)
              at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:333)
              at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214)
              at com.ericsson.iptv.portal.fw.cache.StaticDataCacheControlFilter.doFilter(StaticDataCacheControlFilter.java:129)
              at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:246)
              at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214)
              at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:313)
              at org.apache.catalina.core.StandardContextValve.invokeInternal(StandardContextValve.java:287)
              at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:218)
              at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:648)
              at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:593)
              at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:94)
              at com.sun.enterprise.web.PESessionLockingStandardPipeline.invoke(PESessionLockingStandardPipeline.java:98)
              at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:222)
              at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:648)
              at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:593)
              at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:587)
              at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:1093)
              at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:166)
              at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:648)
              at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:593)
              at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:587)
              at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:1093)
              at org.apache.coyote.tomcat5.CoyoteAdapter.service(CoyoteAdapter.java:291)
              at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.invokeAdapter(DefaultProcessorTask.java:670)
              at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.doProcess(DefaultProcessorTask.java:601)
              at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.process(DefaultProcessorTask.java:875)
              at com.sun.enterprise.web.connector.grizzly.DefaultReadTask.executeProcessorTask(DefaultReadTask.java:365)
              at com.sun.enterprise.web.connector.grizzly.DefaultReadTask.doTask(DefaultReadTask.java:285)
              at com.sun.enterprise.web.connector.grizzly.DefaultReadTask.doTask(DefaultReadTask.java:221)
              at com.sun.enterprise.web.portunif.PortUnificationPipeline$PUTask.doTask(PortUnificationPipeline.java:387)
              at com.sun.enterprise.web.connector.grizzly.TaskBase.run(TaskBase.java:269)
              at com.sun.enterprise.web.connector.grizzly.ssl.SSLWorkerThread.run(SSLWorkerThread.java:111)
      Caused by: java.io.IOException: Broken pipe
              at sun.nio.ch.FileDispatcher.write0(Native Method)
              at sun.nio.ch.SocketDispatcher.write(SocketDispatcher.java:29)
              at sun.nio.ch.IOUtil.writeFromNativeBuffer(IOUtil.java:100)
              at sun.nio.ch.IOUtil.write(IOUtil.java:71)
              at sun.nio.ch.SocketChannelImpl.write(SocketChannelImpl.java:334)
              at com.sun.enterprise.web.connector.grizzly.OutputWriter.flushChannel(OutputWriter.java:96)
              at com.sun.enterprise.web.connector.grizzly.OutputWriter.flushChannel(OutputWriter.java:69)
              at com.sun.enterprise.web.connector.grizzly.SocketChannelOutputBuffer.flushChannel(SocketChannelOutputBuffer.java:176)
              at com.sun.enterprise.web.connector.grizzly.SocketChannelOutputBuffer.flushBuffer(SocketChannelOutputBuffer.java:209)
              at com.sun.enterprise.web.connector.grizzly.SocketChannelOutputBuffer.flush(SocketChannelOutputBuffer.java:187)
              at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.action(DefaultProcessorTask.java:1103)
              at org.apache.coyote.Response.action(Response.java:101)
              at org.apache.coyote.tomcat5.OutputBuffer.doFlush(OutputBuffer.java:381)
              ... 41 more



      This exception shows who was committing the response that servlet B is trying to use (captured in the response object when committed, printed in servlet B when an already committed response is encountered):

      java.lang.RuntimeException
              at org.apache.coyote.Response.sendHeaders(Response.java:258)
              at org.apache.coyote.tomcat5.OutputBuffer.doFlush(OutputBuffer.java:372)
              at org.apache.coyote.tomcat5.OutputBuffer.flush(OutputBuffer.java:351)
              at org.apache.coyote.tomcat5.CoyoteOutputStream.flush(CoyoteOutputStream.java:176)
              at javax.imageio.stream.FileCacheImageOutputStream.close(FileCacheImageOutputStream.java:219)
              at javax.imageio.stream.ImageInputStreamImpl.finalize(ImageInputStreamImpl.java:860)
              at java.lang.ref.Finalizer.invokeFinalizeMethod(Native Method)
              at java.lang.ref.Finalizer.runFinalizer(Finalizer.java:83)
              at java.lang.ref.Finalizer.access$100(Finalizer.java:14)
              at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:160)



      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      Servlet A:

      @Override
      protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
          ImageOutputStream ios = ImageIO.createImageOutputStream(res.getOutputStream());

          try {
              Iterator<ImageWriter> it = ImageIO.getImageWritersByMIMEType(imageMimeType);

              if (it.hasNext()) {
                  ImageWriter writer = it.next();

                  try {
                      writer.setOutput(ios);
                      writer.write(null, new IIOImage(image, null, null), null);
                  } finally {
                      writer.dispose();
                  }
              }
          } finally {
              ios.close();
          }
      }


      Servlet B:

      @Override
      protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
          if (res.isCommitted()) {
              logger.error( " Response is already committed! " );
          }
          res.setContentType( " application/json " );
          res.setCharacterEncoding( " UTF-8 " );
          res.getOutputStream().print( " {\ " status\ " :\ " ok\ " } " );
      }

      ---------- END SOURCE ----------

        Attachments

          Issue Links

            Activity

              People

              Assignee:
              jdv Jayathirth D V
              Reporter:
              webbuggrp Webbug Group
              Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

                Dates

                Created:
                Updated:
                Resolved: