Skip to content

Instantly share code, notes, and snippets.

@nobuoka
Created September 29, 2016 07:58
Show Gist options
  • Save nobuoka/2ad1d5d3468cbd83d8799080c288281e to your computer and use it in GitHub Desktop.
Save nobuoka/2ad1d5d3468cbd83d8799080c288281e to your computer and use it in GitHub Desktop.
package info.vividcode;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* すぐに実行終了するコマンド (外部プロセス) を実行するクラス。
*/
public class CommandProcessRunner {
private static boolean readOutputToBufferIfAvailable(BufferedInputStream out, ByteArrayOutputStream outBuf, byte[] buf) throws IOException {
int available = out.available();
if (available > 0) {
int numReadBytes = out.read(buf);
if (numReadBytes > 0) {
outBuf.write(buf, 0, numReadBytes);
return true;
}
}
return false;
}
private static void readOutputToBufferWhileAvailable(BufferedInputStream out, ByteArrayOutputStream outBuf, byte[] buf) throws IOException, InterruptedException {
while (true) {
if (Thread.currentThread().isInterrupted()) throw new InterruptedException();
boolean read = readOutputToBufferIfAvailable(out, outBuf, buf);
if (!read) break;
}
}
private static void readOutputToBufferUntilTerminated(BufferedInputStream out, ByteArrayOutputStream outBuf, byte[] buf) throws IOException, InterruptedException {
while (true) {
if (Thread.currentThread().isInterrupted()) throw new InterruptedException();
int numReadBytes = out.read(buf);
if (numReadBytes == -1) break;
outBuf.write(buf, 0, numReadBytes);
}
}
/**
* 引数で指定されたプロセスを起動し、標準出力とエラー出力を読み込みながらプロセスの終了を待つ。
* 終了コードと標準出力とエラー出力をまとめたオブジェクトを結果として返す。
* @param command 実行するコマンドとその引数。
* @return コマンドの実行結果。
* @throws IOException プロセスとのやり取りで IO エラーが発生した場合に、そのエラーがそのまま投げられる。
* @throws InterruptedException 実行中のスレッドが中断された場合に発生する。
*/
public static CommandProcessRunner.Result execute(List<String> command) throws IOException, InterruptedException {
byte[] buf = new byte[1024];
ByteArrayOutputStream stdOutBuf = new ByteArrayOutputStream();
ByteArrayOutputStream errOutBuf = new ByteArrayOutputStream();
ProcessBuilder pb = new ProcessBuilder(command);
Process process = pb.start();
try {
try (
BufferedInputStream stdOut = new BufferedInputStream(process.getInputStream());
BufferedInputStream errOut = new BufferedInputStream(process.getErrorStream())
) {
while (true) {
readOutputToBufferWhileAvailable(stdOut, stdOutBuf, buf);
readOutputToBufferWhileAvailable(errOut, errOutBuf, buf);
boolean hasExited;
try {
hasExited = process.waitFor(10, TimeUnit.MICROSECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw e;
}
if (hasExited) break;
}
readOutputToBufferUntilTerminated(stdOut, stdOutBuf, buf);
readOutputToBufferUntilTerminated(errOut, errOutBuf, buf);
}
return new Result(process.exitValue(), stdOutBuf.toByteArray(), errOutBuf.toByteArray());
} finally {
if (process.isAlive()) {
process.destroyForcibly();
if (!Thread.currentThread().isInterrupted()) {
process.waitFor();
}
}
}
}
public static class Result {
public final int exitValue;
public final byte[] standardOutput;
public final byte[] errorOutput;
public Result(int exitValue, byte[] standardOutput, byte[] errorOutput) {
this.exitValue = exitValue;
this.standardOutput = standardOutput;
this.errorOutput = errorOutput;
}
}
}
package info.vividcode;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.concurrent.*;
public class Main {
/**
* Windows マシンでスリープコマンドを実行する例。
*/
public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
try {
Future<CommandProcessRunner.Result> resultFuture = executorService.submit(() ->
CommandProcessRunner.execute(Arrays.asList("powershell", "-Command", "Start-Sleep -Seconds 5")));
CommandProcessRunner.Result result;
try {
result = resultFuture.get(6, TimeUnit.SECONDS);
} catch (TimeoutException e) {
// 待ち時間を過ぎても終了しなかった場合はキャンセルする。
resultFuture.cancel(true);
throw e;
}
System.out.println("終了コード: " + result.exitValue);
System.out.println("標準出力 : " + new String(result.standardOutput, Charset.forName("Shift_JIS")));
System.out.println("エラー出力 : " + new String(result.errorOutput, Charset.forName("Shift_JIS")));
} finally {
executorService.shutdown();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment