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 */ 017package org.apache.commons.io.output; 018 019import java.io.FilterOutputStream; 020import java.io.IOException; 021import java.io.OutputStream; 022 023import org.apache.commons.io.IOUtils; 024import org.apache.commons.io.build.AbstractStreamBuilder; 025 026/** 027 * A Proxy stream which acts as expected, that is it passes the method 028 * calls on to the proxied stream and doesn't change which methods are 029 * being called. It is an alternative base class to FilterOutputStream 030 * to increase reusability. 031 * <p> 032 * See the protected methods for ways in which a subclass can easily decorate 033 * a stream with custom pre-, post- or error processing functionality. 034 * </p> 035 */ 036public class ProxyOutputStream extends FilterOutputStream { 037 038 /** 039 * Builds instances of {@link ProxyOutputStream}. 040 * <p> 041 * This class does not provide a convenience static {@code builder()} method so that subclasses can. 042 * </p> 043 * 044 * @since 2.19.0 045 */ 046 public static class Builder extends AbstractStreamBuilder<ProxyOutputStream, Builder> { 047 048 /** 049 * Constructs a new builder of {@link ProxyOutputStream}. 050 */ 051 public Builder() { 052 // empty 053 } 054 055 /** 056 * Builds a new {@link ProxyOutputStream}. 057 * <p> 058 * This builder uses the following aspects: 059 * </p> 060 * <ul> 061 * <li>{@link #getOutputStream()} is the target aspect.</li> 062 * </ul> 063 * 064 * @return a new instance. 065 * @throws IllegalStateException if the {@code origin} is {@code null}. 066 * @throws UnsupportedOperationException if the origin cannot be converted to an {@link OutputStream}. 067 * @throws IOException if an I/O error occurs converting to an {@link OutputStream} using {@link #getOutputStream()}. 068 * @see #getOutputStream() 069 * @see #getUnchecked() 070 */ 071 @Override 072 public ProxyOutputStream get() throws IOException { 073 return new ProxyOutputStream(this); 074 } 075 076 } 077 078 @SuppressWarnings("resource") // caller closes 079 ProxyOutputStream(final Builder builder) throws IOException { 080 // the delegate is stored in a protected superclass variable named 'out' 081 super(builder.getOutputStream()); 082 } 083 084 /** 085 * Constructs a new ProxyOutputStream. 086 * 087 * @param delegate the OutputStream to delegate to 088 */ 089 public ProxyOutputStream(final OutputStream delegate) { 090 // the delegate is stored in a protected superclass variable named 'out' 091 super(delegate); 092 } 093 094 /** 095 * Invoked by the write methods after the proxied call has returned 096 * successfully. The number of bytes written (1 for the 097 * {@link #write(int)} method, buffer length for {@link #write(byte[])}, 098 * etc.) is given as an argument. 099 * <p> 100 * Subclasses can override this method to add common post-processing 101 * functionality without having to override all the write methods. 102 * The default implementation does nothing. 103 * 104 * @param n number of bytes written 105 * @throws IOException if the post-processing fails 106 * @since 2.0 107 */ 108 @SuppressWarnings("unused") // Possibly thrown from subclasses. 109 protected void afterWrite(final int n) throws IOException { 110 // noop 111 } 112 113 /** 114 * Invoked by the write methods before the call is proxied. The number 115 * of bytes to be written (1 for the {@link #write(int)} method, buffer 116 * length for {@link #write(byte[])}, etc.) is given as an argument. 117 * <p> 118 * Subclasses can override this method to add common pre-processing 119 * functionality without having to override all the write methods. 120 * The default implementation does nothing. 121 * 122 * @param n number of bytes to be written 123 * @throws IOException if the pre-processing fails 124 * @since 2.0 125 */ 126 @SuppressWarnings("unused") // Possibly thrown from subclasses. 127 protected void beforeWrite(final int n) throws IOException { 128 // noop 129 } 130 131 /** 132 * Invokes the delegate's {@code close()} method. 133 * @throws IOException if an I/O error occurs. 134 */ 135 @Override 136 public void close() throws IOException { 137 IOUtils.close(out, this::handleIOException); 138 } 139 140 /** 141 * Invokes the delegate's {@code flush()} method. 142 * @throws IOException if an I/O error occurs. 143 */ 144 @Override 145 public void flush() throws IOException { 146 try { 147 out.flush(); 148 } catch (final IOException e) { 149 handleIOException(e); 150 } 151 } 152 153 /** 154 * Handle any IOExceptions thrown. 155 * <p> 156 * This method provides a point to implement custom exception 157 * handling. The default behavior is to re-throw the exception. 158 * @param e The IOException thrown 159 * @throws IOException if an I/O error occurs. 160 * @since 2.0 161 */ 162 protected void handleIOException(final IOException e) throws IOException { 163 throw e; 164 } 165 166 /** 167 * Sets the underlying output stream. 168 * 169 * @param out the underlying output stream. 170 * @return this instance. 171 * @since 2.19.0 172 */ 173 public ProxyOutputStream setReference(final OutputStream out) { 174 this.out = out; 175 return this; 176 } 177 178 /** 179 * Unwraps this instance by returning the underlying {@link OutputStream}. 180 * <p> 181 * Use with caution; useful to query the underlying {@link OutputStream}. 182 * </p> 183 * 184 * @return the underlying {@link OutputStream}. 185 */ 186 OutputStream unwrap() { 187 return out; 188 } 189 190 /** 191 * Invokes the delegate's {@code write(byte[])} method. 192 * @param bts the bytes to write 193 * @throws IOException if an I/O error occurs. 194 */ 195 @Override 196 public void write(final byte[] bts) throws IOException { 197 try { 198 final int len = IOUtils.length(bts); 199 beforeWrite(len); 200 out.write(bts); 201 afterWrite(len); 202 } catch (final IOException e) { 203 handleIOException(e); 204 } 205 } 206 207 /** 208 * Invokes the delegate's {@code write(byte[])} method. 209 * @param bts the bytes to write 210 * @param st The start offset 211 * @param end The number of bytes to write 212 * @throws IOException if an I/O error occurs. 213 */ 214 @Override 215 public void write(final byte[] bts, final int st, final int end) throws IOException { 216 try { 217 beforeWrite(end); 218 out.write(bts, st, end); 219 afterWrite(end); 220 } catch (final IOException e) { 221 handleIOException(e); 222 } 223 } 224 225 /** 226 * Invokes the delegate's {@code write(int)} method. 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}