001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.commons.io;
018
019
020import java.io.*;
021import static org.apache.juneau.commons.utils.Shorts.*;
022
023/**
024 * A wrapper around an {@link OutputStream} that prevents the underlying stream from being closed.
025 *
026 * <p>
027 * This class wraps an existing {@link OutputStream} and intercepts the {@link #close()} method,
028 * making it a no-op (except for flushing). All other operations are delegated to the underlying
029 * stream. This is useful when you need to pass a stream to code that will close it, but you
030 * want to keep the underlying stream open for further use.
031 *
032 * <h5 class='section'>Features:</h5>
033 * <ul class='spaced-list'>
034 *    <li>Prevents closing - {@link #close()} flushes but doesn't close the underlying stream
035 *    <li>Transparent delegation - all other operations pass through to the wrapped stream
036 *    <li>Useful for resource management - allows multiple consumers without premature closing
037 * </ul>
038 *
039 * <h5 class='section'>Use Cases:</h5>
040 * <ul class='spaced-list'>
041 *    <li>Passing streams to APIs that close them, but you need to keep the stream open
042 *    <li>Multiple operations on the same stream where intermediate operations might close it
043 *    <li>Resource management scenarios where you control the stream lifecycle
044 *    <li>Wrapping system streams (System.out, System.err) that should not be closed
045 * </ul>
046 *
047 * <h5 class='section'>Usage:</h5>
048 * <p class='bjava'>
049 *    <jc>// Wrap a stream that should not be closed</jc>
050 *    FileOutputStream <jv>fos</jv> = <jk>new</jk> FileOutputStream(<js>"output.txt"</js>);
051 *    NoCloseOutputStream <jv>wrapper</jv> = <jk>new</jk> NoCloseOutputStream(<jv>fos</jv>);
052 *
053 *    <jc>// Pass to code that might close it</jc>
054 *    <jv>someMethod</jv>(<jv>wrapper</jv>);  <jc>// May call close(), but fos remains open</jc>
055 *
056 *    <jc>// Continue using the original stream</jc>
057 *    <jv>fos</jv>.write(<js>"more data"</js>.getBytes());
058 *    <jv>fos</jv>.close();  <jc>// Close when actually done</jc>
059 * </p>
060 *
061 * <h5 class='section'>Important Notes:</h5>
062 * <ul class='spaced-list'>
063 *    <li>The {@link #close()} method flushes the stream but does not close the underlying stream
064 *    <li>You are responsible for closing the underlying stream when you're done with it
065 *    <li>This wrapper does not prevent resource leaks - ensure the underlying stream is eventually closed
066 * </ul>
067 *
068 * <h5 class='section'>See Also:</h5><ul>
069 *    <li class='jc'>{@link NoCloseWriter} - Writer counterpart
070 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauCommonsIO">I/O Package</a>
071 * </ul>
072 */
073@SuppressWarnings({
074   "java:S1192", // Duplicated literals (argument/property names) read more clearly inline than as constants
075   "resource" // Intentionally not owned; this wrapper deliberately does not close the underlying stream
076})
077public class NoCloseOutputStream extends OutputStream {
078
079   private final OutputStream os;
080
081   /**
082    * Constructor.
083    *
084    * <p>
085    * Creates a new NoCloseOutputStream that wraps the specified OutputStream. The wrapper
086    * will prevent the underlying stream from being closed via the {@link #close()} method.
087    *
088    * <h5 class='section'>Example:</h5>
089    * <p class='bjava'>
090    *    FileOutputStream <jv>fos</jv> = <jk>new</jk> FileOutputStream(<js>"file.txt"</js>);
091    *    NoCloseOutputStream <jv>wrapper</jv> = <jk>new</jk> NoCloseOutputStream(<jv>fos</jv>);
092    * </p>
093    *
094    * @param os The OutputStream to wrap. Must not be <jk>null</jk>.
095    */
096   public NoCloseOutputStream(OutputStream os) {
097      this.os = reqnn("os", os);
098   }
099
100   /**
101    * Flushes the stream but does not close the underlying OutputStream.
102    *
103    * <p>
104    * This method flushes any buffered data to the underlying stream but does not close it.
105    * The underlying stream remains open and can continue to be used after this method is called.
106    *
107    * <h5 class='section'>Example:</h5>
108    * <p class='bjava'>
109    *    FileOutputStream <jv>fos</jv> = <jk>new</jk> FileOutputStream(<js>"file.txt"</js>);
110    *    NoCloseOutputStream <jv>wrapper</jv> = <jk>new</jk> NoCloseOutputStream(<jv>fos</jv>);
111    *    <jv>wrapper</jv>.close();  <jc>// Flushes but doesn't close fos</jc>
112    *    <jv>fos</jv>.write(<js>"still works"</js>.getBytes());  <jc>// fos is still open</jc>
113    * </p>
114    *
115    * @throws IOException If an I/O error occurs while flushing.
116    */
117   @Override /* Overridden from OutputStream */
118   public void close() throws IOException {
119      os.flush();
120   }
121
122   @Override /* Overridden from OutputStream */
123   public void flush() throws IOException {
124      os.flush();
125   }
126
127   @Override /* Overridden from OutputStream */
128   public void write(byte[] b) throws IOException {
129      reqnn("b", b);
130      os.write(b);
131   }
132
133   @Override /* Overridden from OutputStream */
134   public void write(byte[] b, int off, int len) throws IOException {
135      reqnn("b", b);
136      os.write(b, off, len);
137   }
138
139   @Override /* Overridden from OutputStream */
140   public void write(int b) throws IOException {
141      os.write(b);
142   }
143}