package de.invesdwin.util.streams; import java.io.IOException; import java.io.OutputStream; import javax.annotation.concurrent.NotThreadSafe; import org.apache.commons.io.output.ClosedOutputStream; import de.invesdwin.util.collections.iterable.ACloseableIterator; import de.invesdwin.util.error.Throwables; @NotThreadSafe public abstract class ADelegateOutputStream extends OutputStream { private static final org.slf4j.Logger LOGGER = org.slf4j.LoggerFactory.getLogger(ACloseableIterator.class); private OutputStream delegate; private Exception initStackTrace; private Exception readStackTrace; public ADelegateOutputStream() { if (Throwables.isDebugStackTraceEnabled()) { initStackTrace = new Exception(); initStackTrace.fillInStackTrace(); } } protected OutputStream getDelegate() { if (delegate == null) { delegate = newDelegate(); } return delegate; } protected abstract OutputStream newDelegate(); @Override public void close() throws IOException { if (delegate != null) { delegate.close(); } //forget the reference to original outputstream to potentially free some memory delegate = ClosedOutputStream.CLOSED_OUTPUT_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 " + OutputStream.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 == ClosedOutputStream.CLOSED_OUTPUT_STREAM; } @Override public void write(final int b) throws IOException { if (Throwables.isDebugStackTraceEnabled() && readStackTrace == null) { initStackTrace = null; readStackTrace = new Exception(); readStackTrace.fillInStackTrace(); } getDelegate().write(b); } @Override public void flush() throws IOException { getDelegate().flush(); } }