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