View Javadoc

1   /*
2    * $HeadURL: https://svn.apache.org/repos/asf/httpcomponents/oac.hc3x/trunk/src/java/org/apache/commons/httpclient/AutoCloseInputStream.java $
3    * $Revision$
4    * $Date$
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