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

FilterOutputStream.close() silently ignores flush() exceptions

    Details

    • Type: Bug
    • Status: Closed
    • Priority: P3
    • Resolution: Won't Fix
    • Affects Version/s: 5.0
    • Fix Version/s: None
    • Component/s: core-libs
    • Labels:
    • Subcomponent:
    • CPU:
      x86
    • OS:
      windows_xp

      Description

      FULL PRODUCT VERSION :
      Tested in 1.5.0_02-b09

      ADDITIONAL OS VERSION INFORMATION :
      Appears to be a problem in Java library, so applicable to all OSs.

      A DESCRIPTION OF THE PROBLEM :
      FilterOutputStream silently ignores exceptions when doing a flush on close.
      This can hide IO problems such a full disk. The particular case where it got
      me was using a ZipOutputStream over a BufferedOutputStream.


      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      import java.io.*;
      import java.util.zip.*;
      class test
      {
        static class LimitOutputStream extends OutputStream
        {
          public void write(int n) throws IOException
          {
            num++;
            check();
          }

          public void write(byte[] buf) throws IOException
          {
            num += buf.length;
            check();
          }
          
          public void write(byte[] buf, int off, int len) throws IOException
          {
            num += len;
            check();
          }

          void check() throws IOException
          {
            if (num > 80)
            {
              //System.out.println("Limit.check: " + num);
              //Thread.dumpStack();
              throw new IOException("all full!");
            }
          }
              
          int num;
        }
        
        public static void main(String[] args)
          throws Exception
        {
          ZipOutputStream out =
            new ZipOutputStream(
            new BufferedOutputStream(
            new LimitOutputStream()));
          out.putNextEntry(new ZipEntry("entry"));
          for(int i=0; i<10000; ++i)
            out.write(new byte[100]);
          out.closeEntry();
          // out.flush(); without this line the close appears to silently work
          out.close();
        }
      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      You must explicitly flush() before close() if want to have an underlying
      exception raised.

      Release Regression From : 1.4.2_09
      The above release value was the last known release where this
      bug was known to work. Since then there has been a regression.

        Issue Links

          Activity

          Hide
          iris Iris Clark added a comment -
          BT2:EVALUATION

          This is not a problem with the implementation of FilterOutputStream.flush(). The current implementations of flush() and close() have been in place since at least April 1998. I believe that the problem is more likely to be caused by changes in java.util.zip. The reported problem goes away if I modify main() to avoid all zip code. The following is the key line for those changes:
           
          Old:
           
            ZipOutputStream out =
                new ZipOutputStream(
                new BufferedOutputStream(
                new LimitOutputStream()));
           
          New:
           
            ZipOutputStream out =
                new BufferedOutputStream(
                new LimitOutputStream());
           
          Routing to the zip team for their evaluation.
          Show
          iris Iris Clark added a comment - BT2:EVALUATION This is not a problem with the implementation of FilterOutputStream.flush(). The current implementations of flush() and close() have been in place since at least April 1998. I believe that the problem is more likely to be caused by changes in java.util.zip. The reported problem goes away if I modify main() to avoid all zip code. The following is the key line for those changes:   Old:     ZipOutputStream out =       new ZipOutputStream(       new BufferedOutputStream(       new LimitOutputStream()));   New:     ZipOutputStream out =       new BufferedOutputStream(       new LimitOutputStream());   Routing to the zip team for their evaluation.
          Hide
          bristor Dave Bristor added a comment -
          BT2:EVALUATION

          The root cause appears to be that the size of the byte array in BufferedOutputStream was changed between 1.4.2_09 and 1.5.0 from 512 bytes to 8192 bytes. In the example given, many bytes are written, but they are all 0. Using a ZipOutputStream, this compresses to somewhere between 512 and 8192 bytes. So in 1.4.2_09, the exception is caught under the call to ZipOutputStream.closeEntry, which does *not* involve FilterOutputStream.close (nor its flush method). The stack trace is:

          1.4.2_09:
          ZOS.closeEntry
            DOS.deflate
              BOS.write
                BOS.flushBuffer
                  LOS.write

          But in 1.5.0, with BOS having the larger buffer, BOS.flushBuffer is never called until ZOS.close is invoked, and then the stack trace is:

          1.5.0:
          ZOS.close
            DOS.close
              FOS.close
                BOS.flush
                  BOS.flushBuffer
                    LOS.write

          Here, FOS.close is involved, and it silently ignores the client's exception.

          Attached test case can be used to show the problem, without ZipOutputStream being involved. Run it with an arg under 1.4.2_09, and see that check prints a stack trace from main's call to out.write, then run it under 1.5.0 and see that check prints the stack trace due to main's call to close.

          Rerouting back to I/O folks.
          Show
          bristor Dave Bristor added a comment - BT2:EVALUATION The root cause appears to be that the size of the byte array in BufferedOutputStream was changed between 1.4.2_09 and 1.5.0 from 512 bytes to 8192 bytes. In the example given, many bytes are written, but they are all 0. Using a ZipOutputStream, this compresses to somewhere between 512 and 8192 bytes. So in 1.4.2_09, the exception is caught under the call to ZipOutputStream.closeEntry, which does *not* involve FilterOutputStream.close (nor its flush method). The stack trace is: 1.4.2_09: ZOS.closeEntry   DOS.deflate     BOS.write       BOS.flushBuffer         LOS.write But in 1.5.0, with BOS having the larger buffer, BOS.flushBuffer is never called until ZOS.close is invoked, and then the stack trace is: 1.5.0: ZOS.close   DOS.close     FOS.close       BOS.flush         BOS.flushBuffer           LOS.write Here, FOS.close is involved, and it silently ignores the client's exception. Attached test case can be used to show the problem, without ZipOutputStream being involved. Run it with an arg under 1.4.2_09, and see that check prints a stack trace from main's call to out.write, then run it under 1.5.0 and see that check prints the stack trace due to main's call to close. Rerouting back to I/O folks.
          Hide
          iris Iris Clark added a comment -
          BT2:EVALUATION

          As I said in my previous evaluation, the implementations of FilterOutputStream.flush() and FOS.close() have not changed in many years. Any changes to them now would introduce potentially unpleasant side-effects in existing customer code.
           
          The default buffer size of BufferedOutputStream was modified in Tiger (jdk1.5) as a result of bug 4953311 to optimize for performance in common scenarios such as network writes. It is not possible to change this default back to 512.
           
          However there is a simple work-around to get the previous behaviour, simply construct a BufferedOutputStream with an explicit buffer size:
           
            ZipOutputStream out =
                new ZipOutputStream(
                new BufferedOutputStream(
                new LimitOutputStream(), 512));
           
          Closing this issue as "will not fix".
          Show
          iris Iris Clark added a comment - BT2:EVALUATION As I said in my previous evaluation, the implementations of FilterOutputStream.flush() and FOS.close() have not changed in many years. Any changes to them now would introduce potentially unpleasant side-effects in existing customer code.   The default buffer size of BufferedOutputStream was modified in Tiger (jdk1.5) as a result of bug 4953311 to optimize for performance in common scenarios such as network writes. It is not possible to change this default back to 512.   However there is a simple work-around to get the previous behaviour, simply construct a BufferedOutputStream with an explicit buffer size:     ZipOutputStream out =       new ZipOutputStream(       new BufferedOutputStream(       new LimitOutputStream(), 512));   Closing this issue as "will not fix".

            People

            • Assignee:
              iris Iris Clark
              Reporter:
              ndcosta Nelson Dcosta
            • Votes:
              0 Vote for this issue
              Watchers:
              0 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:
                Imported:
                Indexed: