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.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 @SuppressWarnings("resource") // caller closes 072 @Override 073 public ProxyOutputStream get() throws IOException { 074 return new ProxyOutputStream(getOutputStream()); 075 } 076 077 } 078 079 /** 080 * Constructs a new ProxyOutputStream. 081 * 082 * @param delegate the OutputStream to delegate to 083 */ 084 public ProxyOutputStream(final OutputStream delegate) { 085 // the delegate is stored in a protected superclass variable named 'out' 086 super(delegate); 087 } 088 089 /** 090 * Invoked by the write methods after the proxied call has returned 091 * successfully. The number of bytes written (1 for the 092 * {@link #write(int)} method, buffer length for {@link #write(byte[])}, 093 * etc.) is given as an argument. 094 * <p> 095 * Subclasses can override this method to add common post-processing 096 * functionality without having to override all the write methods. 097 * The default implementation does nothing. 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 110 * of bytes to be written (1 for the {@link #write(int)} method, buffer 111 * length for {@link #write(byte[])}, etc.) is given as an argument. 112 * <p> 113 * Subclasses can override this method to add common pre-processing 114 * functionality without having to override all the write methods. 115 * The default implementation does nothing. 116 * 117 * @param n number of bytes to be written 118 * @throws IOException if the pre-processing fails 119 * @since 2.0 120 */ 121 @SuppressWarnings("unused") // Possibly thrown from subclasses. 122 protected void beforeWrite(final int n) throws IOException { 123 // noop 124 } 125 126 /** 127 * Invokes the delegate's {@code close()} method. 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 * @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 152 * handling. The default behavior is to re-throw the exception. 153 * @param e The IOException thrown 154 * @throws IOException if an I/O error occurs. 155 * @since 2.0 156 */ 157 protected void handleIOException(final IOException e) throws IOException { 158 throw e; 159 } 160 161 /** 162 * Sets the underlying output stream. 163 * 164 * @param out the underlying output stream. 165 * @return this instance. 166 * @since 2.19.0 167 */ 168 public ProxyOutputStream setReference(final OutputStream out) { 169 this.out = out; 170 return this; 171 } 172 173 /** 174 * Unwraps this instance by returning the underlying {@link OutputStream}. 175 * <p> 176 * Use with caution; useful to query the underlying {@link OutputStream}. 177 * </p> 178 * 179 * @return the underlying {@link OutputStream}. 180 */ 181 OutputStream unwrap() { 182 return out; 183 } 184 185 /** 186 * Invokes the delegate's {@code write(byte[])} method. 187 * @param bts the bytes to write 188 * @throws IOException if an I/O error occurs. 189 */ 190 @Override 191 public void write(final byte[] bts) throws IOException { 192 try { 193 final int len = IOUtils.length(bts); 194 beforeWrite(len); 195 out.write(bts); 196 afterWrite(len); 197 } catch (final IOException e) { 198 handleIOException(e); 199 } 200 } 201 202 /** 203 * Invokes the delegate's {@code write(byte[])} method. 204 * @param bts the bytes to write 205 * @param st The start offset 206 * @param end The number of bytes to write 207 * @throws IOException if an I/O error occurs. 208 */ 209 @Override 210 public void write(final byte[] bts, final int st, final int end) throws IOException { 211 try { 212 beforeWrite(end); 213 out.write(bts, st, end); 214 afterWrite(end); 215 } catch (final IOException e) { 216 handleIOException(e); 217 } 218 } 219 220 /** 221 * Invokes the delegate's {@code write(int)} method. 222 * @param idx the byte to write 223 * @throws IOException if an I/O error occurs. 224 */ 225 @Override 226 public void write(final int idx) throws IOException { 227 try { 228 beforeWrite(1); 229 out.write(idx); 230 afterWrite(1); 231 } catch (final IOException e) { 232 handleIOException(e); 233 } 234 } 235 236}