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 * https://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 */ 017 018package org.apache.commons.io.output; 019 020import java.io.FilterOutputStream; 021import java.io.IOException; 022import java.io.OutputStream; 023 024import org.apache.commons.io.IOUtils; 025import org.apache.commons.io.build.AbstractStreamBuilder; 026 027/** 028 * An output stream proxy which delegates to the wrapped output stream. 029 * <p> 030 * See the protected methods for ways in which a subclass can easily decorate a stream with custom pre-, post- or error processing functionality. 031 * </p> 032 */ 033public class ProxyOutputStream extends FilterOutputStream { 034 035 /** 036 * Builds instances of {@link ProxyOutputStream}. 037 * <p> 038 * This class does not provide a convenience static {@code builder()} method so that subclasses can. 039 * </p> 040 * 041 * @since 2.19.0 042 */ 043 public static class Builder extends AbstractStreamBuilder<ProxyOutputStream, Builder> { 044 045 /** 046 * Constructs a new builder of {@link ProxyOutputStream}. 047 */ 048 public Builder() { 049 // empty 050 } 051 052 /** 053 * Builds a new {@link ProxyOutputStream}. 054 * <p> 055 * This builder uses the following aspects: 056 * </p> 057 * <ul> 058 * <li>{@link #getOutputStream()} is the target aspect.</li> 059 * </ul> 060 * 061 * @return a new instance. 062 * @throws IllegalStateException if the {@code origin} is {@code null}. 063 * @throws UnsupportedOperationException if the origin cannot be converted to an {@link OutputStream}. 064 * @throws IOException if an I/O error occurs converting to an {@link OutputStream} using {@link #getOutputStream()}. 065 * @see #getOutputStream() 066 * @see #getUnchecked() 067 */ 068 @Override 069 public ProxyOutputStream get() throws IOException { 070 return new ProxyOutputStream(this); 071 } 072 } 073 074 @SuppressWarnings("resource") // caller closes 075 ProxyOutputStream(final Builder builder) throws IOException { 076 // the delegate is stored in a protected superclass variable named 'out' 077 super(builder.getOutputStream()); 078 } 079 080 /** 081 * Constructs a new ProxyOutputStream. 082 * 083 * @param delegate the OutputStream to delegate to. 084 */ 085 public ProxyOutputStream(final OutputStream delegate) { 086 // the delegate is stored in a protected superclass variable named 'out' 087 super(delegate); 088 } 089 090 /** 091 * Invoked by the write methods after the proxied call has returned successfully. The number of bytes written (1 for the {@link #write(int)} method, buffer 092 * length for {@link #write(byte[])}, etc.) is given as an argument. 093 * <p> 094 * Subclasses can override this method to add common post-processing functionality without having to override all the write methods. The default 095 * implementation does nothing. 096 * </p> 097 * 098 * @param n number of bytes written. 099 * @throws IOException if the post-processing fails. 100 * @since 2.0 101 */ 102 @SuppressWarnings("unused") // Possibly thrown from subclasses. 103 protected void afterWrite(final int n) throws IOException { 104 // noop 105 } 106 107 /** 108 * Invoked by the write methods before the call is proxied. The number of bytes to be written (1 for the {@link #write(int)} method, buffer length for 109 * {@link #write(byte[])}, etc.) is given as an argument. 110 * <p> 111 * Subclasses can override this method to add common pre-processing functionality without having to override all the write methods. The default 112 * implementation does nothing. 113 * </p> 114 * 115 * @param n number of bytes to be written. 116 * @throws IOException if the pre-processing fails. 117 * @since 2.0 118 */ 119 @SuppressWarnings("unused") // Possibly thrown from subclasses. 120 protected void beforeWrite(final int n) throws IOException { 121 // noop 122 } 123 124 /** 125 * Invokes the delegate's {@code close()} method. 126 * 127 * @throws IOException if an I/O error occurs. 128 */ 129 @Override 130 public void close() throws IOException { 131 IOUtils.close(out, this::handleIOException); 132 } 133 134 /** 135 * Invokes the delegate's {@code flush()} method. 136 * 137 * @throws IOException if an I/O error occurs. 138 */ 139 @Override 140 public void flush() throws IOException { 141 try { 142 out.flush(); 143 } catch (final IOException e) { 144 handleIOException(e); 145 } 146 } 147 148 /** 149 * Handle any IOExceptions thrown. 150 * <p> 151 * This method provides a point to implement custom exception. handling. The default behavior is to re-throw the exception. 152 * </p> 153 * 154 * @param e The IOException thrown. 155 * @throws IOException if an I/O error occurs. 156 * @since 2.0 157 */ 158 protected void handleIOException(final IOException e) throws IOException { 159 throw e; 160 } 161 162 /** 163 * Sets the underlying output stream. 164 * <p> 165 * Use with caution. 166 * </p> 167 * 168 * @param out the underlying output stream. 169 * @return {@code this} instance. 170 * @since 2.19.0 171 */ 172 public ProxyOutputStream setReference(final OutputStream out) { 173 this.out = out; 174 return this; 175 } 176 177 /** 178 * Unwraps this instance by returning the underlying {@link OutputStream}. 179 * <p> 180 * Use with caution. 181 * </p> 182 * 183 * @return the underlying {@link OutputStream}. 184 * @since 2.22.0 185 */ 186 public OutputStream unwrap() { 187 return out; 188 } 189 190 /** 191 * Invokes the delegate's {@code write(byte[])} method. 192 * 193 * @param b the bytes to write. 194 * @throws IOException if an I/O error occurs. 195 */ 196 @Override 197 public void write(final byte[] b) throws IOException { 198 try { 199 final int len = IOUtils.length(b); 200 beforeWrite(len); 201 out.write(b); 202 afterWrite(len); 203 } catch (final IOException e) { 204 handleIOException(e); 205 } 206 } 207 208 /** 209 * Invokes the delegate's {@code write(byte[])} method. 210 * 211 * @param b the bytes to write. 212 * @param off The start offset. 213 * @param len The number of bytes to write. 214 * @throws IOException if an I/O error occurs. 215 */ 216 @Override 217 public void write(final byte[] b, final int off, final int len) throws IOException { 218 try { 219 beforeWrite(len); 220 out.write(b, off, len); 221 afterWrite(len); 222 } catch (final IOException e) { 223 handleIOException(e); 224 } 225 } 226 227 /** 228 * Invokes the delegate's {@code write(int)} method. 229 * 230 * @param b the byte to write. 231 * @throws IOException if an I/O error occurs. 232 */ 233 @Override 234 public void write(final int b) throws IOException { 235 try { 236 beforeWrite(1); 237 out.write(b); 238 afterWrite(1); 239 } catch (final IOException e) { 240 handleIOException(e); 241 } 242 } 243 244 /** 245 * Invokes the delegate's {@code write(byte[])} method for the {@code repeat} count. 246 * 247 * @param b the bytes to write. 248 * @param off The start offset. 249 * @param len The number of bytes to write. 250 * @param repeat How many times to write the bytes in {@code b}. 251 * @throws IOException if an I/O error occurs. 252 * @since 2.21.0 253 */ 254 public void writeRepeat(final byte[] b, final int off, final int len, final long repeat) throws IOException { 255 long remains = repeat; 256 while (remains-- > 0) { 257 write(b, off, len); 258 } 259 } 260 261 /** 262 * Invokes the delegate's {@code write(byte[])} method for the {@code repeat} count. 263 * 264 * @param b the bytes to write. 265 * @param repeat How many times to write the bytes in {@code b}. 266 * @throws IOException if an I/O error occurs. 267 * @since 2.21.0 268 */ 269 public void writeRepeat(final byte[] b, final long repeat) throws IOException { 270 long remains = repeat; 271 while (remains-- > 0) { 272 write(b); 273 } 274 } 275 276 /** 277 * Invokes the delegate's {@code write(int)} method. 278 * 279 * @param b the byte to write. 280 * @param repeat How many times to write the byte in {@code b}. 281 * @throws IOException if an I/O error occurs. 282 * @since 2.21.0 283 */ 284 public void writeRepeat(final int b, final long repeat) throws IOException { 285 long remains = repeat; 286 while (remains-- > 0) { 287 write(b); 288 } 289 } 290}