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

BufferedWriter.close() fails to release resources if exception occurs during flush

XMLWordPrintable

    • b61
    • x86
    • windows_xp

      FULL PRODUCT VERSION :
      java version "1.5.0_01"
      Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_01-b08)
      Java HotSpot(TM) Client VM (build 1.5.0_01-b08, mixed mode)

      ADDITIONAL OS VERSION INFORMATION :
      Microsoft Windows XP [Version 5.1.2600]

      A DESCRIPTION OF THE PROBLEM :
      If an exception occurs when closing a BufferedWriter that is writing to a file, the underlying stream does not release its resources correctly which means that subsequent file operations can fail unecessarily. I can reproduce this always with the attached test case when the destination disk is already full. The temporary files will be created, but closing the buffered stream will result in an exception due to the lack of disk space available for flushing the buffer. The next attempt to delete the file fails because the resource hasn't been released properly. Calling the garbage collector causes the resource to be released correctly. The problem seems to lie with the implementation of BufferedWriter.close() (and this may also apply to other buffered streams) where there is a call to flushBuffer() before the call to close the underlying writer. If the call to close() were made in a finally block, then the underlying stream would be closed

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      There are probably lots of ways to recreate this - this is merely one way. Compile and run the attached test case on a drive where the disk is full. The test case should be run with a command line like java DeleteFileTest <path>. Where <path> points to a valid path on a drive that has no more disk space available.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      If there were no bug, then I would not expect any output from the test case at all.
      ACTUAL -
      java.io.IOException: There is not enough space on the disk
      at java.io.FileOutputStream.writeBytes(Native Method)
      at java.io.FileOutputStream.write(FileOutputStream.java:260)
      at sun.nio.cs.StreamEncoder$CharsetSE.writeBytes(StreamEncoder.java:336)
      at sun.nio.cs.StreamEncoder$CharsetSE.implClose(StreamEncoder.java:427)
      at sun.nio.cs.StreamEncoder.close(StreamEncoder.java:160)
      at java.io.OutputStreamWriter.close(OutputStreamWriter.java:222)
      at java.io.BufferedWriter.close(BufferedWriter.java:244)
      at DeleteFileTest.writeStringToFile(DeleteFileTest.java:47)
      at DeleteFileTest.main(DeleteFileTest.java:15)
      File 1 still exists, but should have been deleted
      java.io.IOException: There is not enough space on the disk
      at java.io.FileOutputStream.writeBytes(Native Method)
      at java.io.FileOutputStream.write(FileOutputStream.java:260)
      at sun.nio.cs.StreamEncoder$CharsetSE.writeBytes(StreamEncoder.java:336)
      at sun.nio.cs.StreamEncoder$CharsetSE.implClose(StreamEncoder.java:427)
      at sun.nio.cs.StreamEncoder.close(StreamEncoder.java:160)
      at java.io.OutputStreamWriter.close(OutputStreamWriter.java:222)
      at java.io.BufferedWriter.close(BufferedWriter.java:244)
      at DeleteFileTest.writeStringToFile(DeleteFileTest.java:47)
      at DeleteFileTest.main(DeleteFileTest.java:25)

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      import java.io.*;

      public class DeleteFileTest {
          private static String text;

          public static void main(String[] args) {
              StringBuffer buf = new StringBuffer(10000);
              for (int i = 0; i < 2000; i++) {
                  buf.append("Test ");
              }
              text = buf.toString();
              File file1 = null, file2 = null;
              try {
                  file1 = File.createTempFile("firstDeleteTest", ".tmp", new File(args[0]));
                  writeStringToFile(file1, text);
              } catch (IOException e) {
                  e.printStackTrace();
              } finally {
                  file1.delete();
              }
              if (file1.exists()) System.out.println("File 1 still exists, but should have been deleted");

              try {
                  file2 = File.createTempFile("secondDeleteTest", ".tmp", new File(args[0]));
                  writeStringToFile(file2, text);
              } catch (IOException e) {
                  e.printStackTrace();
              } finally {
                  System.gc();
                  file2.delete();
              }

              if (file2.exists()) System.out.println("File 2 still exists, but should have been deleted");
          }

          private static void writeStringToFile(File file, String text) throws IOException{
              BufferedWriter out = new BufferedWriter(new FileWriter(file));

              try {
                  out.write(text);
              } catch (IOException e){
                  throw e;
              } finally {
                      out.close();
              }
          }

      }

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

      CUSTOMER SUBMITTED WORKAROUND :
      The workaround is demonstrated in the test case as well. Calling System.gc() before attempting to delete the file results in the underlying streams getting cleaned up and thus the delete can succeed.
      ###@###.### 2005-05-06 09:00:31 GMT

            jhangalsunw Jayalaxmi Hangal (Inactive)
            ndcosta Nelson Dcosta (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: