/* * Copyright (c) 2013, the Dart project authors. * * Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.eclipse.org/legal/epl-v10.html * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package com.google.dart.engine.utilities.io; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.UnsupportedEncodingException; /** * Execute the process created by the given process builder; collect the results and the exit code. * The process runs to completion before the run() method returns. */ public class ProcessRunner { private ProcessBuilder processBuilder; private int exitCode; private StringBuilder stdout = new StringBuilder(); private StringBuilder stderr = new StringBuilder(); private Thread processThread; private Process process; public ProcessRunner(ProcessBuilder processBuilder) { this.processBuilder = processBuilder; } public ProcessRunner(String[] arguments) { this(new ProcessBuilder(arguments)); } public void dispose() { if (process != null) { // This is set to null in runAsync(). process.destroy(); } } public int getExitCode() { return exitCode; } public String getStdErr() { return stderr.toString(); } public String getStdOut() { return stdout.toString(); } /** * Execute the process created by the process builder, wait up to the specified time for the * process to complete, and return the exit value. * * @param milliseconds the maximum number of milliseconds to wait for completion * @return the exit value or -1 if timed out waiting for the process to complete */ public int runSync(long milliseconds) throws IOException { exitCode = 0; stdout.setLength(0); stderr.setLength(0); final Process process = processBuilder.start(); processThread = new Thread(new Runnable() { @Override public void run() { try { exitCode = process.waitFor(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }); // Read from stdout. Thread stdoutThread = new Thread(new Runnable() { @Override public void run() { pipeOutput(process.getInputStream(), stdout); } }); // Read from stderr. Thread stderrThread = new Thread(new Runnable() { @Override public void run() { pipeOutput(process.getErrorStream(), stderr); } }); processThread.start(); stdoutThread.start(); stderrThread.start(); try { processThread.join(milliseconds); if (processThread.isAlive()) { exitCode = -1; } else { // Make sure we've read all the output. stdoutThread.join(); stderrThread.join(); } return exitCode; } catch (InterruptedException e) { throw new IOException(e); } } protected void pipeOutput(InputStream in, StringBuilder builder) { try { Reader reader = new InputStreamReader(in, "UTF-8"); char[] buffer = new char[512]; int count = reader.read(buffer); while (count != -1) { builder.append(buffer, 0, count); count = reader.read(buffer); } } catch (UnsupportedEncodingException e) { //TODO (danrubel): better handle this e.printStackTrace(); } catch (IOException e) { // This exception is expected. } } }