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 }