8220477: Channels.newWriter() does not close if underlying channel throws an IOException
authorbpb
Wed, 17 Apr 2019 08:12:19 -0700
changeset 54569 7689e1cc56fe
parent 54568 b2ed96c35687
child 54570 93b702d2a0cb
8220477: Channels.newWriter() does not close if underlying channel throws an IOException Reviewed-by: alanb
src/java.base/share/classes/sun/nio/cs/StreamDecoder.java
src/java.base/share/classes/sun/nio/cs/StreamEncoder.java
test/jdk/java/nio/channels/Channels/Basic.java
--- a/src/java.base/share/classes/sun/nio/cs/StreamDecoder.java	Wed Apr 17 15:37:20 2019 +0100
+++ b/src/java.base/share/classes/sun/nio/cs/StreamDecoder.java	Wed Apr 17 08:12:19 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -28,10 +28,20 @@
 
 package sun.nio.cs;
 
-import java.io.*;
-import java.nio.*;
-import java.nio.channels.*;
-import java.nio.charset.*;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.io.Reader;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CoderResult;
+import java.nio.charset.CodingErrorAction;
+import java.nio.charset.IllegalCharsetNameException;
 
 public class StreamDecoder extends Reader
 {
@@ -190,8 +200,11 @@
         synchronized (lock) {
             if (closed)
                 return;
-            implClose();
-            closed = true;
+            try {
+                implClose();
+            } finally {
+                closed = true;
+            }
         }
     }
 
--- a/src/java.base/share/classes/sun/nio/cs/StreamEncoder.java	Wed Apr 17 15:37:20 2019 +0100
+++ b/src/java.base/share/classes/sun/nio/cs/StreamEncoder.java	Wed Apr 17 08:12:19 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001, 2005, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -23,15 +23,21 @@
  * questions.
  */
 
-/*
- */
-
 package sun.nio.cs;
 
-import java.io.*;
-import java.nio.*;
-import java.nio.channels.*;
-import java.nio.charset.*;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.channels.WritableByteChannel;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.CoderResult;
+import java.nio.charset.CodingErrorAction;
+import java.nio.charset.IllegalCharsetNameException;
 
 public class StreamEncoder extends Writer
 {
@@ -158,8 +164,11 @@
         synchronized (lock) {
             if (closed)
                 return;
-            implClose();
-            closed = true;
+            try {
+                implClose();
+            } finally {
+                closed = true;
+            }
         }
     }
 
@@ -337,8 +346,13 @@
                 writeBytes();
             if (ch != null)
                 ch.close();
-            else
-                out.close();
+            else {
+                try {
+                    out.flush();
+                } finally {
+                    out.close();
+                }
+            }
         } catch (IOException x) {
             encoder.reset();
             throw x;
--- a/test/jdk/java/nio/channels/Channels/Basic.java	Wed Apr 17 15:37:20 2019 +0100
+++ b/test/jdk/java/nio/channels/Channels/Basic.java	Wed Apr 17 08:12:19 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -22,7 +22,7 @@
  */
 
 /* @test
- * @bug 4417152 4481572 6248930 6725399 6884800
+ * @bug 4417152 4481572 6248930 6725399 6884800 8220477
  * @summary Test Channels basic functionality
  */
 
@@ -31,7 +31,6 @@
 import java.nio.charset.*;
 import java.nio.channels.*;
 
-
 public class Basic {
 
     static String message;
@@ -204,6 +203,8 @@
             writeOut(blah, ITERATIONS);
             testNewReader(blah);
 
+            testNewWriterClose();
+            testNewReaderClose();
         } finally {
             blah.delete();
         }
@@ -399,6 +400,98 @@
         r.close();
         fis.close();
     }
+
+    private static void testNewWriterClose() throws Exception {
+        Writer writer = null;
+        try {
+            WritableByteChannel channel = new WritableByteChannel() {
+                @Override
+                public int write(ByteBuffer src) throws IOException {
+                    return 0;
+                }
+
+                @Override
+                public boolean isOpen() {
+                    return true;
+                }
+
+                @Override
+                public void close() throws IOException {
+                    throw new IOException();
+                }
+            };
+            writer = Channels.newWriter(channel,
+                StandardCharsets.UTF_8.newEncoder(), -1);
+            writer.close();
+        } catch (IOException ioe) {
+            Exception theException = null;
+            try {
+                writer.write(1);
+                writer.flush();
+            } catch (Exception e) {
+                theException = e;
+            } finally {
+                if (theException == null) {
+                    throw new RuntimeException("IOException not thrown");
+                } else if (!(theException instanceof IOException)) {
+                    throw new RuntimeException("Exception not an IOException: "
+                        + theException);
+                } else {
+                    String message = theException.getMessage();
+                    if (!message.equals("Stream closed")) {
+                        throw new RuntimeException("Unexpected message "
+                            + message);
+                    }
+                }
+            }
+        }
+    }
+
+    private static void testNewReaderClose() throws Exception {
+        Reader reader = null;
+        try {
+            ReadableByteChannel channel = new ReadableByteChannel() {
+                @Override
+                public int read(ByteBuffer dst) throws IOException {
+                    dst.put((byte)7);
+                    return 1;
+                }
+
+                @Override
+                public boolean isOpen() {
+                    return true;
+                }
+
+                @Override
+                public void close() throws IOException {
+                    throw new IOException();
+                }
+            };
+            reader = Channels.newReader(channel,
+                StandardCharsets.UTF_8.newDecoder(), -1);
+            reader.close();
+        } catch (IOException ioe) {
+            Exception theException = null;
+            try {
+                reader.read();
+            } catch (Exception e) {
+                theException = e;
+            } finally {
+                if (theException == null) {
+                    throw new RuntimeException("IOException not thrown");
+                } else if (!(theException instanceof IOException)) {
+                    throw new RuntimeException("Exception not an IOException: "
+                        + theException);
+                } else {
+                    String message = theException.getMessage();
+                    if (!message.equals("Stream closed")) {
+                        throw new RuntimeException("Unexpected message "
+                            + message);
+                    }
+                }
+            }
+        }
+    }
 }
 
 class ExtendedFileInputStream extends java.io.FileInputStream {