/* * $Id$ * * Copyright (c) 2010 by Joel Uckelman * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License (LGPL) as published by the Free Software Foundation. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, copies are available * at http://www.opensource.org. */ package VASSAL.tools.io; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A {@link Callable} which wraps a {@link java.lang.Process}. * * @author Joel Uckelman * @since 3.2.0 */ class ProcessCallable implements Callable<Integer> { private static final Logger logger = LoggerFactory.getLogger(ProcessCallable.class); protected final Process proc; protected final InputStreamPump stdoutPump; protected final InputStreamPump stderrPump; protected final ExecutorService exec; /** * Creates a <code>ProcessCallable</code>. * * @param proc the process * @param stdout the stream where the process' STDOUT is redirected * @param stderr the stream where the process' STDERR is redirected * @param exec the executor which runs the stream pumps */ public ProcessCallable( Process proc, InputStreamPump stdoutPump, InputStreamPump stderrPump, ExecutorService exec) { if (proc == null) throw new IllegalArgumentException("proc == null"); if (stdoutPump == null) { throw new IllegalArgumentException("stdoutPump == null"); } if (stderrPump == null) { throw new IllegalArgumentException("stderrPump == null"); } if (exec == null) throw new IllegalArgumentException("exec == null"); this.proc = proc; this.stdoutPump = stdoutPump; this.stderrPump = stderrPump; this.exec = exec; } /** * {@inheritDoc} * * @return the return value of the process */ public Integer call() { stdoutPump.setInputStream(proc.getInputStream()); stderrPump.setInputStream(proc.getErrorStream()); final Future<?> out_f = exec.submit(stdoutPump); final Future<?> err_f = exec.submit(stderrPump); try { final int result = proc.waitFor(); // stop the stream pumps stopPump(out_f); stopPump(err_f); // close stdout, stderr, stdin closeStreams(); return result; } catch (InterruptedException e) { // We don't log this because it's not an error, it just // means that the process is being cancelled. // cancel the futures out_f.cancel(true); err_f.cancel(true); // close stdout, stderr, stdin closeStreams(); // kill the process proc.destroy(); } // Pardon the interruption. return -1; } protected void stopPump(Future<?> f) { try { f.get(1000L, TimeUnit.MILLISECONDS); } catch (ExecutionException e) { logger.error("", e); } catch (InterruptedException e) { logger.error("", e); } catch (TimeoutException e) { logger.error("", e); f.cancel(true); } } protected void closeStreams() { IOUtils.closeQuietly(proc.getOutputStream()); IOUtils.closeQuietly(proc.getErrorStream()); IOUtils.closeQuietly(proc.getInputStream()); } }