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