1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.juneau.commons.io;
18
19 import static org.apache.juneau.commons.utils.AssertionUtils.*;
20
21 import java.io.*;
22
23 /**
24 * A wrapper around an {@link OutputStream} that prevents the underlying stream from being closed.
25 *
26 * <p>
27 * This class wraps an existing {@link OutputStream} and intercepts the {@link #close()} method,
28 * making it a no-op (except for flushing). All other operations are delegated to the underlying
29 * stream. This is useful when you need to pass a stream to code that will close it, but you
30 * want to keep the underlying stream open for further use.
31 *
32 * <h5 class='section'>Features:</h5>
33 * <ul class='spaced-list'>
34 * <li>Prevents closing - {@link #close()} flushes but doesn't close the underlying stream
35 * <li>Transparent delegation - all other operations pass through to the wrapped stream
36 * <li>Useful for resource management - allows multiple consumers without premature closing
37 * </ul>
38 *
39 * <h5 class='section'>Use Cases:</h5>
40 * <ul class='spaced-list'>
41 * <li>Passing streams to APIs that close them, but you need to keep the stream open
42 * <li>Multiple operations on the same stream where intermediate operations might close it
43 * <li>Resource management scenarios where you control the stream lifecycle
44 * <li>Wrapping system streams (System.out, System.err) that should not be closed
45 * </ul>
46 *
47 * <h5 class='section'>Usage:</h5>
48 * <p class='bjava'>
49 * <jc>// Wrap a stream that should not be closed</jc>
50 * FileOutputStream <jv>fos</jv> = <jk>new</jk> FileOutputStream(<js>"output.txt"</js>);
51 * NoCloseOutputStream <jv>wrapper</jv> = <jk>new</jk> NoCloseOutputStream(<jv>fos</jv>);
52 *
53 * <jc>// Pass to code that might close it</jc>
54 * <jv>someMethod</jv>(<jv>wrapper</jv>); <jc>// May call close(), but fos remains open</jc>
55 *
56 * <jc>// Continue using the original stream</jc>
57 * <jv>fos</jv>.write(<js>"more data"</js>.getBytes());
58 * <jv>fos</jv>.close(); <jc>// Close when actually done</jc>
59 * </p>
60 *
61 * <h5 class='section'>Important Notes:</h5>
62 * <ul class='spaced-list'>
63 * <li>The {@link #close()} method flushes the stream but does not close the underlying stream
64 * <li>You are responsible for closing the underlying stream when you're done with it
65 * <li>This wrapper does not prevent resource leaks - ensure the underlying stream is eventually closed
66 * </ul>
67 *
68 * <h5 class='section'>See Also:</h5><ul>
69 * <li class='jc'>{@link NoCloseWriter} - Writer counterpart
70 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauCommonsIO">I/O Package</a>
71 * </ul>
72 */
73 public class NoCloseOutputStream extends OutputStream {
74
75 private final OutputStream os;
76
77 /**
78 * Constructor.
79 *
80 * <p>
81 * Creates a new NoCloseOutputStream that wraps the specified OutputStream. The wrapper
82 * will prevent the underlying stream from being closed via the {@link #close()} method.
83 *
84 * <h5 class='section'>Example:</h5>
85 * <p class='bjava'>
86 * FileOutputStream <jv>fos</jv> = <jk>new</jk> FileOutputStream(<js>"file.txt"</js>);
87 * NoCloseOutputStream <jv>wrapper</jv> = <jk>new</jk> NoCloseOutputStream(<jv>fos</jv>);
88 * </p>
89 *
90 * @param os The OutputStream to wrap. Must not be <jk>null</jk>.
91 */
92 public NoCloseOutputStream(OutputStream os) {
93 this.os = assertArgNotNull("os", os);
94 }
95
96 /**
97 * Flushes the stream but does not close the underlying OutputStream.
98 *
99 * <p>
100 * This method flushes any buffered data to the underlying stream but does not close it.
101 * The underlying stream remains open and can continue to be used after this method is called.
102 *
103 * <h5 class='section'>Example:</h5>
104 * <p class='bjava'>
105 * FileOutputStream <jv>fos</jv> = <jk>new</jk> FileOutputStream(<js>"file.txt"</js>);
106 * NoCloseOutputStream <jv>wrapper</jv> = <jk>new</jk> NoCloseOutputStream(<jv>fos</jv>);
107 * <jv>wrapper</jv>.close(); <jc>// Flushes but doesn't close fos</jc>
108 * <jv>fos</jv>.write(<js>"still works"</js>.getBytes()); <jc>// fos is still open</jc>
109 * </p>
110 *
111 * @throws IOException If an I/O error occurs while flushing.
112 */
113 @Override /* Overridden from OutputStream */
114 public void close() throws IOException {
115 os.flush();
116 }
117
118 @Override /* Overridden from OutputStream */
119 public void flush() throws IOException {
120 os.flush();
121 }
122
123 @Override /* Overridden from OutputStream */
124 public void write(byte[] b) throws IOException {
125 assertArgNotNull("b", b);
126 os.write(b);
127 }
128
129 @Override /* Overridden from OutputStream */
130 public void write(byte[] b, int off, int len) throws IOException {
131 assertArgNotNull("b", b);
132 os.write(b, off, len);
133 }
134
135 @Override /* Overridden from OutputStream */
136 public void write(int b) throws IOException {
137 os.write(b);
138 }
139 }