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.wicket.util.io;
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 data have been
025 * written to it. The event can be used, for example, to throw an exception if a maximum has been
026 * reached, or to switch the underlying stream type when the threshold is exceeded.
027 * <p>
028 * This class overrides all <code>OutputStream</code> methods. However, these overrides ultimately
029 * call the corresponding methods in the underlying output stream implementation.
030 * <p>
031 * NOTE: This implementation may trigger the event <em>before</em> the threshold is actually
032 * reached, since it triggers when a pending write operation would cause the threshold to be
033 * exceeded.
034 * 
035 * @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
036 * @version $Id$
037 */
038public abstract class ThresholdingOutputStream 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 specified threshold.
067         * 
068         * @param threshold
069         *            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
084         *            The byte to be written.
085         * @exception IOException
086         *                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 output stream.
099         * 
100         * @param b
101         *            The array of bytes to be written.
102         * @exception IOException
103         *                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 offset
116         * <code>off</code> to this output stream.
117         * 
118         * @param b
119         *            The byte array from which the data will be written.
120         * @param off
121         *            The start offset in the byte array.
122         * @param len
123         *            The number of bytes to write.
124         * @exception IOException
125         *                if an error occurs.
126         */
127        @Override
128        public void write(final byte b[], final int off, final int len) throws IOException
129        {
130                checkThreshold(len);
131                getStream().write(b, off, len);
132                written += len;
133        }
134
135
136        /**
137         * Flushes this output stream and forces any buffered output bytes to be written out.
138         * 
139         * @exception IOException
140         *                if an error occurs.
141         */
142        @Override
143        public void flush() throws IOException
144        {
145                getStream().flush();
146        }
147
148
149        /**
150         * Closes this output stream and releases any system resources associated with this stream.
151         * 
152         * @exception IOException
153         *                if an error occurs.
154         */
155        @Override
156        public void close() throws IOException
157        {
158                try
159                {
160                        flush();
161                }
162                catch (IOException ignored)
163                {
164                        // ignore
165                }
166                getStream().close();
167        }
168
169
170        // --------------------------------------------------------- Public methods
171
172
173        /**
174         * Returns the threshold, in bytes, at which an event will be triggered.
175         * 
176         * @return The threshold point, in bytes.
177         */
178        public int getThreshold()
179        {
180                return threshold;
181        }
182
183
184        /**
185         * Returns the number of bytes that have been written to this output stream.
186         * 
187         * @return The number of bytes written.
188         */
189        public long getByteCount()
190        {
191                return written;
192        }
193
194
195        /**
196         * Determines whether or not the configured threshold has been exceeded for this output stream.
197         * 
198         * @return <code>true</code> if the threshold has been reached; <code>false</code> 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 configured threshold
211         * to be exceeded. If so, triggers an event to allow a concrete implementation to take action on
212         * this.
213         * 
214         * @param count
215         *            The number of bytes about to be written to the underlying output stream.
216         * @exception IOException
217         *                if an error occurs.
218         */
219        protected void checkThreshold(final int count) throws IOException
220        {
221                if (!thresholdExceeded && (written + count > threshold))
222                {
223                        thresholdReached();
224                        thresholdExceeded = true;
225                }
226        }
227
228
229        // ------------------------------------------------------- Abstract methods
230
231
232        /**
233         * Returns the underlying output stream, to which the corresponding <code>OutputStream</code>
234         * methods in this class will ultimately delegate.
235         * 
236         * @return The underlying output stream.
237         * @exception IOException
238         *                if an error occurs.
239         */
240        protected abstract OutputStream getStream() throws IOException;
241
242
243        /**
244         * Indicates that the configured threshold has been reached, and that a subclass should take
245         * whatever action necessary on this event. This may include changing the underlying output
246         * stream.
247         * 
248         * @exception IOException
249         *                if an error occurs.
250         */
251        protected abstract void thresholdReached() throws IOException;
252}