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}