package scribtest; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.Callable; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.apache.commons.io.IOUtils; public class ExecUtil { /** * The summary of a process. * @author Tiago Cogumbreiro (cogumbreiro@users.sf.net) * */ public static class ProcessSummary { /** * The standard output. */ public final String stdout; /** * The standard error output. */ public final String stderr; /** * The exit value of the process. */ public final int exitValue; public ProcessSummary(String stdout, String stderr, int exitValue) { this.stdout = stdout; this.stderr = stderr; this.exitValue = exitValue; } } public static String joinPath(String ...parts) { return join(File.separator, parts); } public static String joinPath(Iterable<String> parts) { return join(File.separator, parts); } public static String join(String separator, String ...parts) { int count = 0; StringBuilder builder = new StringBuilder(); for (String part : parts) { if (count > 0) { builder.append(separator); } builder.append(part); count++; } return builder.toString(); } public static String join(String separator, Iterable<String> parts) { int count = 0; StringBuilder builder = new StringBuilder(); for (String part : parts) { if (count > 0) { builder.append(separator); } builder.append(part); count++; } return builder.toString(); } /** * Runs a process, up to a certain timeout, * * @throws IOException * @throws TimeoutException * @throws ExecutionException * @throws InterruptedException */ public static ProcessSummary execUntil(long timeout, String... command) throws IOException, InterruptedException, ExecutionException { return execUntil(new TreeMap<String, String>(), timeout, command); } /** * Runs a process, up to a certain timeout, * * @throws IOException * @throws TimeoutException * @throws ExecutionException * @throws InterruptedException */ public static ProcessSummary execUntil(Map<String, String> env, long timeout, String... command) throws IOException, InterruptedException, ExecutionException { ProcessBuilder builder = new ProcessBuilder(command); builder.environment().putAll(env); Process process = builder.start(); ExecutorService executorService = Executors.newFixedThreadPool(3); CyclicBarrier onStart = new CyclicBarrier(3); Future<String> stderr = executorService.submit(toString(process.getErrorStream(), onStart)); Future<String> stdout = executorService.submit(toString(process.getInputStream(), onStart)); Future<Integer> waitFor = executorService.submit(waitFor(process, onStart)); ProcessSummary result = null; try { waitFor.get(timeout, TimeUnit.MILLISECONDS); } catch (TimeoutException e) { // timeouts are ok } finally { process.destroy(); waitFor.get(); result = new ProcessSummary(stdout.get(), stderr.get(), process.exitValue()); executorService.shutdown(); } return result; } /** * Waits for a process to terminate. * * @param process The process it should wait for. * @param onStart Notifies others upon starting. * @return The return code. */ public static Callable<Integer> waitFor(final Process process, final CyclicBarrier onStart) { return new Callable<Integer>() { public Integer call() throws InterruptedException, BrokenBarrierException { onStart.await(); return process.waitFor(); } }; } /** * Converts an input stream to a string as much as possible. It stops * at IOExceptions, but returns what it read thus far. * @param in The input stream. * @param onStart Notify others that it started. */ private static Callable<String> toString(final InputStream input, final CyclicBarrier onStart) { return new Callable<String>() { public String call() throws InterruptedException, BrokenBarrierException, IOException { onStart.await(); try { return IOUtils.toString(input); } finally { IOUtils.closeQuietly(input); } } }; } }