Java 5 以降の場合はこちら → JavaSE ネイティブコマンド実行
Process process = Runtime.getRuntime().exec(string[]); int returnCode = process.waitFor();
InputStream stdout = process.getInputStream() InputStream stderr = process.getErrorStream() OutputStream stdin = process.getOutputStream()
public final class CommandResult
implements Serializable {
private String stderr;
private String stdout;
private int status;
private long processingTime;
…(中略:setter/getter)…
}
public final class CommandExecutor {
/** コマンド処理が継続中かのフラグ */
private boolean isRunning = false;
/**
* シェルコマンドを実行します
*
* @param cmdArray コマンド
* @return 実行結果
* @throws InterruptedException プロセス実行例外
* @throws IOException プロセスの出力ストリームが異常
*/
public CommandResult exec(final String[] cmdArray)
throws IOException, InterruptedException {
long startTime = System.currentTimeMillis();
CommandResult res = new CommandResult();
Process process = Runtime.getRuntime().exec(cmdArray);
isRunning = true;
WatchDog wd = new WatchDog(process);
CommandOutputHandler stdoutHandler =
new CommandOutputHandler(process.getInputStream());
CommandOutputHandler stderrHandler =
new CommandOutputHandler(process.getErrorStream());
stdoutHandler.start();
stderrHandler.start();
wd.start();
res.setStatus(process.waitFor());
stdoutHandler.join(); // Solaris だと、join しないと
stderrHandler.join(); // 高負荷時に stdout、stderr を取りこぼす
process.destroy(); // Linux だと、destroy しないと高負荷時に Too many open files になる
long endTime = System.currentTimeMillis();
isRunning = false;
if (!stdoutHandler.isInterrupted()) {
stdoutHandler.interrupt();
}
if (!stderrHandler.isInterrupted()) {
stderrHandler.interrupt();
}
if (!wd.isInterrupted()) {
wd.interrupt();
}
res.setStdout(stdoutHandler.getCmdOutput());
res.setStderr(stderrHandler.getCmdOutput());
res.setProcessingTime(endTime - startTime);
// ログ出力
logger.info(cmdId + ":" + res);
return res;
}
/**
* ProcessのStdout、Stderrの受け取り処理を行う.
* <pre>
* JavaVMの子プロセスからの出力を受け取るバッファは非常に小さいので
* 出力を別スレッドで逐次取り出す必要がある
* </pre>
*/
private final class CommandOutputHandler extends Thread {
/**
* シェルコマンドからの出力を格納するバッファ
*/
private ByteArrayOutputStream buf = new ByteArrayOutputStream();
/**
* シェルコマンドからの出力を受け取るInputStream
*/
private InputStream in;
/**
* デフォルトコンストラクタの無効化
*/
private CommandOutputHandler() {
super();
}
/**
* コンストラクタ
* @param argIn シェルコマンドからの出力を受け取るInputStream
*/
private CommandOutputHandler(final InputStream argIn) {
super();
in = argIn;
}
/**
* @return cmdOutput を戻します。
*/
public String getCmdOutput() {
return buf.toString(); // ByteArrayOutpuStream#toString() は環境変数 LANG でエンコードする
}
/**
* シェルコマンドからの出力を読み取ります
*/
public void run() {
try {
byte[] tmp = new byte[128]; // 大きくしすぎるとバッファが詰まる
int size;
while((size = in.read) > 0) {
buf.write(tmp, 0, size);
}
} catch (IOException ex) {
ex = null; // なにもしないし、できない
} finally {
try {
in.close();
} catch (IOException ex) {
ex = null; // なにもしないし、できない
}
}
}
}
/**
* 起動したシェルコマンドが長い間終了しない場合に強制終了します.
* <pre>
* Apache Ant を参考に作成した。
* </pre>
*/
private final class WatchDog
extends Thread {
/**
* 実行中のプロセス
*/
private Process process;
/**
* Creates a new WatchDog object.
*
* @param param 実行予定のProcess
*/
public WatchDog(final Process param) {
process = param;
}
/**
* デフォルトコンストラクタの無効化
*/
private WatchDog() {
super();
}
/**
* 30000ミリ秒待ってまだプロセスが動いていれば、プロセスを強制終了します
*/
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();
}
}
}
}
+------------------+ +--------------+ |EJBアプリ-----------+--RMI-->+コマンド実行アプリ-+---> /bin/sh ls |EJBコンテナ | |RMIレジストリ | |JavaVM | |JavaVM | +------------------+ +--------------+ EJBはfork()されない fork()されるのコマンド実行アプリのみ