JavaSE ネイティブコマンド実行(旧) の Java 5 近代化改修版
package com.snail.grappaexam;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public final class CommandExecutor {
/** コマンド処理が継続中かのフラグ. */
private boolean isRunning = false;
/** HOME Directory. */
private String pHomeDir = null;
/** スレッドプール. */
private static ExecutorService thPool;
static {
// 3秒間使われなかったらプールしているスレッドを解放する
// (すべてのスレッドが解放されるまでアプリが終了しない)
thPool = new ThreadPoolExecutor(
0, Integer.MAX_VALUE,
3L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
/**
* コンストラクタ.
* ホームディレクトリはカレントディレクトリになります
*/
public CommandExecutor() {
super();
}
/**
* コンストラクタ.
* @param homeDir ホームディレクトリ
*/
public CommandExecutor(final String homeDir) {
this();
pHomeDir = homeDir;
}
/**
* シェルコマンドを実行します.
* @param cmdArray コマンド
* @return 実行結果
* @throws InterruptedException プロセス実行例外
* @throws IOException プロセスの出力ストリームが異常
*/
public CommandResult exec(final String stdin, final String... cmdArray)
throws IOException, InterruptedException, ExecutionException {
long startTime = System.currentTimeMillis();
CommandResult res = new CommandResult();
ProcessBuilder pb = new ProcessBuilder(cmdArray);
if (pHomeDir != null) {
pb.directory(new File(pHomeDir));
}
Process process = pb.start();
isRunning = true;
Future<String> stdout = thPool.submit(new InputHandler(process.getInputStream()));
Future<String> stderr = thPool.submit(new InputHandler(process.getErrorStream()));
Thread watchdog = new Thread(new WatchDog(process));
watchdog.start();
OutputStream out = process.getOutputStream();
out.write(stdin.getBytes(Charset.forName("UTF-8")));
out.close();
res.setStatus(process.waitFor());
long endTime = System.currentTimeMillis();
isRunning = false;
if (watchdog.isAlive()) {
watchdog.interrupt();
}
res.setStdout(stdout.get());
res.setStderr(stderr.get());
process.destroy();
res.setProcessingTime(endTime - startTime);
return res;
}
/**
* ProcessのStdout、Stderrの受け取り処理を行う.
* <pre>
* JavaVMの子プロセスからの出力を受け取るバッファは非常に小さいので
* 出力を別スレッドで逐次取り出す必要がある
* </pre>
*/
private class InputHandler implements Callable<String> {
/**
* シェルコマンドからの出力を受け取るInputStream
*/
private InputStream pIn;
/**
* デフォルトコンストラクタの無効化
*/
private InputHandler() {
super();
}
/**
* コンストラクタ
* @param argIn シェルコマンドからの出力を受け取るInputStream
*/
private InputHandler(final InputStream in) {
this();
pIn = in;
}
@Override
public String call() throws Exception {
ByteArrayOutputStream bArray = new ByteArrayOutputStream();
try {
byte[] tmp = new byte[128]; // 大きくしすぎるとバッファが詰まる
int size;
while((size = pIn.read(tmp)) > 0) {
bArray.write(tmp, 0, size);
}
} catch (IOException ex) {
ex = null; // なにもしないし、できない
} finally {
try {
pIn.close();
} catch (IOException ex) {
ex = null; // なにもしないし、できない
}
}
return bArray.toString();
}
}
/**
* 起動したシェルコマンドが長い間終了しない場合に強制終了します.
* <pre>
* Apache Ant を参考に作成した。
* </pre>
*/
private class WatchDog implements Runnable {
/**
* 実行中のプロセス
*/
private Process process;
/**
* Creates a new WatchDog object.
*
* @param param 実行予定のProcess
*/
public WatchDog(final Process param) {
this();
process = param;
}
/**
* デフォルトコンストラクタの無効化
*/
private WatchDog() {
super();
}
/**
* 30000ミリ秒待ってまだプロセスが動いていれば、プロセスを強制終了します
*/
@Override
public synchronized void run() {
long until = System.currentTimeMillis() + 30000L;
long now;
while (isRunning && (until > (now = System.currentTimeMillis()))) {
try {
wait(until - now);
} catch (InterruptedException ignoreEx) {
// Ignore exception
ignoreEx = null;
}
}
if (isRunning) {
process.destroy();
}
}
}
}
package com.snail.grappaexam;
import java.io.Serializable;
public final class CommandResult implements Serializable {
private String stderr;
private String stdout;
private int status;
private long processingTime;
public void setProcessingTime(long processingTime) {
this.processingTime = processingTime;
}
public void setStatus(int status) {
this.status = status;
}
public void setStderr(String stderr) {
this.stderr = stderr;
}
public void setStdout(String stdout) {
this.stdout = stdout;
}
public long getProcessingTime() {
return processingTime;
}
public int getStatus() {
return status;
}
public String getStderr() {
return stderr;
}
public String getStdout() {
return stdout;
}
}
public class App
{
public static void main( String[] args ) throws ExecutionException
{
try {
CommandExecutor cmd = new CommandExecutor("/Users/Atsushi/");
CommandResult res = cmd.exec("","ls","-la"); // 第一引数は標準入力に与える文字列
System.out.println("STATUS:" + res.getStatus());
System.out.println("STDOUT:\n" + res.getStdout());
} catch (IOException ex) {
Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
} catch (InterruptedException ex) {
Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
}
}
}