    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.IOException;
    020import java.io.OutputStream;
    021
    022
    023/**
    024 * An output stream which triggers an event when a specified number of bytes of
    025 * data have been written to it. The event can be used, for example, to throw
    026 * an exception if a maximum has been reached, or to switch the underlying
    027 * stream type when the threshold is exceeded.
    028 * <p>
    029 * This class overrides all <code>OutputStream</code> methods. However, these
    030 * overrides ultimately call the corresponding methods in the underlying output
    031 * stream implementation.
    032 * <p>
    033 * NOTE: This implementation may trigger the event <em>before</em> the threshold
    034 * is actually reached, since it triggers when a pending write operation would
    035 * cause the threshold to be exceeded.
    036 */
    037public abstract class ThresholdingOutputStream
    038    extends OutputStream
    039{
    040
    041    // ----------------------------------------------------------- Data members
    042
    043
    044    /**
    045     * The threshold at which the event will be triggered.
    046     */
    047    private final int threshold;
    048
    049
    050    /**
    051     * The number of bytes written to the output stream.
    052     */
    053    private long written;
    054
    055
    056    /**
    057     * Whether or not the configured threshold has been exceeded.
    058     */
    059    private boolean thresholdExceeded;
    060
    061
    062    // ----------------------------------------------------------- Constructors
    063
    064
    065    /**
    066     * Constructs an instance of this class which will trigger an event at the
    067     * specified threshold.
    068     *
    069     * @param threshold The number of bytes at which to trigger an event.
    070     */
    071    public ThresholdingOutputStream(final int threshold)
    072    {
    073        this.threshold = threshold;
    074    }
    075
    076
    077    // --------------------------------------------------- OutputStream methods
    078
    079
    080    /**
    081     * Writes the specified byte to this output stream.
    082     *
    083     * @param b The byte to be written.
    084     *
    085     * @throws IOException if an error occurs.
    086     */
    087    @Override
    088    public void write(final int b) throws IOException
    089    {
    090        checkThreshold(1);
    091        getStream().write(b);
    092        written++;
    093    }
    094
    095
    096    /**
    097     * Writes <code>b.length</code> bytes from the specified byte array to this
    098     * output stream.
    099     *
    100     * @param b The array of bytes to be written.
    101     *
    102     * @throws IOException if an error occurs.
    103     */
    104    @Override
    105    public void write(final byte[] b) throws IOException
    106    {
    107        checkThreshold(b.length);
    108        getStream().write(b);
    109        written += b.length;
    110    }
    111
    112
    113    /**
    114     * Writes <code>len</code> bytes from the specified byte array starting at
    115     * offset <code>off</code> to this output stream.
    116     *
    117     * @param b   The byte array from which the data will be written.
    118     * @param off The start offset in the byte array.
    119     * @param len The number of bytes to write.
    120     *
    121     * @throws IOException if an error occurs.
    122     */
    123    @Override
    124    public void write(final byte[] b, final int off, final int len) throws IOException
    125    {
    126        checkThreshold(len);
    127        getStream().write(b, off, len);
    128        written += len;
    129    }
    130
    131
    132    /**
    133     * Flushes this output stream and forces any buffered output bytes to be
    134     * written out.
    135     *
    136     * @throws IOException if an error occurs.
    137     */
    138    @Override
    139    public void flush() throws IOException
    140    {
    141        getStream().flush();
    142    }
    143
    144
    145    /**
    146     * Closes this output stream and releases any system resources associated
    147     * with this stream.
    148     *
    149     * @throws IOException if an error occurs.
    150     */
    151    @Override
    152    public void close() throws IOException
    153    {
    154        try
    155        {
    156            flush();
    157        }
    158        catch (final IOException ignored)
    159        {
    160            // ignore
    161        }
    162        getStream().close();
    163    }
    164
    165
    166    // --------------------------------------------------------- Public methods
    167
    168
    169    /**
    170     * Returns the threshold, in bytes, at which an event will be triggered.
    171     *
    172     * @return The threshold point, in bytes.
    173     */
    174    public int getThreshold()
    175    {
    176        return threshold;
    177    }
    178
    179
    180    /**
    181     * Returns the number of bytes that have been written to this output stream.
    182     *
    183     * @return The number of bytes written.
    184     */
    185    public long getByteCount()
    186    {
    187        return written;
    188    }
    189
    190
    191    /**
    192     * Determines whether or not the configured threshold has been exceeded for
    193     * this output stream.
    194     *
    195     * @return {@code true} if the threshold has been reached;
    196     *         {@code false} otherwise.
    197     */
    198    public boolean isThresholdExceeded()
    199    {
    200        return written > threshold;
    201    }
    202
    203
    204    // ------------------------------------------------------ Protected methods
    205
    206
    207    /**
    208     * Checks to see if writing the specified number of bytes would cause the
    209     * configured threshold to be exceeded. If so, triggers an event to allow
    210     * a concrete implementation to take action on this.
    211     *
    212     * @param count The number of bytes about to be written to the underlying
    213     *              output stream.
    214     *
    215     * @throws IOException if an error occurs.
    216     */
    217    protected void checkThreshold(final int count) throws IOException
    218    {
    219        if (!thresholdExceeded && written + count > threshold)
    220        {
    221            thresholdExceeded = true;
    222            thresholdReached();
    223        }
    224    }
    225
    226    /**
    227     * Resets the byteCount to zero.  You can call this from
    228     * {@link #thresholdReached()} if you want the event to be triggered again.
    229     */
    230    protected void resetByteCount()
    231    {
    232        this.thresholdExceeded = false;
    233        this.written = 0;
    234    }
    235
    236    /**
    237     * Sets the byteCount to count.  Useful for re-opening an output stream
    238     * that has previously been written to.
    239     *
    240     * @param count The number of bytes that have already been written to the
    241     * output stream
    242     *
    243     * @since 2.5
    244     */
    245    protected void setByteCount(final long count) {
    246        this.written = count;
    247    }
    248
    249
    250    // ------------------------------------------------------- Abstract methods
    251
    252
    253    /**
    254     * Returns the underlying output stream, to which the corresponding
    255     * <code>OutputStream</code> methods in this class will ultimately delegate.
    256     *
    257     * @return The underlying output stream.
    258     *
    259     * @throws IOException if an error occurs.
    260     */
    261    protected abstract OutputStream getStream() throws IOException;
    262
    263
    264    /**
    265     * Indicates that the configured threshold has been reached, and that a
    266     * subclass should take whatever action necessary on this event. This may
    267     * include changing the underlying output stream.
    268     *
    269     * @throws IOException if an error occurs.
    270     */
    271    protected abstract void thresholdReached() throws IOException;
    272}