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
31package org.apache.http.conn;
32
33import java.io.InputStream;
34import 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
70public 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     */
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     */
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
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
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
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
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
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     */
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     */
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     */
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     */
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     */
324    public void abortConnection() throws IOException {
325        // tolerate multiple calls
326        selfClosed = true;
327        checkAbort();
328    }
329
330} // class EOFSensorInputStream
331
332