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