// Copyright (C) 2003-2009 by Object Mentor, Inc. All rights reserved. // Released under the terms of the CPL Common Public License version 1.0. package fitnesse.components; import util.TimeMeasurement; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class CommandRunner { protected Process process; protected String input = ""; protected List<Throwable> exceptions = new ArrayList<Throwable>(); protected OutputStream stdin; protected InputStream stdout; protected InputStream stderr; protected StringBuffer outputBuffer = new StringBuffer(); protected StringBuffer errorBuffer = new StringBuffer(); protected int exitCode = -1; private TimeMeasurement timeMeasurement = new TimeMeasurement(); private String command = ""; private Map<String, String> environmentVariables; public CommandRunner() { } public CommandRunner(String command, String input) { this(command, input, null); } public CommandRunner(String command, String input, Map<String, String> environmentVariables) { this.command = command; this.input = input; this.environmentVariables = environmentVariables; } public void asynchronousStart() throws IOException { Runtime rt = Runtime.getRuntime(); timeMeasurement.start(); String[] environmentVariables = determineEnvironment(); process = rt.exec(command, environmentVariables); stdin = process.getOutputStream(); stdout = process.getInputStream(); stderr = process.getErrorStream(); new Thread(new OuputReadingRunnable(stdout, outputBuffer), "CommandRunner stdout").start(); new Thread(new OuputReadingRunnable(stderr, errorBuffer), "CommandRunner error").start(); sendInput(); } private String[] determineEnvironment() { if (environmentVariables == null) { return null; } Map<String, String> systemVariables = new HashMap<String, String>(System.getenv()); systemVariables.putAll(environmentVariables); List<String> systemVariableAssignments = new ArrayList<String>(); for (Map.Entry<String, String> entry : systemVariables.entrySet()) { systemVariableAssignments.add(entry.getKey() + "=" + entry.getValue()); } return systemVariableAssignments.toArray(new String[systemVariableAssignments.size()]); } public void run() throws Exception { asynchronousStart(); join(); } public void join() { waitForDeathOf(process); timeMeasurement.stop(); exitCode = process.exitValue(); } private void waitForDeathOf(Process process) { int timeStep = 100; try { for (int maxDelay = 2000; maxDelay > 0; maxDelay -= timeStep) { if (isDead(process)) { return; } Thread.sleep(timeStep); } } catch (InterruptedException e) { e.printStackTrace(); } System.err.println("Could not detect death of command line test runner."); } private boolean isDead(Process process) throws InterruptedException { try { process.exitValue(); return true; } catch (IllegalThreadStateException e) { return false; } } public void kill() { if (process != null) { process.destroy(); join(); } } protected void setCommand(String command) { this.command = command; } public String getCommand() { return command; } public String getOutput() { return outputBuffer.toString(); } public String getError() { return errorBuffer.toString(); } public List<Throwable> getExceptions() { return exceptions; } public boolean hasExceptions() { return exceptions.size() > 0; } public boolean wroteToErrorStream() { return errorBuffer.length() > 0; } public boolean wroteToOutputStream() { return outputBuffer.length() > 0; } public int getExitCode() { return exitCode; } public void exceptionOccurred(Exception e) { exceptions.add(e); } public long getExecutionTime() { return timeMeasurement.elapsed(); } protected void sendInput() { Thread thread = new Thread() { public void run() { try { stdin.write(input.getBytes("UTF-8")); stdin.flush(); } catch (Exception e) { exceptionOccurred(e); } finally { try { stdin.close(); } catch (IOException e) { e.printStackTrace(); } } } }; thread.start(); try { thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } } private void readOutput(InputStream input, StringBuffer buffer) { try { int c; while ((c = input.read()) != -1) buffer.append((char) c); } catch (Exception e) { exceptionOccurred(e); } } private class OuputReadingRunnable implements Runnable { public InputStream input; public StringBuffer buffer; public OuputReadingRunnable(InputStream input, StringBuffer buffer) { this.input = input; this.buffer = buffer; } public void run() { readOutput(input, buffer); } } }