1 /*
2  * Copyright (c) 2015, 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 import java.io.BufferedOutputStream;
24 import java.io.FilterOutputStream;
25 import java.io.IOException;
26 import java.io.OutputStream;
27 
28 /*
29  * @test
30  * @bug 8042377
31  * @summary Ensure suppressed exceptions are properly handled in close()
32  */
33 public class SuppressedException {
34     private static final String CLOSE_MESSAGE = "Close exception";
35     private static final String FLUSH_MESSAGE = "Flush exception";
36     private static final String SAME_MESSAGE = "Same exception";
37 
main(String[] args)38     public static void main(String[] args) throws java.io.IOException {
39         SuppressedException test = new SuppressedException();
40         test.test();
41     }
42 
createOutputStream(OutputStream out, boolean isBuffered)43     private FilterOutputStream createOutputStream(OutputStream out,
44         boolean isBuffered) {
45         return isBuffered ? new BufferedOutputStream(out) :
46             new FilterOutputStream(out);
47     }
48 
test()49     private void test() {
50         int failures = 0;
51         FilterOutputStream buf;
52 
53         boolean[] isBuffered = new boolean[] {false, true};
54         for (boolean buffered : isBuffered) {
55             System.err.println("\n>>> Buffered: " + buffered + " <<<");
56             System.err.flush();
57 
58             try {
59                 buf = createOutputStream(new OutputStreamFailsWithException(),
60                         buffered);
61                 buf.close();
62                 System.err.println("\nNo IOException thrown for same exception");
63                 failures++;
64             } catch (IOException expected) {
65                 if (!expected.getMessage().equals(SAME_MESSAGE)) {
66                     System.err.println("\nIOException with unexpected message thrown");
67                     expected.printStackTrace();
68                     failures++;
69                 }
70             } catch (IllegalArgumentException unexpected) {
71                 System.err.println("\nUnexpected IllegalArgumentException thrown");
72                 unexpected.printStackTrace();
73                 failures++;
74             }
75 
76             try {
77                 buf = createOutputStream(
78                         new OutputStreamFailsWithException(false, false),
79                         buffered);
80                 buf.close();
81             } catch (IOException e) {
82                 System.err.println("\nUnexpected IOException thrown");
83                 e.printStackTrace();
84                 failures++;
85             }
86 
87             try {
88                 buf = createOutputStream(
89                         new OutputStreamFailsWithException(true, false),
90                         buffered);
91                 buf.close();
92             } catch (IOException e) {
93                 if (!e.getMessage().equals(CLOSE_MESSAGE)) {
94                     System.err.println("\nIOException with unexpected message thrown");
95                     e.printStackTrace();
96                     failures++;
97                 }
98             }
99 
100             try {
101                 buf = createOutputStream(
102                         new OutputStreamFailsWithException(false, true),
103                         buffered);
104                 buf.close();
105             } catch (IOException e) {
106                 if (!e.getMessage().equals(FLUSH_MESSAGE)) {
107                     System.err.println("\nIOException with unexpected message thrown");
108                     e.printStackTrace();
109                     failures++;
110                 }
111             }
112 
113             try {
114                 buf = createOutputStream(
115                         new OutputStreamFailsWithException(true, true),
116                         buffered);
117                 buf.close();
118             } catch (IOException e) {
119                 if (!e.getMessage().equals(CLOSE_MESSAGE)) {
120                     System.err.println("\nIOException with unexpected message thrown");
121                     e.printStackTrace();
122                     failures++;
123                 }
124 
125                 Throwable[] suppressed = e.getSuppressed();
126                 if (suppressed == null) {
127                     System.err.println("\nExpected suppressed exception not present");
128                     e.printStackTrace();
129                     failures++;
130                 } else if (suppressed.length != 1) {
131                     System.err.println("\nUnexpected number of suppressed exceptions");
132                     e.printStackTrace();
133                     failures++;
134                 } else if (!(suppressed[0] instanceof IOException)) {
135                     System.err.println("\nSuppressed exception is not an IOException");
136                     e.printStackTrace();
137                     failures++;
138                 } else if (!suppressed[0].getMessage().equals(FLUSH_MESSAGE)) {
139                     System.err.println("\nIOException with unexpected message thrown");
140                     e.printStackTrace();
141                     failures++;
142                 }
143             }
144         }
145 
146         if (failures > 0) {
147             throw new RuntimeException("Test failed with " + failures + " errors");
148         } else {
149             System.out.println("Test succeeded.");
150         }
151     }
152 
153     class OutputStreamFailsWithException extends OutputStream {
154         private final IOException sameException = new IOException(SAME_MESSAGE);
155 
156         private final Boolean throwSeparateCloseException;
157         private final Boolean throwSeparateFlushException;
158 
OutputStreamFailsWithException()159         OutputStreamFailsWithException() {
160             throwSeparateCloseException = null;
161             throwSeparateFlushException = null;
162         }
163 
OutputStreamFailsWithException(boolean throwCloseException, boolean throwFlushException)164         OutputStreamFailsWithException(boolean throwCloseException,
165                 boolean throwFlushException) {
166             throwSeparateCloseException = throwCloseException;
167             throwSeparateFlushException = throwFlushException;
168         }
169 
170         @Override
write(int i)171         public void write(int i) throws IOException {
172             throw new UnsupportedOperationException("");
173         }
174 
175         @Override
flush()176         public void flush() throws IOException {
177             System.out.println("flush()");
178             if (throwSeparateFlushException == null) {
179                 throw sameException;
180             } else if (throwSeparateFlushException) {
181                 throw new IOException(FLUSH_MESSAGE);
182             }
183         }
184 
185         @Override
close()186         public void close() throws IOException {
187             System.out.println("close()");
188             if (throwSeparateCloseException == null) {
189                 throw sameException;
190             } else if (throwSeparateCloseException) {
191                 throw new IOException(CLOSE_MESSAGE);
192             }
193         }
194     }
195 }
196