2 * Copyright (c) 2001-2007 Sun Microsystems, Inc. All rights reserved.
4 * The Sun Project JXTA(TM) Software License
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
9 * 1. Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
16 * 3. The end-user documentation included with the redistribution, if any, must
17 * include the following acknowledgment: "This product includes software
18 * developed by Sun Microsystems, Inc. for JXTA(TM) technology."
19 * Alternately, this acknowledgment may appear in the software itself, if
20 * and wherever such third-party acknowledgments normally appear.
22 * 4. The names "Sun", "Sun Microsystems, Inc.", "JXTA" and "Project JXTA" must
23 * not be used to endorse or promote products derived from this software
24 * without prior written permission. For written permission, please contact
25 * Project JXTA at http://www.jxta.org.
27 * 5. Products derived from this software may not be called "JXTA", nor may
28 * "JXTA" appear in their name, without prior written permission of Sun.
30 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
31 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
32 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SUN
33 * MICROSYSTEMS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
34 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
36 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
37 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
38 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
39 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 * JXTA is a registered trademark of Sun Microsystems, Inc. in the United
42 * States and other countries.
44 * Please see the license information page at :
45 * <http://www.jxta.org/project/www/license.html> for instructions on use of
46 * the license in source files.
48 * ====================================================================
50 * This software consists of voluntary contributions made by many individuals
51 * on behalf of Project JXTA. For more information on Project JXTA, please see
52 * http://www.jxta.org.
54 * This license is based on the BSD license adopted by the Apache Foundation.
57 package net.jxta.util;
60 import java.io.ByteArrayInputStream;
61 import java.io.FilterInputStream;
62 import java.io.InputStream;
64 import java.io.IOException;
66 import java.util.logging.Logger;
67 import java.util.logging.Level;
68 import net.jxta.logging.Logging;
72 * Implements a bounds on the number of bytes which may be read from an
73 * InputStream. {link LimitInputStream.close() close()} does not close the
76 public class LimitInputStream extends FilterInputStream {
81 private static final Logger LOG = Logger.getLogger(LimitInputStream.class.getName());
83 private transient long limit;
85 private transient long read;
87 private transient long mark;
89 private transient boolean fatalUnderflow;
91 private transient boolean alreadycounting;
94 * Creates a new instance of LimitInputStream
96 * @param in the stream which will be limited.
97 * @param limit the number of bytes which can be read from the stream.
99 public LimitInputStream(InputStream in, long limit) {
100 this(in, limit, false);
104 * Creates a new instance of LimitInputStream
106 * @param in the stream which will be limited.
107 * @param limit the number of bytes which can be read from the stream.
108 * @param underflowThrows if the underlying stream EOFs before limit then
109 * an IOException will be thrown.
111 public LimitInputStream(InputStream in, long limit, boolean underflowThrows) {
116 this.fatalUnderflow = underflowThrows;
117 this.alreadycounting = false;
123 * <p/>Debugging toString.
126 public String toString() {
128 return "closed/" + super.toString();
130 if (in instanceof ByteArrayInputStream) {
131 // ByteArrayInputStream.toString() prints the entire stream!
132 return in.getClass().getName() + "@" + System.identityHashCode(in) + "/" + super.toString() + ":" + read + "-"
135 return in.toString() + "/" + super.toString() + ":" + read + "-" + limit;
141 * Closes this input stream and releases any system resources
142 * associated with the stream.
144 * This method simply forgets the underlying stream.
146 * @exception IOException if an I/O error occurs.
147 * @see java.io.FilterInputStream#in
150 public void close() throws IOException {
155 * Returns the number of bytes that can be read from this input
156 * stream without blocking.
159 * simply performs <code>in.available(n)</code> and
160 * returns the result.
162 * @return the number of bytes that can be read from the input stream
164 * @exception IOException if an I/O error occurs.
165 * @see java.io.FilterInputStream#in
168 public int available() throws IOException {
170 throw new IOException("Stream has been closed.");
173 return (int) Math.min(super.available(), (limit - read));
177 * Marks the current position in this input stream. A subsequent
178 * call to the <code>reset</code> method repositions this stream at
179 * the last marked position so that subsequent reads re-read the same bytes.
181 * The <code>readlimit</code> argument tells this input stream to
182 * allow that many bytes to be read before the mark position gets
185 * This method simply performs <code>in.mark(readlimit)</code>.
187 * @param readlimit the maximum limit of bytes that can be read before
188 * the mark position becomes invalid.
189 * @see java.io.FilterInputStream#in
190 * @see java.io.FilterInputStream#reset()
193 public void mark(int readlimit) {
196 } // don't throw exception to be consistent with other impls.
198 super.mark(readlimit);
203 * Repositions this stream to the position at the time the
204 * <code>mark</code> method was last called on this input stream.
206 * This method simply performs <code>in.reset()</code>.
208 * Stream marks are intended to be used in
209 * situations where you need to read ahead a little to see what's in
210 * the stream. Often this is most easily done by invoking some
211 * general parser. If the stream is of the type handled by the
212 * parse, it just chugs along happily. If the stream is not of
213 * that type, the parser should toss an exception when it fails.
214 * If this happens within readlimit bytes, it allows the outer
215 * code to reset the stream and try another parser.
217 * @exception IOException if the stream has not been marked or if the
218 * mark has been invalidated.
219 * @see java.io.FilterInputStream#in
220 * @see java.io.FilterInputStream#mark(int)
223 public void reset() throws IOException {
225 throw new IOException("Stream has been closed.");
229 throw new IOException("reset() without mark(), or I dont know where mark is");
238 * Skips over and discards <code>n</code> bytes of data from the
239 * input stream. The <code>skip</code> method may, for a variety of
240 * reasons, end up skipping over some smaller number of bytes,
241 * possibly <code>0</code>. The actual number of bytes skipped is
245 * simply performs <code>in.skip(n)</code>.
247 * @param n the number of bytes to be skipped.
248 * @return the actual number of bytes skipped.
249 * @exception IOException if an I/O error occurs.
252 public synchronized long skip(long n) throws IOException {
254 throw new IOException("Stream has been closed.");
257 long skipLen = Math.min(n, (limit - read));
259 boolean wascounting = alreadycounting;
261 alreadycounting = true;
262 long result = super.skip(skipLen);
264 alreadycounting = wascounting;
266 if ((-1 != result) && !alreadycounting) {
274 * Reads the next byte of data from this input stream. The value
275 * byte is returned as an <code>int</code> in the range
276 * <code>0</code> to <code>255</code>. If no byte is available
277 * because the end of the stream has been reached, the value
278 * <code>-1</code> is returned. This method blocks until input data
279 * is available, the end of the stream is detected, or an exception
283 * simply performs <code>in.read()</code> and returns the result.
285 * @return the next byte of data, or <code>-1</code> if the end of the
287 * @exception IOException if an I/O error occurs.
288 * @see java.io.FilterInputStream#in
291 public synchronized int read() throws IOException {
293 throw new IOException("Stream has been closed.");
300 boolean wascounting = alreadycounting;
302 alreadycounting = true;
303 int result = super.read();
305 alreadycounting = wascounting;
307 if (!alreadycounting) {
311 if (fatalUnderflow && (read != limit)) {
312 IOException failed = new IOException(
313 "Underflow in read, stream EOFed at " + read + " before limit of " + limit);
315 if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
316 LOG.log(Level.WARNING, failed.getMessage(), failed);
327 * Reads up to <code>len</code> bytes of data from this input stream
328 * into an array of bytes. This method blocks until some input is
331 * This method simply performs <code>in.read(b, off, len)</code>
332 * and returns the result.
334 * @param b the buffer into which the data is read.
335 * @param off the start offset of the data.
336 * @param len the maximum number of bytes read.
337 * @return the total number of bytes read into the buffer, or
338 * <code>-1</code> if there is no more data because the end of
339 * the stream has been reached.
340 * @exception IOException if an I/O error occurs.
341 * @see java.io.FilterInputStream#in
344 public synchronized int read(byte[] b, int off, int len) throws IOException {
346 throw new IOException("Stream has been closed.");
353 int readLen = (int) Math.min(len, limit - read);
355 boolean wascounting = alreadycounting;
357 alreadycounting = true;
358 int result = super.read(b, off, readLen);
360 alreadycounting = wascounting;
362 if (!alreadycounting) {
366 if (fatalUnderflow && (read != limit)) {
367 IOException failed = new IOException(
368 "Underflow while tring to read " + readLen + ", stream EOFed at " + read + " before limit of " + limit);
370 if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
371 LOG.log(Level.WARNING, failed.getMessage(), failed);