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 */
23import java.io.BufferedOutputStream;
24import java.io.FilterOutputStream;
25import java.io.IOException;
26import java.io.OutputStream;
27
28/*
29 * @test
30 * @bug 8042377
31 * @summary Ensure suppressed exceptions are properly handled in close()
32 */
33public 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
38    public static void main(String[] args) throws java.io.IOException {
39        SuppressedException test = new SuppressedException();
40        test.test();
41    }
42
43    private FilterOutputStream createOutputStream(OutputStream out,
44        boolean isBuffered) {
45        return isBuffered ? new BufferedOutputStream(out) :
46            new FilterOutputStream(out);
47    }
48
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
159        OutputStreamFailsWithException() {
160            throwSeparateCloseException = null;
161            throwSeparateFlushException = null;
162        }
163
164        OutputStreamFailsWithException(boolean throwCloseException,
165                boolean throwFlushException) {
166            throwSeparateCloseException = throwCloseException;
167            throwSeparateFlushException = throwFlushException;
168        }
169
170        @Override
171        public void write(int i) throws IOException {
172            throw new UnsupportedOperationException("");
173        }
174
175        @Override
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
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