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     * @version $Id: ThresholdingOutputStream.java 1307462 2012-03-30 15:13:11Z ggregory $
038     */
039    public 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(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(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(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(byte b[], int off, 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 (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(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        // ------------------------------------------------------- Abstract methods
239    
240    
241        /**
242         * Returns the underlying output stream, to which the corresponding
243         * <code>OutputStream</code> methods in this class will ultimately delegate.
244         *
245         * @return The underlying output stream.
246         *
247         * @exception IOException if an error occurs.
248         */
249        protected abstract OutputStream getStream() throws IOException;
250    
251    
252        /**
253         * Indicates that the configured threshold has been reached, and that a
254         * subclass should take whatever action necessary on this event. This may
255         * include changing the underlying output stream.
256         *
257         * @exception IOException if an error occurs.
258         */
259        protected abstract void thresholdReached() throws IOException;
260    }