/* * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/EofSensorInputStream.java $ * $Revision: 672367 $ * $Date: 2008-06-27 12:49:20 -0700 (Fri, 27 Jun 2008) $ * * ==================================================================== * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.http.conn; import java.io.InputStream; import java.io.IOException; /** * A stream wrapper that triggers actions on {@link #close close()} and EOF. * Primarily used to auto-release an underlying * {@link ManagedClientConnection connection} * when the response body is consumed or no longer needed. * *

* This class is based on AutoCloseInputStream in HttpClient 3.1, * but has notable differences. It does not allow mark/reset, distinguishes * different kinds of event, and does not always close the underlying stream * on EOF. That decision is left to the {@link EofSensorWatcher watcher}. *

* * @see EofSensorWatcher EofSensorWatcher * * @author Roland Weber * @author Ortwin Glueck * @author Eric Johnson * @author Mike Bowler * * * * @version $Revision: 672367 $ * * @since 4.0 * * @deprecated Please use {@link java.net.URL#openConnection} instead. * Please visit this webpage * for further details. */ @Deprecated // don't use FilterInputStream as the base class, we'd have to // override markSupported(), mark(), and reset() to disable them public class EofSensorInputStream extends InputStream implements ConnectionReleaseTrigger { /** * The wrapped input stream, while accessible. * The value changes to null when the wrapped stream * becomes inaccessible. */ protected InputStream wrappedStream; /** * Indicates whether this stream itself is closed. * If it isn't, but {@link #wrappedStream wrappedStream} * is null, we're running in EOF mode. * All read operations will indicate EOF without accessing * the underlying stream. After closing this stream, read * operations will trigger an {@link IOException IOException}. * * @see #isReadAllowed isReadAllowed */ private boolean selfClosed; /** The watcher to be notified, if any. */ private EofSensorWatcher eofWatcher; /** * Creates a new EOF sensor. * If no watcher is passed, the underlying stream will simply be * closed when EOF is detected or {@link #close close} is called. * Otherwise, the watcher decides whether the underlying stream * should be closed before detaching from it. * * @param in the wrapped stream * @param watcher the watcher for events, or null for * auto-close behavior without notification */ public EofSensorInputStream(final InputStream in, final EofSensorWatcher watcher) { if (in == null) { throw new IllegalArgumentException ("Wrapped stream may not be null."); } wrappedStream = in; selfClosed = false; eofWatcher = watcher; } /** * Checks whether the underlying stream can be read from. * * @return true if the underlying stream is accessible, * false if this stream is in EOF mode and * detached from the underlying stream * * @throws IOException if this stream is already closed */ protected boolean isReadAllowed() throws IOException { if (selfClosed) { throw new IOException("Attempted read on closed stream."); } return (wrappedStream != null); } // non-javadoc, see base class InputStream @Override public int read() throws IOException { int l = -1; if (isReadAllowed()) { try { l = wrappedStream.read(); checkEOF(l); } catch (IOException ex) { checkAbort(); throw ex; } } return l; } // non-javadoc, see base class InputStream @Override public int read(byte[] b, int off, int len) throws IOException { int l = -1; if (isReadAllowed()) { try { l = wrappedStream.read(b, off, len); checkEOF(l); } catch (IOException ex) { checkAbort(); throw ex; } } return l; } // non-javadoc, see base class InputStream @Override public int read(byte[] b) throws IOException { int l = -1; if (isReadAllowed()) { try { l = wrappedStream.read(b); checkEOF(l); } catch (IOException ex) { checkAbort(); throw ex; } } return l; } // non-javadoc, see base class InputStream @Override public int available() throws IOException { int a = 0; // not -1 if (isReadAllowed()) { try { a = wrappedStream.available(); // no checkEOF() here, available() can't trigger EOF } catch (IOException ex) { checkAbort(); throw ex; } } return a; } // non-javadoc, see base class InputStream @Override public void close() throws IOException { // tolerate multiple calls to close() selfClosed = true; checkClose(); } /** * Detects EOF and notifies the watcher. * This method should only be called while the underlying stream is * still accessible. Use {@link #isReadAllowed isReadAllowed} to * check that condition. *
* If EOF is detected, the watcher will be notified and this stream * is detached from the underlying stream. This prevents multiple * notifications from this stream. * * @param eof the result of the calling read operation. * A negative value indicates that EOF is reached. * * @throws IOException * in case of an IO problem on closing the underlying stream */ protected void checkEOF(int eof) throws IOException { if ((wrappedStream != null) && (eof < 0)) { try { boolean scws = true; // should close wrapped stream? if (eofWatcher != null) scws = eofWatcher.eofDetected(wrappedStream); if (scws) wrappedStream.close(); } finally { wrappedStream = null; } } } /** * Detects stream close and notifies the watcher. * There's not much to detect since this is called by {@link #close close}. * The watcher will only be notified if this stream is closed * for the first time and before EOF has been detected. * This stream will be detached from the underlying stream to prevent * multiple notifications to the watcher. * * @throws IOException * in case of an IO problem on closing the underlying stream */ protected void checkClose() throws IOException { if (wrappedStream != null) { try { boolean scws = true; // should close wrapped stream? if (eofWatcher != null) scws = eofWatcher.streamClosed(wrappedStream); if (scws) wrappedStream.close(); } finally { wrappedStream = null; } } } /** * Detects stream abort and notifies the watcher. * There's not much to detect since this is called by * {@link #abortConnection abortConnection}. * The watcher will only be notified if this stream is aborted * for the first time and before EOF has been detected or the * stream has been {@link #close closed} gracefully. * This stream will be detached from the underlying stream to prevent * multiple notifications to the watcher. * * @throws IOException * in case of an IO problem on closing the underlying stream */ protected void checkAbort() throws IOException { if (wrappedStream != null) { try { boolean scws = true; // should close wrapped stream? if (eofWatcher != null) scws = eofWatcher.streamAbort(wrappedStream); if (scws) wrappedStream.close(); } finally { wrappedStream = null; } } } /** * Same as {@link #close close()}. */ public void releaseConnection() throws IOException { this.close(); } /** * Aborts this stream. * This is a special version of {@link #close close()} which prevents * re-use of the underlying connection, if any. Calling this method * indicates that there should be no attempt to read until the end of * the stream. */ public void abortConnection() throws IOException { // tolerate multiple calls selfClosed = true; checkAbort(); } } // class EOFSensorInputStream