/*
* $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
*
* 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}.
*
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.
*