8220477: Channels.newWriter() does not close if underlying channel throws an IOException
Reviewed-by: alanb
--- 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 {