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 a {@link Writer} that prevents the underlying writer from being closed. 025 * 026 * <p> 027 * This class wraps an existing {@link Writer} and intercepts the {@link #close()} method, 028 * making it a no-op (except for flushing). All other operations are delegated to the underlying 029 * writer. This is useful when you need to pass a writer to code that will close it, but you 030 * want to keep the underlying writer 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 writer 035 * <li>Transparent delegation - all other operations pass through to the wrapped writer 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 writers to APIs that close them, but you need to keep the writer open 042 * <li>Multiple operations on the same writer where intermediate operations might close it 043 * <li>Resource management scenarios where you control the writer lifecycle 044 * <li>Wrapping system writers that should not be closed 045 * </ul> 046 * 047 * <h5 class='section'>Usage:</h5> 048 * <p class='bjava'> 049 * <jc>// Wrap a writer that should not be closed</jc> 050 * FileWriter <jv>fw</jv> = <jk>new</jk> FileWriter(<js>"output.txt"</js>); 051 * NoCloseWriter <jv>wrapper</jv> = <jk>new</jk> NoCloseWriter(<jv>fw</jv>); 052 * 053 * <jc>// Pass to code that might close it</jc> 054 * <jv>someMethod</jv>(<jv>wrapper</jv>); <jc>// May call close(), but fw remains open</jc> 055 * 056 * <jc>// Continue using the original writer</jc> 057 * <jv>fw</jv>.write(<js>"more data"</js>); 058 * <jv>fw</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 writer but does not close the underlying writer 064 * <li>You are responsible for closing the underlying writer when you're done with it 065 * <li>This wrapper does not prevent resource leaks - ensure the underlying writer is eventually closed 066 * </ul> 067 * 068 * <h5 class='section'>See Also:</h5><ul> 069 * <li class='jc'>{@link NoCloseOutputStream} - OutputStream 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 NoCloseWriter extends Writer { 074 075 private final Writer w; 076 077 /** 078 * Constructor. 079 * 080 * <p> 081 * Creates a new NoCloseWriter that wraps the specified Writer. The wrapper will prevent 082 * the underlying writer from being closed via the {@link #close()} method. 083 * 084 * <h5 class='section'>Example:</h5> 085 * <p class='bjava'> 086 * FileWriter <jv>fw</jv> = <jk>new</jk> FileWriter(<js>"file.txt"</js>); 087 * NoCloseWriter <jv>wrapper</jv> = <jk>new</jk> NoCloseWriter(<jv>fw</jv>); 088 * </p> 089 * 090 * @param w The Writer to wrap. Must not be <jk>null</jk>. 091 */ 092 public NoCloseWriter(Writer w) { 093 this.w = assertArgNotNull("w", w); 094 } 095 096 @Override /* Overridden from Writer */ 097 public Writer append(char c) throws IOException { 098 return w.append(c); 099 } 100 101 @Override /* Overridden from Writer */ 102 public Writer append(CharSequence csq) throws IOException { 103 return w.append(csq); 104 } 105 106 @Override /* Overridden from Writer */ 107 public Writer append(CharSequence csq, int start, int end) throws IOException { 108 return w.append(csq, start, end); 109 } 110 111 /** 112 * Flushes the writer but does not close the underlying Writer. 113 * 114 * <p> 115 * This method flushes any buffered data to the underlying writer but does not close it. 116 * The underlying writer remains open and can continue to be used after this method is called. 117 * 118 * <h5 class='section'>Example:</h5> 119 * <p class='bjava'> 120 * FileWriter <jv>fw</jv> = <jk>new</jk> FileWriter(<js>"file.txt"</js>); 121 * NoCloseWriter <jv>wrapper</jv> = <jk>new</jk> NoCloseWriter(<jv>fw</jv>); 122 * <jv>wrapper</jv>.close(); <jc>// Flushes but doesn't close fw</jc> 123 * <jv>fw</jv>.write(<js>"still works"</js>); <jc>// fw is still open</jc> 124 * </p> 125 * 126 * @throws IOException If an I/O error occurs while flushing. 127 */ 128 @Override /* Overridden from Writer */ 129 public void close() throws IOException { 130 w.flush(); 131 } 132 133 @Override /* Overridden from Writer */ 134 public void flush() throws IOException { 135 w.flush(); 136 } 137 138 @Override /* Overridden from Object */ 139 public String toString() { 140 return w.toString(); 141 } 142 143 @Override /* Overridden from Writer */ 144 public void write(char[] cbuf) throws IOException { 145 assertArgNotNull("cbuf", cbuf); 146 w.write(cbuf); 147 } 148 149 @Override /* Overridden from Writer */ 150 public void write(char[] cbuf, int off, int len) throws IOException { 151 assertArgNotNull("cbuf", cbuf); 152 w.write(cbuf, off, len); 153 } 154 155 @Override /* Overridden from Writer */ 156 public void write(int c) throws IOException { 157 w.write(c); 158 } 159 160 @Override /* Overridden from Writer */ 161 public void write(String str) throws IOException { 162 assertArgNotNull("str", str); 163 w.write(str); 164 } 165 166 @Override /* Overridden from Writer */ 167 public void write(String str, int off, int len) throws IOException { 168 assertArgNotNull("str", str); 169 w.write(str, off, len); 170 } 171}