package de.invesdwin.util.streams;
import java.io.IOException;
import java.io.InputStream;
import javax.annotation.concurrent.NotThreadSafe;
import org.apache.commons.io.input.ClosedInputStream;
import de.invesdwin.util.collections.iterable.ACloseableIterator;
import de.invesdwin.util.error.Throwables;
@NotThreadSafe
public abstract class ADelegateInputStream extends InputStream {
private static final org.slf4j.Logger LOGGER = org.slf4j.LoggerFactory.getLogger(ACloseableIterator.class);
private InputStream delegate;
private Exception initStackTrace;
private Exception readStackTrace;
public ADelegateInputStream() {
if (Throwables.isDebugStackTraceEnabled()) {
initStackTrace = new Exception();
initStackTrace.fillInStackTrace();
}
}
@Override
public int available() throws IOException {
return getDelegate().available();
}
protected InputStream getDelegate() {
if (delegate == null) {
delegate = newDelegate();
}
return delegate;
}
protected abstract InputStream newDelegate();
@Override
public void close() throws IOException {
if (delegate != null) {
delegate.close();
}
//forget the reference to original inputstream to potentially free some memory
delegate = ClosedInputStream.CLOSED_INPUT_STREAM;
}
/**
* http://www.informit.com/articles/article.aspx?p=1216151&seqNum=7
*/
@Override
protected void finalize() throws Throwable {
if (!isClosed()) {
if (delegate != null) {
String warning = "Finalizing unclosed " + InputStream.class.getSimpleName() + " ["
+ getClass().getName() + "]";
if (Throwables.isDebugStackTraceEnabled()) {
final Exception stackTrace;
if (initStackTrace != null) {
warning += " which was initialized but never used";
stackTrace = initStackTrace;
} else {
stackTrace = readStackTrace;
}
if (stackTrace != null) {
warning += " from stacktrace:\n" + Throwables.getFullStackTrace(stackTrace);
}
}
LOGGER.warn(warning);
}
close();
}
super.finalize();
}
public boolean isClosed() {
return delegate == ClosedInputStream.CLOSED_INPUT_STREAM;
}
@Override
public synchronized void mark(final int readlimit) {
getDelegate().mark(readlimit);
}
@Override
public boolean markSupported() {
return getDelegate().markSupported();
}
@Override
public synchronized void reset() throws IOException {
getDelegate().reset();
}
@Override
public int read() throws IOException {
if (Throwables.isDebugStackTraceEnabled() && readStackTrace == null) {
initStackTrace = null;
readStackTrace = new Exception();
readStackTrace.fillInStackTrace();
}
final int read = getDelegate().read();
if (shouldCloseOnMinus1Read() && read == -1) {
close();
}
return read;
}
protected boolean shouldCloseOnMinus1Read() {
return true;
}
}