/* * Vitry, copyright (C) Hans Hoglund 2011 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * See COPYING.txt for details. */ package vitry.runtime.util; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; /** * Pushes input from one stream to another. * * @author Hans Hoglund */ public class Pusher { private static enum PusherState { INIT, STARTED, STOPPED, STREAM_END } private final InputStream source; private final OutputStream sink; private volatile PusherState state = PusherState.INIT; private PushThread thread = null; private Throwable exception = null; public Pusher(InputStream source, OutputStream sink) { this.source = source; this.sink = sink; } public Pusher(BufferedInputStream source, PrintStream sink) { this.source = source; this.sink = sink; } /** * Start pushing asynchronously. * @throws IllegalStateException if the end of stream has been reached. */ public void start() { switch (state) { case INIT: case STOPPED: thread = new PushThread(); thread.start(); break; case STARTED: break; case STREAM_END: throw new IllegalStateException("Nothing more to push"); } } /** * Stop pushing. */ public void stop() { switch (state) { case INIT: case STOPPED: case STREAM_END: break; case STARTED: thread.interrupt(); break; } } /** * Whether the pusher completed normally. */ public boolean isDone() { return state == PusherState.STREAM_END; } /** * If an exception has occured during push, return it, otherwise * return null. */ public Throwable getException() { return exception; } private class PushThread extends Thread { public void run() { state = PusherState.STARTED; int chars; byte[] buffer = new byte[1024]; try { try { while ((chars = source.read(buffer, 0, buffer.length)) > 0) { sink.write(buffer, 0, chars); synchronized (this) { // Wait for interrupt this.wait(1); } } // No more input state = PusherState.STREAM_END; sink.flush(); } catch (InterruptedException e) { state = PusherState.STOPPED; sink.flush(); } } catch (IOException e) { state = PusherState.STOPPED; exception = e; } } } public static void main(String[] args) { Pusher p = new Pusher(System.in, System.out); p.start(); } }