Apache common execパッケージの対応する使用概要
7894 ワード
最近Javaツールを書いていますが、様々なSHELLコマンドが呼び出され、Runtime.getRuntime().exec(command)が使用されています.この方法.しかし、私があるコマンドを呼び出して操作を実行すると、プログラムはそこに詰まっていたが、他の操作は正常に出力することができ、検索と模索を経て、Javaがコマンドを実行するときにあるBufferに出力したことが分かった.このBufferは容量制限があり、満タンになっても誰も読み取らないと、ずっと待っていて、プロセスがロックされる現象をもたらした.これにより、実行中にいくつかのスレッドを新たに開き、標準出力とエラー出力を絶えず読み出すことができます.
final Process p = Runtime.getRuntime().exec(command);
new Thread(new Runnable() {
@Override
public void run() {
while (true){
try {
p.exitValue();
break;
} catch (Exception e){
showInfo(System.err,p.getErrorStream());
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (true){
try {
p.exitValue();
break;
} catch (Exception e){
showInfo(System.out,p.getInputStream());
}
}
}
}).start();
int exitValue = p.waitFor();
waitForの実行が完了する前に、エラー出力と標準は別々に処理されることに注意してください.
Apache commons-execは外部プロセスを実行するための一般的な方法を提供し、Apache commons execライブラリは監視犬Watchdogを提供して監視プロセスの実行タイムアウトを設定すると同時に、同期と非同期機能を実現し、Apache commonsexecはマルチスレッドに関連し、例えば新しいプロセスを起動し、Javaではプロセスの3つのデータストリームを処理するために3つのスレッドを再開する必要がある.それぞれ標準入力、標準出力、エラー出力です.
この機能を使用するにはcommons-exec-1.3.jarパッケージを導入する必要があります.現在の最新バージョンは1.3バージョンです.
Apache commons-execの公式サイト:http://commons.apache.org/proper/commons-exec/
ここでは、ツールを使用してshellコマンドを実行する方法について説明するExampleの例があります.コマンドに関するコードを実行します.
final Long executeSubTaskId = subTaskExecuteContext.getSubTaskExecuteId();
final Long taskStatusId = subTaskExecuteContext.getTaskExecuteContext().getTaskExecuteId();
ByteArrayOutputStream outputStream =
new MzByteArrayOutputStream(executeSubTaskId, taskStatusNotifyCenter, true);
ByteArrayOutputStream errorStream =
new MzByteArrayOutputStream(executeSubTaskId, taskStatusNotifyCenter, false);
PumpStreamHandler streamHandler = new PumpStreamHandler(outputStream, errorStream);
taskThreadPoolExecutor.setStreamHandler(streamHandler);
CommandLine commandLine = new CommandLine(new File(executeShellPath));
final TaskProcessInfo taskProcessInfo = new TaskProcessInfo(subTaskExecuteContext.getTaskExecuteContext().getTaskId(),
taskStatusId,
executeSubTaskId);
ProcessManagerDestroyer processManagerDestroyer =
new ProcessManagerDestroyer(
taskProcessInfo, processInfoManager);
taskThreadPoolExecutor.setProcessDestroyer(processManagerDestroyer);
try {
taskThreadPoolExecutor.execute(commandLine, new DefaultExecuteResultHandler() {
@Override
public void onProcessComplete(int exitValue) {
super.onProcessComplete(exitValue);
LOG.info(String.format("Task Process info: %s succeed!", taskProcessInfo));
taskStatusNotifyCenter.notifyEventChanged(new TaskStatusEvent(TaskStatusEventType.TASK_FINISHED,
new TaskEventObject(subTaskExecuteContext.getTaskExecuteContext().getTaskId(),
subTaskExecuteContext.getTaskExecuteContext().getTaskExecuteId(),
subTaskExecuteContext.getSubTaskExecuteId())));
}
@Override
public void onProcessFailed(ExecuteException e) {
super.onProcessFailed(e);
LOG.error(e);
LOG.error(String.format("Task Process info: %s failed!", taskProcessInfo));
taskStatusNotifyCenter.notifyEventChanged(new TaskStatusEvent(TaskStatusEventType.TASK_FAILED,
new TaskEventObject(subTaskExecuteContext.getTaskExecuteContext().getTaskId(),
subTaskExecuteContext.getTaskExecuteContext().getTaskExecuteId(),
subTaskExecuteContext.getSubTaskExecuteId())));
}
});
} catch (IOException e) {
throw new BusinessException(e);
}
新しいプロセスを実行するにはnewの2つのByteArrayOutputStreamが必要です.1つは標準出力ストリームを記録するために使用され、1つはエラー出力ストリームを記録するために使用されます.ByteArrayOutputStreamの内容をタイムリーにクリーンアップするには、出力ストリームを選択的に書き換えることができます.
@Override
public synchronized void write(byte[] b, int off, int len) {
super.write(b, off, len);
writeTimes++;
writeLength += len;
if (writeLength >= MAX_WRITE_LENGTH || writeTimes >= MAX_WRITE_TIMES) {
updateStatus();
this.buf = new byte[32];
writeLength = 0;
writeTimes = 0;
}
}
@Override
public void flush() throws IOException {
super.flush();
updateStatus();
}
確立されたProcessManagerDestroyerは、タスクの作成時またはタスクの完了時に、タスクの現在の記録ステータスに使用されます.
public class ProcessManagerDestroyer implements ProcessDestroyer {
private final ProcessInfoManager processInfoManager;
private final TaskProcessInfo taskProcessInfo;
public ProcessManagerDestroyer(TaskProcessInfo taskProcessInfo, ProcessInfoManager processInfoManager) {
this.taskProcessInfo = taskProcessInfo;
this.processInfoManager = processInfoManager;
}
@Override
public boolean add(Process process) {
processInfoManager.addProcess(taskProcessInfo, process);
return true;
}
@Override
public boolean remove(Process process) {
processInfoManager.removeProcess(taskProcessInfo);
return true;
}
@Override
public int size() {
return processInfoManager.taskCount();
}
Destroyerで新しいProcessを保存し、後でdestroyメソッドを呼び出してkillを削除します.
process.destroy();
最後に作成されたD e f a ultExecuteResultHandlerリスナーは、タスクの実行が完了したり、エラーが発生したりしたときに、対応する情報を提示し、イベントを送信するために使用されます.
shellを実行するときに発生した問題は、初歩的には実行する権限がありませんか?
org.apache.commons.exec.ExecuteException: Execution failed (Exit value: -559038737. Caused by java.io.IOException: Cannot run program "/Users/mazhiqiang/Downloads/1.sh" (in directory "."): error=13, Permission denied)
at org.apache.commons.exec.DefaultExecutor$1.run(DefaultExecutor.java:205)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.io.IOException: Cannot run program "/Users/xxx/Downloads/1.sh" (in directory "."): error=13, Permission denied
at java.lang.ProcessBuilder.start(ProcessBuilder.java:1042)
at java.lang.Runtime.exec(Runtime.java:620)
at org.apache.commons.exec.launcher.Java13CommandLauncher.exec(Java13CommandLauncher.java:61)
at org.apache.commons.exec.DefaultExecutor.launch(DefaultExecutor.java:279)
at org.apache.commons.exec.DefaultExecutor.executeInternal(DefaultExecutor.java:336)
at org.apache.commons.exec.DefaultExecutor.access$200(DefaultExecutor.java:48)
at org.apache.commons.exec.DefaultExecutor$1.run(DefaultExecutor.java:200)
... 1 more
Caused by: java.io.IOException: error=13, Permission denied
at java.lang.UNIXProcess.forkAndExec(Native Method)
at java.lang.UNIXProcess.(UNIXProcess.java:185)
at java.lang.ProcessImpl.start(ProcessImpl.java:134)
at java.lang.ProcessBuilder.start(ProcessBuilder.java:1023)
... 7 more
最終的に、生成されたshellファイルに実行可能ファイルのexecutableプロパティとshellコマンドファイルヘッダが追加されていないことがわかりました.
file.executable()
!/bin/bash
応用中に後ろに穴がたくさんあって、私たちが記入するのを待っています.
転載先:https://www.cnblogs.com/mmaa/p/5789887.html