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    }