1 /*
2  * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 import java.io.*;
25 
26 /**
27  * @test
28  * @bug 7015589 8054565
29  * @summary Test that buffering streams are considered closed even when the
30  *    close or flush from the underlying stream fails.
31  */
32 
33 public class FailingFlushAndClose {
34 
35     static int failed;
36 
fail(String msg)37     static void fail(String msg) {
38         System.err.println("FAIL: " + msg);
39         failed++;
40     }
41 
failWithIOE(String msg)42     static void failWithIOE(String msg) throws IOException {
43         fail(msg);
44         throw new IOException(msg);
45     }
46 
47     static class FailingCloseInputStream extends InputStream {
48         boolean closed;
49         @Override
read()50         public int read()throws IOException {
51             if (closed)
52                 failWithIOE("input stream is closed");
53             return 1;
54         }
55         @Override
close()56         public void close() throws IOException {
57             if (!closed) {
58                 closed = true;
59                 throw new IOException("close failed");
60             }
61         }
62     }
63 
64     static class FailingCloseOutputStream extends OutputStream {
65         boolean closed;
66         @Override
write(int b)67         public void write(int b) throws IOException {
68             if (closed)
69                 failWithIOE("output stream is closed");
70         }
71         @Override
flush()72         public void flush() throws IOException {
73             if (closed)
74                 failWithIOE("output stream is closed");
75         }
76         @Override
close()77         public void close() throws IOException {
78             if (!closed) {
79                 closed = true;
80                 throw new IOException("close failed");
81             }
82         }
83     }
84 
85     static class FailingFlushOutputStream extends OutputStream {
86         boolean closed;
87         @Override
write(int b)88         public void write(int b) throws IOException {
89             if (closed)
90                 failWithIOE("output stream is closed");
91         }
92         @Override
flush()93         public void flush() throws IOException {
94             if (closed) {
95                 failWithIOE("output stream is closed");
96             } else {
97                 throw new IOException("flush failed");
98             }
99         }
100         @Override
close()101         public void close() throws IOException {
102             closed = true;
103         }
104     }
105 
106     static class FailingCloseReader extends Reader {
107         boolean closed;
108         @Override
read(char[] cbuf, int off, int len)109         public int read(char[] cbuf, int off, int len) throws IOException {
110             if (closed)
111                 failWithIOE("reader is closed");
112             return 1;
113         }
114         @Override
close()115         public void close() throws IOException {
116             if (!closed) {
117                 closed = true;
118                 throw new IOException("close failed");
119             }
120         }
121     }
122 
123     static class FailingCloseWriter extends Writer {
124         boolean closed;
125         @Override
write(char[] cbuf, int off, int len)126         public void write(char[] cbuf, int off, int len) throws IOException {
127             if (closed)
128                 failWithIOE("writer is closed");
129         }
130         @Override
flush()131         public void flush() throws IOException {
132             if (closed)
133                 failWithIOE("writer is closed");
134         }
135         @Override
close()136         public void close() throws IOException {
137             if (!closed) {
138                 closed = true;
139                 throw new IOException("close failed");
140             }
141         }
142     }
143 
144     static class FailingFlushWriter extends Writer {
145         boolean closed;
146         @Override
write(char[] cbuf, int off, int len)147         public void write(char[] cbuf, int off, int len) throws IOException {
148             if (closed)
149                 failWithIOE("writer is closed");
150         }
151         @Override
flush()152         public void flush() throws IOException {
153             if (closed) {
154                 failWithIOE("writer is closed");
155             } else {
156                 throw new IOException("flush failed");
157             }
158         }
159         @Override
close()160         public void close() throws IOException {
161             if (!closed) {
162                 closed = true;
163                 throw new IOException("close failed");
164             }
165         }
166     }
167 
testFailingClose(InputStream in)168     static InputStream testFailingClose(InputStream in) throws IOException {
169         System.out.println(in.getClass());
170         in.read(new byte[100]);
171         try {
172             in.close();
173             fail("close did not fail");
174         } catch (IOException expected) { }
175         try {
176             in.read(new byte[100]);
177             fail("read did not fail");
178         } catch (IOException expected) { }
179         return in;
180     }
181 
testFailingClose(OutputStream out)182     static OutputStream testFailingClose(OutputStream out) throws IOException {
183         System.out.println(out.getClass());
184         out.write(1);
185         try {
186             out.close();
187             fail("close did not fail");
188         } catch (IOException expected) { }
189         try {
190             out.write(1);
191             if (!(out instanceof BufferedOutputStream))
192                 fail("write did not fail");
193         } catch (IOException expected) { }
194         return out;
195     }
196 
testFailingFlush(OutputStream out)197     static OutputStream testFailingFlush(OutputStream out) throws IOException {
198         System.out.println(out.getClass());
199         out.write(1);
200         try {
201             out.flush();
202             fail("flush did not fail");
203         } catch (IOException expected) { }
204         if (out instanceof BufferedOutputStream) {
205             out.write(1);
206             try {
207                 out.close();
208                 fail("close did not fail");
209             } catch (IOException expected) { }
210         }
211         return out;
212     }
213 
closeAgain(InputStream in)214     static void closeAgain(InputStream in) throws IOException {
215         // assert the given stream should already be closed.
216         try {
217             in.close();
218         } catch (IOException expected) {
219             fail("unexpected IOException from subsequent close");
220         }
221     }
closeAgain(OutputStream out)222     static void closeAgain(OutputStream out) throws IOException {
223         // assert the given stream should already be closed.
224         try {
225             out.close();
226         } catch (IOException expected) {
227             fail("unexpected IOException from subsequent close");
228         }
229     }
230 
testFailingClose(Reader r)231     static Reader testFailingClose(Reader r) throws IOException {
232         System.out.println(r.getClass());
233         r.read(new char[100]);
234         try {
235             r.close();
236             fail("close did not fail");
237         } catch (IOException expected) { }
238         try {
239             r.read(new char[100]);
240             fail("read did not fail");
241         } catch (IOException expected) { }
242         return r;
243     }
244 
testFailingClose(Writer w)245     static Writer testFailingClose(Writer w) throws IOException {
246         System.out.println(w.getClass());
247         w.write("message");
248         try {
249             w.close();
250             fail("close did not fail");
251         } catch (IOException expected) { }
252         try {
253             w.write("another message");
254             fail("write did not fail");
255         } catch (IOException expected) { }
256         return w;
257     }
258 
testFailingFlush(Writer w)259     static Writer testFailingFlush(Writer w) throws IOException {
260         System.out.println(w.getClass());
261         w.write("message");
262         try {
263             w.flush();
264             fail("flush did not fail");
265         } catch (IOException expected) { }
266         if (w instanceof BufferedWriter) {
267             // assume this message will be buffered
268             w.write("another message");
269             try {
270                 w.close();
271                 fail("close did not fail");
272             } catch (IOException expected) { }
273         }
274         return w;
275     }
276 
closeAgain(Reader r)277     static Reader closeAgain(Reader r) throws IOException {
278         // assert the given stream should already be closed.
279         try {
280             r.close();
281         } catch (IOException expected) {
282             fail("unexpected IOException from subsequent close");
283         }
284         return r;
285     }
closeAgain(Writer w)286     static Writer closeAgain(Writer w) throws IOException {
287         // assert the given stream should already be closed.
288         try {
289             w.close();
290         } catch (IOException expected) {
291             fail("unexpected IOException from subsequent close");
292         }
293         return w;
294     }
295 
main(String[] args)296     public static void main(String[] args) throws IOException {
297 
298         closeAgain(testFailingClose(new BufferedInputStream(new FailingCloseInputStream())));
299         closeAgain(testFailingClose(new BufferedOutputStream(new FailingCloseOutputStream())));
300 
301         closeAgain(testFailingClose(new BufferedReader(new FailingCloseReader())));
302         closeAgain(testFailingClose(new BufferedWriter(new FailingCloseWriter())));
303 
304         closeAgain(testFailingFlush(new BufferedOutputStream(new FailingFlushOutputStream())));
305         closeAgain(testFailingFlush(new BufferedWriter(new FailingFlushWriter())));
306 
307         if (failed > 0)
308             throw new RuntimeException(failed + " test(s) failed - see log for details");
309     }
310 }
311