changeset 11201:5f22b23442ba

8054565: FilterOutputStream.close may throw IOException if called twice and underlying flush or close fails Reviewed-by: alanb, prappo Contributed-by: Chris Hegarty <chris.hegarty@oracle.com>, Nathan Clement <nathan.a.clement@hotmail.com>
author chegar
date Sun, 04 Jan 2015 19:32:46 +0000
parents 9acaa4f57b0b
children d06dbe4fcf73
files src/java.base/share/classes/java/io/FilterOutputStream.java test/java/io/etc/FailingFlushAndClose.java
diffstat 2 files changed, 63 insertions(+), 16 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.base/share/classes/java/io/FilterOutputStream.java	Thu Jan 01 16:14:01 2015 -0800
+++ b/src/java.base/share/classes/java/io/FilterOutputStream.java	Sun Jan 04 19:32:46 2015 +0000
@@ -48,6 +48,8 @@
      */
     protected OutputStream out;
 
+    private boolean closed;
+
     /**
      * Creates an output stream filter built on top of the specified
      * underlying output stream.
@@ -144,9 +146,9 @@
      * Closes this output stream and releases any system resources
      * associated with the stream.
      * <p>
-     * The <code>close</code> method of <code>FilterOutputStream</code>
-     * calls its <code>flush</code> method, and then calls the
-     * <code>close</code> method of its underlying output stream.
+     * When not already closed, the {@code close} method of {@code
+     * FilterOutputStream} calls its {@code flush} method, and then
+     * calls the {@code close} method of its underlying output stream.
      *
      * @exception  IOException  if an I/O error occurs.
      * @see        java.io.FilterOutputStream#flush()
@@ -154,6 +156,9 @@
      */
     @SuppressWarnings("try")
     public void close() throws IOException {
+        if (closed)
+            return;
+        closed = true;
         try (OutputStream ostream = out) {
             flush();
         }
--- a/test/java/io/etc/FailingFlushAndClose.java	Thu Jan 01 16:14:01 2015 -0800
+++ b/test/java/io/etc/FailingFlushAndClose.java	Sun Jan 04 19:32:46 2015 +0000
@@ -25,7 +25,7 @@
 
 /**
  * @test
- * @bug 7015589
+ * @bug 7015589 8054565
  * @summary Test that buffering streams are considered closed even when the
  *    close or flush from the underlying stream fails.
  */
@@ -165,7 +165,7 @@
         }
     }
 
-    static void testFailingClose(InputStream in) throws IOException {
+    static InputStream testFailingClose(InputStream in) throws IOException {
         System.out.println(in.getClass());
         in.read(new byte[100]);
         try {
@@ -176,9 +176,10 @@
             in.read(new byte[100]);
             fail("read did not fail");
         } catch (IOException expected) { }
+        return in;
     }
 
-    static void testFailingClose(OutputStream out) throws IOException {
+    static OutputStream testFailingClose(OutputStream out) throws IOException {
         System.out.println(out.getClass());
         out.write(1);
         try {
@@ -190,9 +191,10 @@
             if (!(out instanceof BufferedOutputStream))
                 fail("write did not fail");
         } catch (IOException expected) { }
+        return out;
     }
 
-    static void testFailingFlush(OutputStream out) throws IOException {
+    static OutputStream testFailingFlush(OutputStream out) throws IOException {
         System.out.println(out.getClass());
         out.write(1);
         try {
@@ -206,9 +208,27 @@
                 fail("close did not fail");
             } catch (IOException expected) { }
         }
+        return out;
     }
 
-    static void testFailingClose(Reader r) throws IOException {
+    static void closeAgain(InputStream in) throws IOException {
+        // assert the given stream should already be closed.
+        try {
+            in.close();
+        } catch (IOException expected) {
+            fail("unexpected IOException from subsequent close");
+        }
+    }
+    static void closeAgain(OutputStream out) throws IOException {
+        // assert the given stream should already be closed.
+        try {
+            out.close();
+        } catch (IOException expected) {
+            fail("unexpected IOException from subsequent close");
+        }
+    }
+
+    static Reader testFailingClose(Reader r) throws IOException {
         System.out.println(r.getClass());
         r.read(new char[100]);
         try {
@@ -219,9 +239,10 @@
             r.read(new char[100]);
             fail("read did not fail");
         } catch (IOException expected) { }
+        return r;
     }
 
-    static void testFailingClose(Writer w) throws IOException {
+    static Writer testFailingClose(Writer w) throws IOException {
         System.out.println(w.getClass());
         w.write("message");
         try {
@@ -232,9 +253,10 @@
             w.write("another message");
             fail("write did not fail");
         } catch (IOException expected) { }
+        return w;
     }
 
-    static void testFailingFlush(Writer w) throws IOException {
+    static Writer testFailingFlush(Writer w) throws IOException {
         System.out.println(w.getClass());
         w.write("message");
         try {
@@ -249,18 +271,38 @@
                 fail("close did not fail");
             } catch (IOException expected) { }
         }
+        return w;
+    }
+
+    static Reader closeAgain(Reader r) throws IOException {
+        // assert the given stream should already be closed.
+        try {
+            r.close();
+        } catch (IOException expected) {
+            fail("unexpected IOException from subsequent close");
+        }
+        return r;
+    }
+    static Writer closeAgain(Writer w) throws IOException {
+        // assert the given stream should already be closed.
+        try {
+            w.close();
+        } catch (IOException expected) {
+            fail("unexpected IOException from subsequent close");
+        }
+        return w;
     }
 
     public static void main(String[] args) throws IOException {
 
-        testFailingClose(new BufferedInputStream(new FailingCloseInputStream()));
-        testFailingClose(new BufferedOutputStream(new FailingCloseOutputStream()));
+        closeAgain(testFailingClose(new BufferedInputStream(new FailingCloseInputStream())));
+        closeAgain(testFailingClose(new BufferedOutputStream(new FailingCloseOutputStream())));
 
-        testFailingClose(new BufferedReader(new FailingCloseReader()));
-        testFailingClose(new BufferedWriter(new FailingCloseWriter()));
+        closeAgain(testFailingClose(new BufferedReader(new FailingCloseReader())));
+        closeAgain(testFailingClose(new BufferedWriter(new FailingCloseWriter())));
 
-        testFailingFlush(new BufferedOutputStream(new FailingFlushOutputStream()));
-        testFailingFlush(new BufferedWriter(new FailingFlushWriter()));
+        closeAgain(testFailingFlush(new BufferedOutputStream(new FailingFlushOutputStream())));
+        closeAgain(testFailingFlush(new BufferedWriter(new FailingFlushWriter())));
 
         if (failed > 0)
             throw new RuntimeException(failed + " test(s) failed - see log for details");