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 019import static org.apache.juneau.commons.utils.AssertionUtils.*; 020 021import java.io.*; 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 */ 073public class NoCloseOutputStream extends OutputStream { 074 075 private final OutputStream os; 076 077 /** 078 * Constructor. 079 * 080 * <p> 081 * Creates a new NoCloseOutputStream that wraps the specified OutputStream. The wrapper 082 * will prevent the underlying stream from being closed via the {@link #close()} method. 083 * 084 * <h5 class='section'>Example:</h5> 085 * <p class='bjava'> 086 * FileOutputStream <jv>fos</jv> = <jk>new</jk> FileOutputStream(<js>"file.txt"</js>); 087 * NoCloseOutputStream <jv>wrapper</jv> = <jk>new</jk> NoCloseOutputStream(<jv>fos</jv>); 088 * </p> 089 * 090 * @param os The OutputStream to wrap. Must not be <jk>null</jk>. 091 */ 092 public NoCloseOutputStream(OutputStream os) { 093 this.os = assertArgNotNull("os", os); 094 } 095 096 /** 097 * Flushes the stream but does not close the underlying OutputStream. 098 * 099 * <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}