TimeoutOutputStream.java

package gov.usgs.earthquake.util;

import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;

/**
 * An output stream that self-closes if the specified timeout elapses between
 * writes.
 */
public class TimeoutOutputStream extends FilterOutputStream implements Runnable {

	/** write timeout in milliseconds. */
	private final long timeout;
	/** thread that enforces timeout. */
	private final Thread timeoutThread;
	/** flag for timeoutThread to terminate. */
	private boolean closed = false;

	/**
	 * Create a TimeoutOutputStream.
	 * 
	 * @param out
	 *            the wrapped output stream.
	 * @param timeout
	 *            the timeout in milliseconds between writes. If this timeout
	 *            completes, the underlying stream will be closed.
	 */
	public TimeoutOutputStream(final OutputStream out, final long timeout) {
		super(out);
		this.timeout = timeout;
		this.timeoutThread = new Thread(this);
		this.timeoutThread.start();
	}

	@Override
	public void write(int b) throws IOException {
		// pass directly to underlying stream
		this.out.write(b);
		timeoutThread.interrupt();
	}

	@Override
	public void write(byte[] buf) throws IOException {
		// pass directly to underlying stream
		this.out.write(buf);
		timeoutThread.interrupt();
	}

	@Override
	public void write(byte[] buf, int offset, int length) throws IOException {
		// pass directly to underlying stream
		this.out.write(buf, offset, length);
		timeoutThread.interrupt();
	}

	@Override
	public void close() throws IOException {
		closed = true;
		try {
			super.close();
		} finally {
			// interrupt in case close called from outside timeoutThread
			timeoutThread.interrupt();
		}
	}

	@Override
	public void run() {
		while (!closed) {
			try {
				// wait for timeout milliseconds
				Thread.sleep(timeout);
				// timeout elapsed, close stream
				try {
					close();
				} catch (IOException e) {
				}
			} catch (InterruptedException ie) {
				// a write occured
			}
		}
	}

}