package au.com.vaadinutils.util;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.google.common.base.Preconditions;
/**
* encapsulates the process of joining together a PipedInputStream and a
* PipedOutputStream, without leaving either of them hanging
*
* @author rsutton
*
*/
public class PipedOutputStreamWrapper extends OutputStream
{
final CountDownLatch readLatch = new CountDownLatch(1);
final CountDownLatch writeLatch = new CountDownLatch(1);
final private PipedInputStream inputStream = new PipedInputStream();
volatile private PipedOutputStream outputStream;
volatile Long writerThreadId;
Logger logger = LogManager.getLogger();
/**
* do not call this method until outputIsReady returns true
*
* the thread that calls this method must NOT be the same thread that is
* writing to the OutputStream
*
* @return
* @throws InterruptedException
*/
public InputStream getInputStream() throws InterruptedException
{
if (!writeLatch.await(10, TimeUnit.SECONDS))
{
logger.warn("The writer thread seems to be taking a long time to start, will wait a further 10 minutes.");
}
if (!writeLatch.await(10, TimeUnit.MINUTES))
{
throw new RuntimeException("Reader timeout, waiting for writer");
}
Preconditions.checkState(writerThreadId != Thread.currentThread().getId(),
"The consumer thread(getInputStream) must not be the producer thread(write)");
readLatch.countDown();
return inputStream;
}
volatile boolean writerStarted = false;
@Override
public void write(int b) throws IOException
{
if (!writerStarted)
{
writerStarted = true;
writerThreadId = Thread.currentThread().getId();
outputStream = new PipedOutputStream(inputStream);
writeLatch.countDown();
try
{
if (!readLatch.await(10, TimeUnit.MINUTES))
{
throw new RuntimeException("Writer timeout, waiting for reader.");
}
}
catch (InterruptedException e)
{
outputStream.close();
inputStream.close();
throw new RuntimeException(e);
}
}
outputStream.write(b);
}
public void close() throws IOException
{
if (outputStream != null)
{
outputStream.close();
}
}
public void waitForOutputToBeReady(int duration, TimeUnit unit) throws InterruptedException, TimeoutException
{
if (!writeLatch.await(duration, unit))
{
throw new TimeoutException("Output stream Timeout");
}
}
}