new port of 3.1 AutoCloseInputStream

git-svn-id: https://svn.apache.org/repos/asf/jakarta/httpcomponents/httpclient/trunk@508886 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Roland Weber 2007-02-18 08:25:55 +00:00
parent 0eb8e5cf27
commit 41916e4de1
2 changed files with 338 additions and 0 deletions

View File

@ -0,0 +1,250 @@
/*
* $Header$
* $Revision$
* $Date$
*
* ====================================================================
*
* 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
* <http://www.apache.org/>.
*
*/
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.
*
* <p>
* This class is based on <code>AutoCloseInputStream</code> 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}.
* </p>
*
* @see EofSensorWatcher EofSensorWatcher
*
* @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
* @author Ortwin Glueck
* @author Eric Johnson
* @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
*
*
* <!-- empty lines to avoid svn diff problems -->
* @version $Revision$
*
* @since 4.0
*/
public class EofSensorInputStream extends InputStream {
// don't use FilterInputStream as the base class, we'd have to
// override markSupported(), mark(), and reset() to disable them
/**
* The wrapped input stream, while accessible.
* The value changes to <code>null</code> 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 <code>null</code>, 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 <code>null</code> 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 underyling stream can be read from.
*
* @return <code>true</code> if the underlying stream is accessible,
* <code>false</code> 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
public int read() throws IOException {
int l = -1;
if (isReadAllowed()) {
l = wrappedStream.read();
checkEOF(l);
}
return l;
}
// non-javadoc, see base class InputStream
public int read(byte[] b, int off, int len) throws IOException {
int l = -1;
if (isReadAllowed()) {
l = wrappedStream.read(b, off, len);
checkEOF(l);
}
return l;
}
// non-javadoc, see base class InputStream
public int read(byte[] b) throws IOException {
int l = -1;
if (isReadAllowed()) {
l = wrappedStream.read(b);
checkEOF(l);
}
return l;
}
// non-javadoc, see base class InputStream
public int available() throws IOException {
int a = 0; // not -1
if (isReadAllowed()) {
a = wrappedStream.available();
// no checkEOF() here, available() can't trigger EOF
}
return a;
}
// non-javadoc, see base class InputStream
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.
* <br/>
* 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;
}
}
}
} // class EOFSensorInputStream

View File

@ -0,0 +1,88 @@
/*
* $Header$
* $Revision$
* $Date$
*
* ====================================================================
*
* 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
* <http://www.apache.org/>.
*
*/
package org.apache.http.conn;
import java.io.InputStream;
import java.io.IOException;
/**
* A watcher for {@link EofSensorInputStream EofSensorInputStream}.
* Each stream will notify it's watcher at most once.
*
* @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
*
*
* <!-- empty lines to avoid svn diff problems -->
* @version $Revision$
*
* @since 4.0
*/
public interface EofSensorWatcher {
/**
* Indicates that EOF is detected.
*
* @param wrapped the underlying stream which has reached EOF
*
* @return <code>true</code> if <code>wrapped</code> should be closed,
* <code>false</code> if it should be left alone
*
* @throws IOException
* in case of an IO problem, for example if the watcher itself
* closes the underlying stream. The caller will leave the
* wrapped stream alone, as if <code>false</code> was returned.
*/
boolean eofDetected(InputStream wrapped)
throws IOException
;
/**
* Indicates that the {@link EofSensorInputStream stream} is closed.
* This method will be called only if EOF was <i>not</i> detected
* before closing. Otherwise, {@link #eofDetected eofDetected} is called.
*
* @param wrapped the underlying stream which has not reached EOF
*
* @return <code>true</code> if <code>wrapped</code> should be closed,
* <code>false</code> if it should be left alone
*
* @throws IOException
* in case of an IO problem, for example if the watcher itself
* closes the underlying stream. The caller will leave the
* wrapped stream alone, as if <code>false</code> was returned.
*/
boolean streamClosed(InputStream wrapped)
throws IOException
;
} // interface EofSensorWatcher