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