package io.lumify.core.util; import io.lumify.core.exception.LumifyException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; public class Pipe { private static ExecutorService executor = Executors.newFixedThreadPool(5); private Semaphore completionSemaphore = new Semaphore(1); public Pipe pipe(final InputStream in, final OutputStream out, final StatusHandler statusHandler) { final boolean[] threadStarted = new boolean[1]; threadStarted[0] = false; executor.execute(new Runnable() { @Override public void run() { try { int read; byte[] buffer = new byte[1 * 1024 * 1024]; completionSemaphore.acquire(); threadStarted[0] = true; while ((read = in.read(buffer)) > 0) { out.write(buffer, 0, read); } } catch (IOException e) { statusHandler.handleException(e); } catch (InterruptedException e) { statusHandler.handleException(e); } finally { threadStarted[0] = true; // if something fails before we get a chance to set this to true make sure it happens statusHandler.handleComplete(); completionSemaphore.release(); } } }); // It could be possible for the process to exit before this thread gets started resulting in this thread // to not read the output stream. This waiting is to make sure the thread is started before we return. // http://stackoverflow.com/questions/2150723/process-waitfor-threads-and-inputstreams (see resolution) while (!threadStarted[0]) { try { Thread.sleep(100); } catch (InterruptedException e) { throw new LumifyException("Could not sleep", e); } } return this; } public void waitForCompletion(long timeout, TimeUnit units) throws InterruptedException { completionSemaphore.tryAcquire(timeout, units); } public static class StatusHandler { public void handleException(Exception e) { } public void handleComplete() { } } }