1 /*
2  * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/EofSensorInputStream.java $
3  * $Revision: 672367 $
4  * $Date: 2008-06-27 12:49:20 -0700 (Fri, 27 Jun 2008) $
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.http.conn;
32 
33 import java.io.InputStream;
34 import java.io.IOException;
35 
36 
37 /**
38  * A stream wrapper that triggers actions on {@link #close close()} and EOF.
39  * Primarily used to auto-release an underlying
40  * {@link ManagedClientConnection connection}
41  * when the response body is consumed or no longer needed.
42  *
43  * <p>
44  * This class is based on <code>AutoCloseInputStream</code> in HttpClient 3.1,
45  * but has notable differences. It does not allow mark/reset, distinguishes
46  * different kinds of event, and does not always close the underlying stream
47  * on EOF. That decision is left to the {@link EofSensorWatcher watcher}.
48  * </p>
49  *
50  * @see EofSensorWatcher EofSensorWatcher
51  *
52  * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
53  * @author Ortwin Glueck
54  * @author Eric Johnson
55  * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
56  *
57  *
58  * <!-- empty lines to avoid svn diff problems -->
59  * @version $Revision: 672367 $
60  *
61  * @since 4.0
62  *
63  * @deprecated Please use {@link java.net.URL#openConnection} instead.
64  *     Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
65  *     for further details.
66  */
67 @Deprecated
68 // don't use FilterInputStream as the base class, we'd have to
69 // override markSupported(), mark(), and reset() to disable them
70 public class EofSensorInputStream extends InputStream
71     implements ConnectionReleaseTrigger {
72 
73     /**
74      * The wrapped input stream, while accessible.
75      * The value changes to <code>null</code> when the wrapped stream
76      * becomes inaccessible.
77      */
78     protected InputStream wrappedStream;
79 
80 
81     /**
82      * Indicates whether this stream itself is closed.
83      * If it isn't, but {@link #wrappedStream wrappedStream}
84      * is <code>null</code>, we're running in EOF mode.
85      * All read operations will indicate EOF without accessing
86      * the underlying stream. After closing this stream, read
87      * operations will trigger an {@link IOException IOException}.
88      *
89      * @see #isReadAllowed isReadAllowed
90      */
91     private boolean selfClosed;
92 
93     /** The watcher to be notified, if any. */
94     private EofSensorWatcher eofWatcher;
95 
96 
97     /**
98      * Creates a new EOF sensor.
99      * If no watcher is passed, the underlying stream will simply be
100      * closed when EOF is detected or {@link #close close} is called.
101      * Otherwise, the watcher decides whether the underlying stream
102      * should be closed before detaching from it.
103      *
104      * @param in        the wrapped stream
105      * @param watcher   the watcher for events, or <code>null</code> for
106      *                  auto-close behavior without notification
107      */
EofSensorInputStream(final InputStream in, final EofSensorWatcher watcher)108     public EofSensorInputStream(final InputStream in,
109                                 final EofSensorWatcher watcher) {
110         if (in == null) {
111             throw new IllegalArgumentException
112                 ("Wrapped stream may not be null.");
113         }
114 
115         wrappedStream = in;
116         selfClosed = false;
117         eofWatcher = watcher;
118     }
119 
120 
121     /**
122      * Checks whether the underlying stream can be read from.
123      *
124      * @return  <code>true</code> if the underlying stream is accessible,
125      *          <code>false</code> if this stream is in EOF mode and
126      *          detached from the underlying stream
127      *
128      * @throws IOException      if this stream is already closed
129      */
isReadAllowed()130     protected boolean isReadAllowed() throws IOException {
131         if (selfClosed) {
132             throw new IOException("Attempted read on closed stream.");
133         }
134         return (wrappedStream != null);
135     }
136 
137 
138     // non-javadoc, see base class InputStream
139     @Override
read()140     public int read() throws IOException {
141         int l = -1;
142 
143         if (isReadAllowed()) {
144             try {
145                 l = wrappedStream.read();
146                 checkEOF(l);
147             } catch (IOException ex) {
148                 checkAbort();
149                 throw ex;
150             }
151         }
152 
153         return l;
154     }
155 
156 
157     // non-javadoc, see base class InputStream
158     @Override
read(byte[] b, int off, int len)159     public int read(byte[] b, int off, int len) throws IOException {
160         int l = -1;
161 
162         if (isReadAllowed()) {
163             try {
164                 l = wrappedStream.read(b,  off,  len);
165                 checkEOF(l);
166             } catch (IOException ex) {
167                 checkAbort();
168                 throw ex;
169             }
170         }
171 
172         return l;
173     }
174 
175 
176     // non-javadoc, see base class InputStream
177     @Override
read(byte[] b)178     public int read(byte[] b) throws IOException {
179         int l = -1;
180 
181         if (isReadAllowed()) {
182             try {
183                 l = wrappedStream.read(b);
184                 checkEOF(l);
185             } catch (IOException ex) {
186                 checkAbort();
187                 throw ex;
188             }
189         }
190         return l;
191     }
192 
193 
194     // non-javadoc, see base class InputStream
195     @Override
available()196     public int available() throws IOException {
197         int a = 0; // not -1
198 
199         if (isReadAllowed()) {
200             try {
201                 a = wrappedStream.available();
202                 // no checkEOF() here, available() can't trigger EOF
203             } catch (IOException ex) {
204                 checkAbort();
205                 throw ex;
206             }
207         }
208 
209         return a;
210     }
211 
212 
213     // non-javadoc, see base class InputStream
214     @Override
close()215     public void close() throws IOException {
216         // tolerate multiple calls to close()
217         selfClosed = true;
218         checkClose();
219     }
220 
221 
222     /**
223      * Detects EOF and notifies the watcher.
224      * This method should only be called while the underlying stream is
225      * still accessible. Use {@link #isReadAllowed isReadAllowed} to
226      * check that condition.
227      * <br/>
228      * If EOF is detected, the watcher will be notified and this stream
229      * is detached from the underlying stream. This prevents multiple
230      * notifications from this stream.
231      *
232      * @param eof       the result of the calling read operation.
233      *                  A negative value indicates that EOF is reached.
234      *
235      * @throws IOException
236      *          in case of an IO problem on closing the underlying stream
237      */
checkEOF(int eof)238     protected void checkEOF(int eof) throws IOException {
239 
240         if ((wrappedStream != null) && (eof < 0)) {
241             try {
242                 boolean scws = true; // should close wrapped stream?
243                 if (eofWatcher != null)
244                     scws = eofWatcher.eofDetected(wrappedStream);
245                 if (scws)
246                     wrappedStream.close();
247             } finally {
248                 wrappedStream = null;
249             }
250         }
251     }
252 
253 
254     /**
255      * Detects stream close and notifies the watcher.
256      * There's not much to detect since this is called by {@link #close close}.
257      * The watcher will only be notified if this stream is closed
258      * for the first time and before EOF has been detected.
259      * This stream will be detached from the underlying stream to prevent
260      * multiple notifications to the watcher.
261      *
262      * @throws IOException
263      *          in case of an IO problem on closing the underlying stream
264      */
checkClose()265     protected void checkClose() throws IOException {
266 
267         if (wrappedStream != null) {
268             try {
269                 boolean scws = true; // should close wrapped stream?
270                 if (eofWatcher != null)
271                     scws = eofWatcher.streamClosed(wrappedStream);
272                 if (scws)
273                     wrappedStream.close();
274             } finally {
275                 wrappedStream = null;
276             }
277         }
278     }
279 
280 
281     /**
282      * Detects stream abort and notifies the watcher.
283      * There's not much to detect since this is called by
284      * {@link #abortConnection abortConnection}.
285      * The watcher will only be notified if this stream is aborted
286      * for the first time and before EOF has been detected or the
287      * stream has been {@link #close closed} gracefully.
288      * This stream will be detached from the underlying stream to prevent
289      * multiple notifications to the watcher.
290      *
291      * @throws IOException
292      *          in case of an IO problem on closing the underlying stream
293      */
checkAbort()294     protected void checkAbort() throws IOException {
295 
296         if (wrappedStream != null) {
297             try {
298                 boolean scws = true; // should close wrapped stream?
299                 if (eofWatcher != null)
300                     scws = eofWatcher.streamAbort(wrappedStream);
301                 if (scws)
302                     wrappedStream.close();
303             } finally {
304                 wrappedStream = null;
305             }
306         }
307     }
308 
309 
310     /**
311      * Same as {@link #close close()}.
312      */
releaseConnection()313     public void releaseConnection() throws IOException {
314         this.close();
315     }
316 
317     /**
318      * Aborts this stream.
319      * This is a special version of {@link #close close()} which prevents
320      * re-use of the underlying connection, if any. Calling this method
321      * indicates that there should be no attempt to read until the end of
322      * the stream.
323      */
abortConnection()324     public void abortConnection() throws IOException {
325         // tolerate multiple calls
326         selfClosed = true;
327         checkAbort();
328     }
329 
330 } // class EOFSensorInputStream
331 
332