001: /*
002: * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/AutoCloseInputStream.java,v 1.9 2004/04/18 23:51:34 jsdever Exp $
003: * $Revision: 505890 $
004: * $Date: 2007-02-11 12:25:25 +0100 (Sun, 11 Feb 2007) $
005: *
006: * ====================================================================
007: *
008: * Licensed to the Apache Software Foundation (ASF) under one or more
009: * contributor license agreements. See the NOTICE file distributed with
010: * this work for additional information regarding copyright ownership.
011: * The ASF licenses this file to You under the Apache License, Version 2.0
012: * (the "License"); you may not use this file except in compliance with
013: * the License. You may obtain a copy of the License at
014: *
015: * http://www.apache.org/licenses/LICENSE-2.0
016: *
017: * Unless required by applicable law or agreed to in writing, software
018: * distributed under the License is distributed on an "AS IS" BASIS,
019: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
020: * See the License for the specific language governing permissions and
021: * limitations under the License.
022: * ====================================================================
023: *
024: * This software consists of voluntary contributions made by many
025: * individuals on behalf of the Apache Software Foundation. For more
026: * information on the Apache Software Foundation, please see
027: * <http://www.apache.org/>.
028: *
029: */
030:
031: package org.apache.commons.httpclient;
032:
033: import java.io.FilterInputStream;
034: import java.io.IOException;
035: import java.io.InputStream;
036:
037: /**
038: * Closes an underlying stream as soon as the end of the stream is reached, and
039: * notifies a client when it has done so.
040: *
041: * @author Ortwin Glueck
042: * @author Eric Johnson
043: * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
044: *
045: * @since 2.0
046: */
047: class AutoCloseInputStream extends FilterInputStream {
048:
049: /**
050: * True if this stream is open. Assume that the underlying stream
051: * is open until we get an EOF indication.
052: */
053: private boolean streamOpen = true;
054:
055: /** True if the stream closed itself. */
056: private boolean selfClosed = false;
057:
058: /**
059: * The watcher is notified when the contents of the stream have
060: * been exhausted
061: */
062: private ResponseConsumedWatcher watcher = null;
063:
064: /**
065: * Create a new auto closing stream for the provided connection
066: *
067: * @param in the input stream to read from
068: * @param watcher To be notified when the contents of the stream have been
069: * consumed.
070: */
071: public AutoCloseInputStream(final InputStream in,
072: final ResponseConsumedWatcher watcher) {
073: super (in);
074: this .watcher = watcher;
075: }
076:
077: /**
078: * Reads the next byte of data from the input stream.
079: *
080: * @throws IOException when there is an error reading
081: * @return the character read, or -1 for EOF
082: */
083: public int read() throws IOException {
084: int l = -1;
085:
086: if (isReadAllowed()) {
087: // underlying stream not closed, go ahead and read.
088: l = super .read();
089: checkClose(l);
090: }
091:
092: return l;
093: }
094:
095: /**
096: * Reads up to <code>len</code> bytes of data from the stream.
097: *
098: * @param b a <code>byte</code> array to read data into
099: * @param off an offset within the array to store data
100: * @param len the maximum number of bytes to read
101: * @return the number of bytes read or -1 for EOF
102: * @throws IOException if there are errors reading
103: */
104: public int read(byte[] b, int off, int len) throws IOException {
105: int l = -1;
106:
107: if (isReadAllowed()) {
108: l = super .read(b, off, len);
109: checkClose(l);
110: }
111:
112: return l;
113: }
114:
115: /**
116: * Reads some number of bytes from the input stream and stores them into the
117: * buffer array b.
118: *
119: * @param b a <code>byte</code> array to read data into
120: * @return the number of bytes read or -1 for EOF
121: * @throws IOException if there are errors reading
122: */
123: public int read(byte[] b) throws IOException {
124: int l = -1;
125:
126: if (isReadAllowed()) {
127: l = super .read(b);
128: checkClose(l);
129: }
130: return l;
131: }
132:
133: /**
134: * Obtains the number of bytes that can be read without blocking.
135: *
136: * @return the number of bytes available without blocking
137: * @throws IOException in case of a problem
138: */
139: public int available() throws IOException {
140: int a = 0; // not -1
141:
142: if (isReadAllowed()) {
143: a = super .available();
144: // no checkClose() here, available() can't trigger EOF
145: }
146:
147: return a;
148: }
149:
150: /**
151: * Close the stream, and also close the underlying stream if it is not
152: * already closed.
153: * @throws IOException If an IO problem occurs.
154: */
155: public void close() throws IOException {
156: if (!selfClosed) {
157: selfClosed = true;
158: notifyWatcher();
159: }
160: }
161:
162: /**
163: * Close the underlying stream should the end of the stream arrive.
164: *
165: * @param readResult The result of the read operation to check.
166: * @throws IOException If an IO problem occurs.
167: */
168: private void checkClose(int readResult) throws IOException {
169: if (readResult == -1) {
170: notifyWatcher();
171: }
172: }
173:
174: /**
175: * See whether a read of the underlying stream should be allowed, and if
176: * not, check to see whether our stream has already been closed!
177: *
178: * @return <code>true</code> if it is still OK to read from the stream.
179: * @throws IOException If an IO problem occurs.
180: */
181: private boolean isReadAllowed() throws IOException {
182: if (!streamOpen && selfClosed) {
183: throw new IOException("Attempted read on closed stream.");
184: }
185: return streamOpen;
186: }
187:
188: /**
189: * Notify the watcher that the contents have been consumed.
190: * @throws IOException If an IO problem occurs.
191: */
192: private void notifyWatcher() throws IOException {
193: if (streamOpen) {
194: super .close();
195: streamOpen = false;
196:
197: if (watcher != null) {
198: watcher.responseConsumed();
199: }
200: }
201: }
202: }
|