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