| /* |
| * Copyright (c) 2008, 2018, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code 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 General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| package nsk.share.aod; |
| |
| import java.io.*; |
| import java.util.*; |
| import nsk.share.*; |
| |
| public class ProcessExecutor { |
| |
| private String[] cmdLine; |
| |
| private long timeout; |
| |
| private boolean printProcessOutput; |
| |
| private String processOutputPrefix; |
| |
| private InputStreamReaderThread outReader; |
| |
| private InputStreamReaderThread errReader; |
| |
| private Process startedProcess; |
| |
| private ProcessWaiterThread processWaiter; |
| |
| private long expectedFinishTime; |
| |
| private volatile boolean executionCompleted; |
| |
| private int exitCode; |
| |
| private class InputStreamReaderThread extends Thread { |
| private BufferedReader in; |
| |
| private String outputPrefix; |
| |
| private List<String> output = new ArrayList<String>(); |
| |
| private volatile boolean streamWasAbruptlyClosed; |
| |
| private Throwable unexpectedException; |
| |
| public InputStreamReaderThread(InputStream in, String prefix) { |
| this.in = new BufferedReader(new InputStreamReader(in)); |
| this.outputPrefix = prefix; |
| setDaemon(true); |
| } |
| |
| public void streamWasAbruptlyClosed(boolean newValue) { |
| streamWasAbruptlyClosed = newValue; |
| } |
| |
| public void run() { |
| try { |
| while (true) { |
| String line = in.readLine(); |
| if (line == null) |
| return; |
| |
| output.add(line); |
| |
| if (printProcessOutput) |
| System.out.println(outputPrefix + line); |
| } |
| } catch (IOException e) { |
| if (!streamWasAbruptlyClosed) { |
| unexpectedException = e; |
| e.printStackTrace( ); |
| } |
| } catch (Throwable t) { |
| unexpectedException = t; |
| t.printStackTrace( ); |
| } |
| } |
| |
| void checkStatus() { |
| if (unexpectedException != null) |
| throw new Failure("Exception was thrown during InputStreamReaderThread work: " + unexpectedException, |
| unexpectedException); |
| } |
| } |
| |
| private class ProcessWaiterThread extends Thread { |
| |
| private Throwable unexpectedException; |
| |
| private Process process; |
| |
| private InputStreamReaderThread outReader; |
| |
| private InputStreamReaderThread errReader; |
| |
| ProcessWaiterThread(Process process, InputStreamReaderThread outReader, InputStreamReaderThread errReader) { |
| this.process = process; |
| this.outReader = outReader; |
| this.errReader = errReader; |
| |
| setDaemon(true); |
| } |
| |
| public void run() { |
| try { |
| exitCode = process.waitFor(); |
| outReader.join(); |
| errReader.join(); |
| |
| synchronized (ProcessWaiterThread.this) { |
| executionCompleted = true; |
| ProcessWaiterThread.this.notify(); |
| } |
| } catch (InterruptedException e) { |
| /* |
| * ProcessWaiterThread is interrupted if started process |
| * didn't finish in expected time |
| */ |
| } catch (Throwable t) { |
| unexpectedException = t; |
| t.printStackTrace(); |
| } |
| } |
| |
| void checkStatus() { |
| if (unexpectedException != null) |
| throw new Failure("Exception was thrown during ProcessWaiterThread work: " |
| + unexpectedException, unexpectedException); |
| } |
| } |
| |
| public ProcessExecutor(String cmdLine, long timeout) { |
| this.cmdLine = new String[]{cmdLine}; |
| this.timeout = timeout; |
| } |
| |
| public ProcessExecutor(String cmdLine, long timeout, String outputPrefix) { |
| this(cmdLine, timeout); |
| this.printProcessOutput = true; |
| this.processOutputPrefix = outputPrefix; |
| } |
| |
| public void startProcess() throws IOException { |
| if (cmdLine.length == 1) |
| startedProcess = Runtime.getRuntime().exec(cmdLine[0]); |
| else |
| startedProcess = Runtime.getRuntime().exec(cmdLine); |
| |
| expectedFinishTime = System.currentTimeMillis() + timeout; |
| |
| outReader = new InputStreamReaderThread(startedProcess.getInputStream(), |
| processOutputPrefix == null ? "" : processOutputPrefix + " (stdout): "); |
| errReader = new InputStreamReaderThread(startedProcess.getErrorStream(), |
| processOutputPrefix == null ? "" : processOutputPrefix + " (stderr): "); |
| |
| outReader.start(); |
| errReader.start(); |
| |
| processWaiter = new ProcessWaiterThread(startedProcess, outReader, errReader); |
| processWaiter.start(); |
| } |
| |
| |
| public void waitForProcess() throws InterruptedException { |
| synchronized (processWaiter) { |
| while ((System.currentTimeMillis() < expectedFinishTime) && !executionCompleted) { |
| processWaiter.wait(expectedFinishTime - System.currentTimeMillis()); |
| } |
| } |
| |
| if (!executionCompleted) { |
| destroyProcessAndWaitThreads(); |
| |
| executionCompleted = true; |
| |
| throw new Failure("Execution timed out (timeout: " + timeout + "ms)"); |
| } |
| } |
| |
| private void destroyProcessAndWaitThreads() { |
| outReader.streamWasAbruptlyClosed(true); |
| errReader.streamWasAbruptlyClosed(true); |
| |
| processWaiter.interrupt(); |
| startedProcess.destroy(); |
| |
| try { |
| outReader.join(); |
| errReader.join(); |
| processWaiter.join(); |
| |
| outReader.checkStatus(); |
| errReader.checkStatus(); |
| processWaiter.checkStatus(); |
| } catch (InterruptedException e) { |
| throw new Failure("Unexpected InterruptedException", e); |
| } |
| } |
| |
| private void checkProcessState() { |
| if (!executionCompleted) |
| throw new IllegalStateException("Process didn't finish execution"); |
| } |
| |
| public void destroyProcess() { |
| if (executionCompleted) |
| return; |
| |
| destroyProcessAndWaitThreads(); |
| } |
| |
| public long pid() { |
| return startedProcess.pid(); |
| } |
| |
| public int getExitCode() { |
| checkProcessState(); |
| |
| return exitCode; |
| } |
| |
| public List<String> getProcessOut() { |
| checkProcessState(); |
| |
| return outReader.output; |
| } |
| |
| public List<String> getProcessErr() { |
| checkProcessState(); |
| |
| return errReader.output; |
| } |
| } |