/ org.apache.commons.httpclient / src / org / apache / commons / httpclient / AutoCloseInputStream.java
AutoCloseInputStream.java
  1  /*
  2   * $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 $
  3   * $Revision: 505890 $
  4   * $Date: 2007-02-11 12:25:25 +0100 (Sun, 11 Feb 2007) $
  5   *
  6   * ====================================================================
  7   *
  8   *  Licensed to the Apache Software Foundation (ASF) under one or more
  9   *  contributor license agreements.  See the NOTICE file distributed with
 10   *  this work for additional information regarding copyright ownership.
 11   *  The ASF licenses this file to You under the Apache License, Version 2.0
 12   *  (the "License"); you may not use this file except in compliance with
 13   *  the License.  You may obtain a copy of the License at
 14   *
 15   *      http://www.apache.org/licenses/LICENSE-2.0
 16   *
 17   *  Unless required by applicable law or agreed to in writing, software
 18   *  distributed under the License is distributed on an "AS IS" BASIS,
 19   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 20   *  See the License for the specific language governing permissions and
 21   *  limitations under the License.
 22   * ====================================================================
 23   *
 24   * This software consists of voluntary contributions made by many
 25   * individuals on behalf of the Apache Software Foundation.  For more
 26   * information on the Apache Software Foundation, please see
 27   * <http://www.apache.org/>.
 28   *
 29   */
 30  
 31  package org.apache.commons.httpclient;
 32  
 33  import java.io.FilterInputStream;
 34  import java.io.IOException;
 35  import java.io.InputStream;
 36  
 37  /**
 38   * Closes an underlying stream as soon as the end of the stream is reached, and
 39   * notifies a client when it has done so.
 40   *
 41   * @author Ortwin Glueck
 42   * @author Eric Johnson
 43   * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
 44   *
 45   * @since 2.0
 46   */
 47  class AutoCloseInputStream extends FilterInputStream {
 48  
 49      /** 
 50       * True if this stream is open.  Assume that the underlying stream 
 51       * is open until we get an EOF indication.
 52       */
 53      private boolean streamOpen = true;
 54  
 55      /** True if the stream closed itself. */
 56      private boolean selfClosed = false;
 57  
 58      /** 
 59       * The watcher is notified when the contents of the stream have
 60       * been  exhausted
 61       */ 
 62      private ResponseConsumedWatcher watcher = null;
 63  
 64      /**
 65       * Create a new auto closing stream for the provided connection
 66       *
 67       * @param in the input stream to read from
 68       * @param watcher   To be notified when the contents of the stream have been
 69       *  consumed.
 70       */
 71      public AutoCloseInputStream(
 72              final InputStream in, final ResponseConsumedWatcher watcher) {
 73          super(in);
 74          this.watcher = watcher;
 75      }
 76  
 77      /**
 78       * Reads the next byte of data from the input stream.
 79       *
 80       * @throws IOException when there is an error reading
 81       * @return the character read, or -1 for EOF
 82       */
 83      public int read() throws IOException {
 84          int l = -1;
 85  
 86          if (isReadAllowed()) {
 87              // underlying stream not closed, go ahead and read.
 88              l = super.read();
 89              checkClose(l);
 90          }
 91  
 92          return l;
 93      }
 94  
 95      /**
 96       * Reads up to <code>len</code> bytes of data from the stream.
 97       *
 98       * @param b a <code>byte</code> array to read data into
 99       * @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  }
203