    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 *
    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     * Invokes the delegate's <code>write(int)</code> method.
    049     * @param idx the byte to write
    050     * @throws IOException if an I/O error occurs
    051     */
    052    @Override
    053    public void write(final int idx) throws IOException {
    054        try {
    055            beforeWrite(1);
    056            out.write(idx);
    057            afterWrite(1);
    058        } catch (final IOException e) {
    059            handleIOException(e);
    060        }
    061    }
    062
    063    /**
    064     * Invokes the delegate's <code>write(byte[])</code> method.
    065     * @param bts the bytes to write
    066     * @throws IOException if an I/O error occurs
    067     */
    068    @Override
    069    public void write(final byte[] bts) throws IOException {
    070        try {
    071            final int len = IOUtils.length(bts);
    072            beforeWrite(len);
    073            out.write(bts);
    074            afterWrite(len);
    075        } catch (final IOException e) {
    076            handleIOException(e);
    077        }
    078    }
    079
    080    /**
    081     * Invokes the delegate's <code>write(byte[])</code> method.
    082     * @param bts the bytes to write
    083     * @param st The start offset
    084     * @param end The number of bytes to write
    085     * @throws IOException if an I/O error occurs
    086     */
    087    @Override
    088    public void write(final byte[] bts, final int st, final int end) throws IOException {
    089        try {
    090            beforeWrite(end);
    091            out.write(bts, st, end);
    092            afterWrite(end);
    093        } catch (final IOException e) {
    094            handleIOException(e);
    095        }
    096    }
    097
    098    /**
    099     * Invokes the delegate's <code>flush()</code> method.
    100     * @throws IOException if an I/O error occurs
    101     */
    102    @Override
    103    public void flush() throws IOException {
    104        try {
    105            out.flush();
    106        } catch (final IOException e) {
    107            handleIOException(e);
    108        }
    109    }
    110
    111    /**
    112     * Invokes the delegate's <code>close()</code> method.
    113     * @throws IOException if an I/O error occurs
    114     */
    115    @Override
    116    public void close() throws IOException {
    117        IOUtils.close(out, this::handleIOException);
    118    }
    119
    120    /**
    121     * Invoked by the write methods before the call is proxied. The number
    122     * of bytes to be written (1 for the {@link #write(int)} method, buffer
    123     * length for {@link #write(byte[])}, etc.) is given as an argument.
    124     * <p>
    125     * Subclasses can override this method to add common pre-processing
    126     * functionality without having to override all the write methods.
    127     * The default implementation does nothing.
    128     *
    129     * @since 2.0
    130     * @param n number of bytes to be written
    131     * @throws IOException if the pre-processing fails
    132     */
    133    protected void beforeWrite(final int n) throws IOException {
    134        // noop
    135    }
    136
    137    /**
    138     * Invoked by the write methods after the proxied call has returned
    139     * successfully. The number of bytes written (1 for the
    140     * {@link #write(int)} method, buffer length for {@link #write(byte[])},
    141     * etc.) is given as an argument.
    142     * <p>
    143     * Subclasses can override this method to add common post-processing
    144     * functionality without having to override all the write methods.
    145     * The default implementation does nothing.
    146     *
    147     * @since 2.0
    148     * @param n number of bytes written
    149     * @throws IOException if the post-processing fails
    150     */
    151    protected void afterWrite(final int n) throws IOException {
    152        // noop
    153    }
    154
    155    /**
    156     * Handle any IOExceptions thrown.
    157     * <p>
    158     * This method provides a point to implement custom exception
    159     * handling. The default behavior is to re-throw the exception.
    160     * @param e The IOException thrown
    161     * @throws IOException if an I/O error occurs
    162     * @since 2.0
    163     */
    164    protected void handleIOException(final IOException e) throws IOException {
    165        throw e;
    166    }
    167
    168}