/* * Copyright 2016 The Depan Project Authors * * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 * * 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.devtools.depan.platform.process; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; /** * Execute a system {@link Process} on this thread. Standard input and * output are collected via per-process threads and their contents * are available after {@link #execProcess(ProcessBuilder)} terminates. * * @author <a href="mailto:[email protected]">Lee Carver</a> */ public abstract class ProcessExecutor { private int exitCode; private String outText; private String errText; /** * Hook method for derived classes to configure the output thread. * A call to {@link Thread#setName(String)} is common. * * @param forOut {@link Thread} to configure */ protected abstract void configOutThread(Thread forOut); /** * Hook method for derived classes to configure the error thread. * A call to {@link Thread#setName(String)} is common. * * @param forErr {@link Thread} to configure */ protected abstract void configErrThread(Thread forErr); /** * Compute the effective POM for the supplied POM definition file. * After this method returns, various results from the execution can * be retrieved. The available results include the effective pom and * the exit code from the process execution. * * This is a Template method which relies on the methods * {@link #configOutThread(Thread)} and {@link #configErrThread(Thread)}. * Each derived class is expected to implement these hook methods. */ protected void execProcess(ProcessBuilder builder) throws IOException, InterruptedException { Process process = builder.start(); ReaderThread outThread = new ReaderThread(process.getInputStream()); configOutThread(outThread); outThread.start(); ReaderThread errThread = new ReaderThread(process.getErrorStream()); configErrThread(errThread); errThread.start(); exitCode = process.waitFor(); outThread.shutdown(); errThread.shutdown(); outText = outThread.getResult(); errText = errThread.getResult(); } public int getExitCode() { return exitCode; } public String getOut() { return outText; } public String getErr() { return errText; } private static class ReaderThread extends Thread { private InputStream input; private StringBuilder result = new StringBuilder(); public ReaderThread(InputStream input) { this.input = input; } public String getResult() { return result.toString(); } public void shutdown() { if (isAlive()) { interrupt(); } } @Override public void run() { Reader reader = new InputStreamReader(input); boolean ready = true; while (ready) { try { // It's OK if this blocks until a character is ready. int next = reader.read(); if (next < 0) { ready = false; } else { result.append((char) next); } } catch (IOException e) { ready = false; } } } } }