    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;
    024
    025/**
    026 * A Proxy stream which acts as expected, that is it passes the method
    027 * calls on to the proxied stream and doesn't change which methods are
    028 * being called. It is an alternative base class to FilterOutputStream
    029 * to increase reusability.
    030 * <p>
    031 * See the protected methods for ways in which a subclass can easily decorate
    032 * a stream with custom pre-, post- or error processing functionality.
    033 * </p>
    034 */
    035public class ProxyOutputStream extends FilterOutputStream {
    036
    037    /**
    038     * Constructs a new ProxyOutputStream.
    039     *
    040     * @param proxy  the OutputStream to delegate to
    041     */
    042    public ProxyOutputStream(final OutputStream proxy) {
    043        super(proxy);
    044        // the proxy is stored in a protected superclass variable named 'out'
    045    }
    046
    047    /**
    048     * Invoked by the write methods after the proxied call has returned
    049     * successfully. The number of bytes written (1 for the
    050     * {@link #write(int)} method, buffer length for {@link #write(byte[])},
    051     * etc.) is given as an argument.
    052     * <p>
    053     * Subclasses can override this method to add common post-processing
    054     * functionality without having to override all the write methods.
    055     * The default implementation does nothing.
    056     *
    057     * @since 2.0
    058     * @param n number of bytes written
    059     * @throws IOException if the post-processing fails
    060     */
    061    @SuppressWarnings("unused") // Possibly thrown from subclasses.
    062    protected void afterWrite(final int n) throws IOException {
    063        // noop
    064    }
    065
    066    /**
    067     * Invoked by the write methods before the call is proxied. The number
    068     * of bytes to be written (1 for the {@link #write(int)} method, buffer
    069     * length for {@link #write(byte[])}, etc.) is given as an argument.
    070     * <p>
    071     * Subclasses can override this method to add common pre-processing
    072     * functionality without having to override all the write methods.
    073     * The default implementation does nothing.
    074     *
    075     * @since 2.0
    076     * @param n number of bytes to be written
    077     * @throws IOException if the pre-processing fails
    078     */
    079    @SuppressWarnings("unused") // Possibly thrown from subclasses.
    080    protected void beforeWrite(final int n) throws IOException {
    081        // noop
    082    }
    083
    084    /**
    085     * Invokes the delegate's {@code close()} method.
    086     * @throws IOException if an I/O error occurs.
    087     */
    088    @Override
    089    public void close() throws IOException {
    090        IOUtils.close(out, this::handleIOException);
    091    }
    092
    093    /**
    094     * Invokes the delegate's {@code flush()} method.
    095     * @throws IOException if an I/O error occurs.
    096     */
    097    @Override
    098    public void flush() throws IOException {
    099        try {
    100            out.flush();
    101        } catch (final IOException e) {
    102            handleIOException(e);
    103        }
    104    }
    105
    106    /**
    107     * Handle any IOExceptions thrown.
    108     * <p>
    109     * This method provides a point to implement custom exception
    110     * handling. The default behavior is to re-throw the exception.
    111     * @param e The IOException thrown
    112     * @throws IOException if an I/O error occurs.
    113     * @since 2.0
    114     */
    115    protected void handleIOException(final IOException e) throws IOException {
    116        throw e;
    117    }
    118
    119    /**
    120     * Invokes the delegate's {@code write(byte[])} method.
    121     * @param bts the bytes to write
    122     * @throws IOException if an I/O error occurs.
    123     */
    124    @Override
    125    public void write(final byte[] bts) throws IOException {
    126        try {
    127            final int len = IOUtils.length(bts);
    128            beforeWrite(len);
    129            out.write(bts);
    130            afterWrite(len);
    131        } catch (final IOException e) {
    132            handleIOException(e);
    133        }
    134    }
    135
    136    /**
    137     * Invokes the delegate's {@code write(byte[])} method.
    138     * @param bts the bytes to write
    139     * @param st The start offset
    140     * @param end The number of bytes to write
    141     * @throws IOException if an I/O error occurs.
    142     */
    143    @Override
    144    public void write(final byte[] bts, final int st, final int end) throws IOException {
    145        try {
    146            beforeWrite(end);
    147            out.write(bts, st, end);
    148            afterWrite(end);
    149        } catch (final IOException e) {
    150            handleIOException(e);
    151        }
    152    }
    153
    154    /**
    155     * Invokes the delegate's {@code write(int)} method.
    156     * @param idx the byte to write
    157     * @throws IOException if an I/O error occurs.
    158     */
    159    @Override
    160    public void write(final int idx) throws IOException {
    161        try {
    162            beforeWrite(1);
    163            out.write(idx);
    164            afterWrite(1);
    165        } catch (final IOException e) {
    166            handleIOException(e);
    167        }
    168    }
    169
    170}