8054565: FilterOutputStream.close may throw IOException if called twice and underlying flush or close fails
Co-authored-by: Nathan Clement <nathan.a.clement@hotmail.com> Reviewed-by: alanb, prappo
This commit is contained in:
parent
d708838bee
commit
916c818ec2
@ -48,6 +48,8 @@ class FilterOutputStream extends OutputStream {
|
|||||||
*/
|
*/
|
||||||
protected OutputStream out;
|
protected OutputStream out;
|
||||||
|
|
||||||
|
private boolean closed;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an output stream filter built on top of the specified
|
* Creates an output stream filter built on top of the specified
|
||||||
* underlying output stream.
|
* underlying output stream.
|
||||||
@ -144,9 +146,9 @@ class FilterOutputStream extends OutputStream {
|
|||||||
* Closes this output stream and releases any system resources
|
* Closes this output stream and releases any system resources
|
||||||
* associated with the stream.
|
* associated with the stream.
|
||||||
* <p>
|
* <p>
|
||||||
* The <code>close</code> method of <code>FilterOutputStream</code>
|
* When not already closed, the {@code close} method of {@code
|
||||||
* calls its <code>flush</code> method, and then calls the
|
* FilterOutputStream} calls its {@code flush} method, and then
|
||||||
* <code>close</code> method of its underlying output stream.
|
* calls the {@code close} method of its underlying output stream.
|
||||||
*
|
*
|
||||||
* @exception IOException if an I/O error occurs.
|
* @exception IOException if an I/O error occurs.
|
||||||
* @see java.io.FilterOutputStream#flush()
|
* @see java.io.FilterOutputStream#flush()
|
||||||
@ -154,6 +156,9 @@ class FilterOutputStream extends OutputStream {
|
|||||||
*/
|
*/
|
||||||
@SuppressWarnings("try")
|
@SuppressWarnings("try")
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
|
if (closed)
|
||||||
|
return;
|
||||||
|
closed = true;
|
||||||
try (OutputStream ostream = out) {
|
try (OutputStream ostream = out) {
|
||||||
flush();
|
flush();
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ import java.io.*;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @test
|
* @test
|
||||||
* @bug 7015589
|
* @bug 7015589 8054565
|
||||||
* @summary Test that buffering streams are considered closed even when the
|
* @summary Test that buffering streams are considered closed even when the
|
||||||
* close or flush from the underlying stream fails.
|
* close or flush from the underlying stream fails.
|
||||||
*/
|
*/
|
||||||
@ -165,7 +165,7 @@ public class FailingFlushAndClose {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void testFailingClose(InputStream in) throws IOException {
|
static InputStream testFailingClose(InputStream in) throws IOException {
|
||||||
System.out.println(in.getClass());
|
System.out.println(in.getClass());
|
||||||
in.read(new byte[100]);
|
in.read(new byte[100]);
|
||||||
try {
|
try {
|
||||||
@ -176,9 +176,10 @@ public class FailingFlushAndClose {
|
|||||||
in.read(new byte[100]);
|
in.read(new byte[100]);
|
||||||
fail("read did not fail");
|
fail("read did not fail");
|
||||||
} catch (IOException expected) { }
|
} catch (IOException expected) { }
|
||||||
|
return in;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void testFailingClose(OutputStream out) throws IOException {
|
static OutputStream testFailingClose(OutputStream out) throws IOException {
|
||||||
System.out.println(out.getClass());
|
System.out.println(out.getClass());
|
||||||
out.write(1);
|
out.write(1);
|
||||||
try {
|
try {
|
||||||
@ -190,9 +191,10 @@ public class FailingFlushAndClose {
|
|||||||
if (!(out instanceof BufferedOutputStream))
|
if (!(out instanceof BufferedOutputStream))
|
||||||
fail("write did not fail");
|
fail("write did not fail");
|
||||||
} catch (IOException expected) { }
|
} catch (IOException expected) { }
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void testFailingFlush(OutputStream out) throws IOException {
|
static OutputStream testFailingFlush(OutputStream out) throws IOException {
|
||||||
System.out.println(out.getClass());
|
System.out.println(out.getClass());
|
||||||
out.write(1);
|
out.write(1);
|
||||||
try {
|
try {
|
||||||
@ -206,9 +208,27 @@ public class FailingFlushAndClose {
|
|||||||
fail("close did not fail");
|
fail("close did not fail");
|
||||||
} catch (IOException expected) { }
|
} 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());
|
System.out.println(r.getClass());
|
||||||
r.read(new char[100]);
|
r.read(new char[100]);
|
||||||
try {
|
try {
|
||||||
@ -219,9 +239,10 @@ public class FailingFlushAndClose {
|
|||||||
r.read(new char[100]);
|
r.read(new char[100]);
|
||||||
fail("read did not fail");
|
fail("read did not fail");
|
||||||
} catch (IOException expected) { }
|
} catch (IOException expected) { }
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void testFailingClose(Writer w) throws IOException {
|
static Writer testFailingClose(Writer w) throws IOException {
|
||||||
System.out.println(w.getClass());
|
System.out.println(w.getClass());
|
||||||
w.write("message");
|
w.write("message");
|
||||||
try {
|
try {
|
||||||
@ -232,9 +253,10 @@ public class FailingFlushAndClose {
|
|||||||
w.write("another message");
|
w.write("another message");
|
||||||
fail("write did not fail");
|
fail("write did not fail");
|
||||||
} catch (IOException expected) { }
|
} catch (IOException expected) { }
|
||||||
|
return w;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void testFailingFlush(Writer w) throws IOException {
|
static Writer testFailingFlush(Writer w) throws IOException {
|
||||||
System.out.println(w.getClass());
|
System.out.println(w.getClass());
|
||||||
w.write("message");
|
w.write("message");
|
||||||
try {
|
try {
|
||||||
@ -249,18 +271,38 @@ public class FailingFlushAndClose {
|
|||||||
fail("close did not fail");
|
fail("close did not fail");
|
||||||
} catch (IOException expected) { }
|
} 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 {
|
public static void main(String[] args) throws IOException {
|
||||||
|
|
||||||
testFailingClose(new BufferedInputStream(new FailingCloseInputStream()));
|
closeAgain(testFailingClose(new BufferedInputStream(new FailingCloseInputStream())));
|
||||||
testFailingClose(new BufferedOutputStream(new FailingCloseOutputStream()));
|
closeAgain(testFailingClose(new BufferedOutputStream(new FailingCloseOutputStream())));
|
||||||
|
|
||||||
testFailingClose(new BufferedReader(new FailingCloseReader()));
|
closeAgain(testFailingClose(new BufferedReader(new FailingCloseReader())));
|
||||||
testFailingClose(new BufferedWriter(new FailingCloseWriter()));
|
closeAgain(testFailingClose(new BufferedWriter(new FailingCloseWriter())));
|
||||||
|
|
||||||
testFailingFlush(new BufferedOutputStream(new FailingFlushOutputStream()));
|
closeAgain(testFailingFlush(new BufferedOutputStream(new FailingFlushOutputStream())));
|
||||||
testFailingFlush(new BufferedWriter(new FailingFlushWriter()));
|
closeAgain(testFailingFlush(new BufferedWriter(new FailingFlushWriter())));
|
||||||
|
|
||||||
if (failed > 0)
|
if (failed > 0)
|
||||||
throw new RuntimeException(failed + " test(s) failed - see log for details");
|
throw new RuntimeException(failed + " test(s) failed - see log for details");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user