1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 package org.apache.commons.io.output;
18 
19 import java.io.IOException;
20 import java.io.OutputStream;
21 
22 
23 /**
24  * An output stream which triggers an event when a specified number of bytes of
25  * data have been written to it. The event can be used, for example, to throw
26  * an exception if a maximum has been reached, or to switch the underlying
27  * stream type when the threshold is exceeded.
28  * <p>
29  * This class overrides all <code>OutputStream</code> methods. However, these
30  * overrides ultimately call the corresponding methods in the underlying output
31  * stream implementation.
32  * <p>
33  * NOTE: This implementation may trigger the event <em>before</em> the threshold
34  * is actually reached, since it triggers when a pending write operation would
35  * cause the threshold to be exceeded.
36  *
37  * @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
38  *
39  * @version $Id: ThresholdingOutputStream.java 540714 2007-05-22 19:39:44Z niallp $
40  */
41 public abstract class ThresholdingOutputStream
42     extends OutputStream
43 {
44 
45     // ----------------------------------------------------------- Data members
46 
47 
48     /**
49      * The threshold at which the event will be triggered.
50      */
51     private int threshold;
52 
53 
54     /**
55      * The number of bytes written to the output stream.
56      */
57     private long written;
58 
59 
60     /**
61      * Whether or not the configured threshold has been exceeded.
62      */
63     private boolean thresholdExceeded;
64 
65 
66     // ----------------------------------------------------------- Constructors
67 
68 
69     /**
70      * Constructs an instance of this class which will trigger an event at the
71      * specified threshold.
72      *
73      * @param threshold The number of bytes at which to trigger an event.
74      */
ThresholdingOutputStream(int threshold)75     public ThresholdingOutputStream(int threshold)
76     {
77         this.threshold = threshold;
78     }
79 
80 
81     // --------------------------------------------------- OutputStream methods
82 
83 
84     /**
85      * Writes the specified byte to this output stream.
86      *
87      * @param b The byte to be written.
88      *
89      * @exception IOException if an error occurs.
90      */
write(int b)91     public void write(int b) throws IOException
92     {
93         checkThreshold(1);
94         getStream().write(b);
95         written++;
96     }
97 
98 
99     /**
100      * Writes <code>b.length</code> bytes from the specified byte array to this
101      * output stream.
102      *
103      * @param b The array of bytes to be written.
104      *
105      * @exception IOException if an error occurs.
106      */
write(byte b[])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      */
write(byte b[], int off, int len)125     public void write(byte b[], int off, int len) throws IOException
126     {
127         checkThreshold(len);
128         getStream().write(b, off, len);
129         written += len;
130     }
131 
132 
133     /**
134      * Flushes this output stream and forces any buffered output bytes to be
135      * written out.
136      *
137      * @exception IOException if an error occurs.
138      */
flush()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      * @exception IOException if an error occurs.
150      */
close()151     public void close() throws IOException
152     {
153         try
154         {
155             flush();
156         }
157         catch (IOException ignored)
158         {
159             // ignore
160         }
161         getStream().close();
162     }
163 
164 
165     // --------------------------------------------------------- Public methods
166 
167 
168     /**
169      * Returns the threshold, in bytes, at which an event will be triggered.
170      *
171      * @return The threshold point, in bytes.
172      */
getThreshold()173     public int getThreshold()
174     {
175         return threshold;
176     }
177 
178 
179     /**
180      * Returns the number of bytes that have been written to this output stream.
181      *
182      * @return The number of bytes written.
183      */
getByteCount()184     public long getByteCount()
185     {
186         return written;
187     }
188 
189 
190     /**
191      * Determines whether or not the configured threshold has been exceeded for
192      * this output stream.
193      *
194      * @return <code>true</code> if the threshold has been reached;
195      *         <code>false</code> otherwise.
196      */
isThresholdExceeded()197     public boolean isThresholdExceeded()
198     {
199         return (written > threshold);
200     }
201 
202 
203     // ------------------------------------------------------ Protected methods
204 
205 
206     /**
207      * Checks to see if writing the specified number of bytes would cause the
208      * configured threshold to be exceeded. If so, triggers an event to allow
209      * a concrete implementation to take action on this.
210      *
211      * @param count The number of bytes about to be written to the underlying
212      *              output stream.
213      *
214      * @exception IOException if an error occurs.
215      */
checkThreshold(int count)216     protected void checkThreshold(int count) throws IOException
217     {
218         if (!thresholdExceeded && (written + count > threshold))
219         {
220             thresholdExceeded = true;
221             thresholdReached();
222         }
223     }
224 
225     /**
226      * Resets the byteCount to zero.  You can call this from
227      * {@link #thresholdReached()} if you want the event to be triggered again.
228      */
resetByteCount()229     protected void resetByteCount()
230     {
231         this.thresholdExceeded = false;
232         this.written = 0;
233     }
234 
235     // ------------------------------------------------------- Abstract methods
236 
237 
238     /**
239      * Returns the underlying output stream, to which the corresponding
240      * <code>OutputStream</code> methods in this class will ultimately delegate.
241      *
242      * @return The underlying output stream.
243      *
244      * @exception IOException if an error occurs.
245      */
getStream()246     protected abstract OutputStream getStream() throws IOException;
247 
248 
249     /**
250      * Indicates that the configured threshold has been reached, and that a
251      * subclass should take whatever action necessary on this event. This may
252      * include changing the underlying output stream.
253      *
254      * @exception IOException if an error occurs.
255      */
thresholdReached()256     protected abstract void thresholdReached() throws IOException;
257 }
258