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