JDK-6378948 : BufferedWriter.close() doesn't close underlying Writer if flushBuffer() throws IOException
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.io
  • Affected Version: 5.0
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2006-01-31
  • Updated: 2012-01-11
  • Resolved: 2006-02-01
Related Reports
Duplicate :  
Description
FULL PRODUCT VERSION :
java version "1.4.2"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.
Java HotSpot(TM) Client VM (build 1.4.2-b28, mixed mode)

FULL OS VERSION :
Microsoft Windows XP [Version 5.1.2600]

A DESCRIPTION OF THE PROBLEM :
If a BufferedWriter is used to write to a file, and an IOException occurs during the flush call in BufferedWriter.close(), then the file will not be closed.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Create a floppy disk with little (e.g., 1K) free disk space.
Run the attached program.
When prompted, specify the name for a new file on the floppy disk.
The program should display the message "Closing the writer." on standard error, then display a dialog instructing you to try to delete the file.
Try to delete the file.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
You should be able to delete the file, BufferedWriter.close() was called.
ACTUAL -
You cannot delete the file.

REPRODUCIBILITY :
This bug can be reproduced always.

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

/**
 * This program illustrates that some writers, such as BufferedWriter and
 * OutputStreamWriter, don't close the underlying Writer or OutputStream if an
 * exception is thrown during the close operation.
 *
 * The works by determining the number of strings that need to be written to
 * a file on a given directory in order for a flush operation to throw an
 * IOException. It then uses the standard approach of wrapping a FileWriter in
 * a BufferedWriter to write the file. After writing the file, the BufferedWriter
 * is closed. The program then displays a dialog that asks to user to verify
 * that the file is still open by trying to delete the file.
 *
 * The program determines the number of strings that need to be written by
 * writing strings to a temporary file in the target directory. Once it has
 * written enough strings to cause an IOException, it closes and deletes the
 * file. The approach used to create this file is to wrap a FileOutputStream
 * in an OutputStreamWriter and BufferedWriter. Closing the FileOutputStream
 * actually closes the underlying file, which allows it to be deleted. This
 * approach provides a workaround for the defect.
 */
public class CloseFailure
{
  public static void main (String[] args)
  {
    JFileChooser chooser = new JFileChooser();
    
    chooser.setDialogTitle ("Specify a file on a floppy disk with little free space.");
    if (chooser.showSaveDialog (null) != JFileChooser.APPROVE_OPTION)
      return;
    
    File   file    = chooser.getSelectedFile();
    int     nWrites = determineWritesRequired (file.getParentFile());
    Writer writer  = null;
    
    try
    {
      writer = new BufferedWriter (new FileWriter (file));
      writeNStrings (writer, nWrites);
      System.err.println ("Closing the writer.");
      writer.close();
      
    } catch (IOException ioe) {
      JOptionPane.showMessageDialog (null,
        "You can verify that the file is still open\nby trying to delete it.");
    } finally {
      System.exit (0);
    } // try
  } // main
  
  private static final String SOME_STRING =
    "This program illustrates a defect in the implementation of BufferedWriter.close().\n";
  
  /**
   * Return the number of SOME_STRINGs that need to be written to the
   * given directory to cause an IOException when the writer is flushed.
   */
  private static int determineWritesRequired (File directory)
  {
    File     file   = null;
    OutputStream stream  = null;
    Writer     writer  = null;
    int       result  = 0;
    
    try
    {
      file   = File.createTempFile ("Temp", ".txt", directory);
      stream = new FileOutputStream (file);
      writer = new BufferedWriter (new OutputStreamWriter (stream));
      
      while (true)
      {
        ++result;
        writer.write (SOME_STRING);
        writer.flush();
      } // while
      
    } catch (IOException ioe) {
      try
      {
        stream.close();
        file.delete();
      } catch (IOException e) {
        e.printStackTrace();
      } // try
    } // try
    
    return result;
  } // determineWritesRequired
  
  /**
   * Write n SOME_STRINGS to the given Writer.
   */
  private static void writeNStrings (Writer writer, int n) throws IOException
  {
    try
    {
      for (int i = 0; i < n; ++i)
        writer.write (SOME_STRING);
      
    } catch (IOException e) {
      System.err.println ("We didn't get the number of writes correct.");
    } // try
  } // writeSomeStuff
} // CloseFailure
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Wrap a FileOutputStream in an OutputStreamWriter instead of using a FileWriter and close the FileOutputStream if there is an exception writing to or closing the Writer.

Comments
EVALUATION Duplicate of bug 6266377 which was fixed in Mustang b61.
01-02-2006