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 * @version $Id: ThresholdingOutputStream.java 1687815 2015-06-26 17:18:56Z krosenvold $ 038 */ 039public 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(final 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(final 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(final 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(final byte b[], final int off, final 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 (final 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(final 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 /** 239 * Sets the byteCount to count. Usefull for re-opening an output stream 240 * that been previously written to. 241 * 242 * @param count The number of bytes that have already been written to the 243 * output stream 244 * 245 * @since 2.5 246 */ 247 protected void setByteCount(final long count) { 248 this.written = count; 249 } 250 251 252 // ------------------------------------------------------- Abstract methods 253 254 255 /** 256 * Returns the underlying output stream, to which the corresponding 257 * <code>OutputStream</code> methods in this class will ultimately delegate. 258 * 259 * @return The underlying output stream. 260 * 261 * @exception IOException if an error occurs. 262 */ 263 protected abstract OutputStream getStream() throws IOException; 264 265 266 /** 267 * Indicates that the configured threshold has been reached, and that a 268 * subclass should take whatever action necessary on this event. This may 269 * include changing the underlying output stream. 270 * 271 * @exception IOException if an error occurs. 272 */ 273 protected abstract void thresholdReached() throws IOException; 274}