JDK-8041746 : FileCacheImageOutputStream does not clean up resources in close() if an exceptio
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.imageio
  • Affected Version: 6u22,7
  • Priority: P3
  • Status: Closed
  • Resolution: Duplicate
  • OS: linux
  • Submitted: 2013-03-20
  • Updated: 2015-11-25
  • Resolved: 2015-11-25
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 9
9Resolved
Related Reports
Duplicate :  
Duplicate :  
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 ----------
Comments
This is duplicate of JDK-6867419.
25-11-2015

Have to make closed and not resolved since it is Duplicate.
25-11-2015

This is duplicate of JDK-6967419. Fix is approved and it will be pushed in JDK 9.
24-11-2015

Duplicate of JDK-6967419. Fix will be pushed in JDK 9.
24-11-2015

This is duplicate of JDK-6967419. Review for Fix is done and it will pushed in JDK 9.
24-11-2015

we have no plans to fix it
20-06-2014