java.lang.ProcessAPIのガイド
1. 序章
このチュートリアルでは、プロセスAPIについて詳しく見ていきます。
Process を使用してシェルコマンドを実行する方法の詳細については、前のチュートリアルここを参照してください。
それが参照するプロセスは、実行中のアプリケーションです。 Process クラスは、出力の抽出、入力の実行、ライフサイクルの監視、終了ステータスの確認、破棄(強制終了)など、これらのプロセスと対話するためのメソッドを提供します。
2. Processクラスを使用したJavaプログラムのコンパイルと実行
Process APIを使用して、別のJavaプログラムをコンパイルして実行する例を見てみましょう。
@Test
public void whenExecutedFromAnotherProgram_thenSourceProgramOutput3() throws IOException {
Process process = Runtime.getRuntime()
.exec("javac -cp src src\\main\\java\\com\\baeldung\\java9\\process\\OutputStreamExample.java");
process = Runtime.getRuntime()
.exec("java -cp src/main/java com.baeldung.java9.process.OutputStreamExample");
BufferedReader output = new BufferedReader(new InputStreamReader(process.getInputStream()));
int value = Integer.parseInt(output.readLine());
assertEquals(3, value);
}
したがって、既存のJavaコード内でJavaコードを実行するアプリケーションは、事実上無制限です。
3. プロセスの作成
私たちのJavaアプリケーションは、オペレーティングシステムの制限に従って、コンピュータシステム内で実行されている任意のアプリケーションを呼び出すことができます。
したがって、アプリケーションを実行できます。 ProcessAPIを利用して実行できるさまざまなユースケースを見てみましょう。
ProcessBuilder クラスを使用すると、アプリケーション内にサブプロセスを作成できます。
Windowsベースのメモ帳アプリケーションを開くデモを見てみましょう。
ProcessBuilder builder = new ProcessBuilder("notepad.exe");
Process process = builder.start();
4. 破壊プロセス
Process は、サブプロセスまたはプロセスを破棄するメソッドも提供します。 ただし、アプリケーションを強制終了する方法はプラットフォームによって異なります。
可能なさまざまなユースケースを見てみましょう。
4.1. 参照によるプロセスの破棄
Windows OSを使用していて、メモ帳アプリケーションを生成して破棄したいとします。
以前と同様に、 ProcessBuilderクラスとstart()メソッドを使用して、メモ帳アプリケーションのインスタンスを作成できます。
次に、 Processオブジェクトでdestroy()メソッドを呼び出すことができます。
4.2. IDによるプロセスの破棄
また、アプリケーションによって作成されていない可能性のある、オペレーティングシステム内で実行されているプロセスを強制終了することもできます。
オペレーティングシステムを不安定にする可能性のある重要なプロセスを無意識のうちに破壊する可能性があるため、これを行う際には注意が必要です。
まず、タスクマネージャーをチェックして現在実行中のプロセスのプロセスIDを確認し、pidを確認する必要があります。
例を見てみましょう:
long pid = /* PID to kill */;
Optional<ProcessHandle> optionalProcessHandle = ProcessHandle.of(pid);
optionalProcessHandle.ifPresent(processHandle -> processHandle.destroy());
4.3. プロセスを強制的に破壊する
destroy()メソッドを実行すると、記事の前半で見たように、サブプロセスが強制終了されます。
destroy()が機能しない場合は、 destroyForcibly()のオプションがあります。
常に最初にdestroy()メソッドから始める必要があります。 その後、 isAlive()を実行することにより、サブプロセスのクイックチェックを実行できます。
trueが返された場合は、 destroyForcibly()を実行します。
ProcessBuilder builder = new ProcessBuilder("notepad.exe");
Process process = builder.start();
process.destroy();
if (process.isAlive()) {
process.destroyForcibly();
}
5. プロセスが完了するのを待っています
また、2つのオーバーロードされたメソッドがあり、プロセスの完了を確実に待つことができます。
5.1. waitfor()
このメソッドが実行されると、サブプロセスが終了しない限り、現在の実行プロセススレッドがブロッキング待機状態になります。
例を見てみましょう:
ProcessBuilder builder = new ProcessBuilder("notepad.exe");
Process process = builder.start();
assertThat(process.waitFor() >= 0);
上記の例から、現在のスレッドが実行を継続し、サブプロセススレッドが終了するのを待ち続けることがわかります。 サブプロセスが終了すると、現在のスレッドは実行を継続します。
5.2. waitfor(long timeOut、TimeUnit time)
このメソッドが実行されると、サブプロセスが終了するか時間切れにならない限り、現在の実行プロセススレッドがブロッキング待機状態になります。
例を見てみましょう:
ProcessBuilder builder = new ProcessBuilder("notepad.exe");
Process process = builder.start();
assertFalse(process.waitFor(1, TimeUnit.SECONDS));
上記の例から、現在のスレッドが実行を継続するか、サブプロセススレッドが終了するか、指定された時間間隔が経過したかどうかを待機し続けることがわかります。
このメソッドを実行すると、サブプロセスが終了した場合はブール値trueが返され、サブプロセスが終了する前に待機時間が経過した場合はブール値falseが返されます。
6. exitValue()
このメソッドが実行されると、現在のスレッドはサブプロセスが終了または破棄されるのを待ちませんが、サブプロセスが終了しない場合はIllegalThreadStateExceptionをスローします。
別の方法で、サブプロセスが正常に終了した場合、プロセスの終了値になります。
任意の正の整数にすることができます。
サブプロセスが正常に終了したときにexitValue()メソッドが正の整数を返す例を見てみましょう。
@Test
public void
givenSubProcess_whenCurrentThreadWillNotWaitIndefinitelyforSubProcessToEnd_thenProcessExitValueReturnsGrt0()
throws IOException {
ProcessBuilder builder = new ProcessBuilder("notepad.exe");
Process process = builder.start();
assertThat(process.exitValue() >= 0);
}
7. isAlive()
プロセスが生きているかどうかにかかわらず主観的なビジネス処理を実行したい場合。
ブール値を返すプロセスが有効であるかどうかを確認するためのクイックチェックを実行できます。
その簡単な例を見てみましょう。
ProcessBuilder builder = new ProcessBuilder("notepad.exe");
Process process = builder.start();
Thread.sleep(10000);
process.destroy();
assertTrue(process.isAlive());
8. プロセスストリームの処理
デフォルトでは、作成されたサブプロセスにはターミナルまたはコンソールがありません。 そのすべての標準I/O(つまり、stdin、stdout、stderr)操作は、親プロセスに送信されます。 これにより、親プロセスはこれらのストリームを使用して、サブプロセスに入力を供給し、サブプロセスから出力を取得できます。
その結果、サブプロセスの入力/出力を制御できるため、非常に柔軟に対応できます。
8.1. getErrorStream()
興味深いことに、サブプロセスから生成されたエラーをフェッチして、ビジネス処理を実行できます。
その後、要件に基づいて特定のビジネス処理チェックを実行できます。
例を見てみましょう:
@Test
public void givenSubProcess_whenEncounterError_thenErrorStreamNotNull() throws IOException {
Process process = Runtime.getRuntime().exec(
"javac -cp src src\\main\\java\\com\\baeldung\\java9\\process\\ProcessCompilationError.java");
BufferedReader error = new BufferedReader(new InputStreamReader(process.getErrorStream()));
String errorString = error.readLine();
assertNotNull(errorString);
}
8.2. getInputStream()
サブプロセスによって生成された出力をフェッチして親プロセス内で消費することもできるため、プロセス間で情報を共有できます。
@Test
public void givenSourceProgram_whenReadingInputStream_thenFirstLineEquals3() throws IOException {
Process process = Runtime.getRuntime().exec(
"javac -cp src src\\main\\java\\com\\baeldung\\java9\\process\\OutputStreamExample.java");
process = Runtime.getRuntime()
.exec("java -cp src/main/java com.baeldung.java9.process.OutputStreamExample");
BufferedReader output = new BufferedReader(new InputStreamReader(process.getInputStream()));
int value = Integer.parseInt(output.readLine());
assertEquals(3, value);
}
8.3. ge tOutputStream()
親プロセスからサブプロセスに入力を送信できます。
Writer w = new OutputStreamWriter(process.getOutputStream(), "UTF-8");
w.write("send to child\n");
8.4. プロセスストリームのフィルタリング
これは、選択的に実行されているプロセスと対話するための完全に有効なユースケースです。
Process は、特定の述語に基づいて実行中のプロセスを選択的にフィルタリングする機能を提供します。
その後、この選択的なプロセスセットでビジネスオペレーションを実行できます。
@Test
public void givenRunningProcesses_whenFilterOnProcessIdRange_thenGetSelectedProcessPid() {
assertThat(((int) ProcessHandle.allProcesses()
.filter(ph -> (ph.pid() > 10000 && ph.pid() < 50000))
.count()) > 0);
}
9. 結論
Process は、オペレーティングシステムレベルの対話のための強力なクラスです。 ターミナルコマンドのトリガー、およびアプリケーションの起動、監視、強制終了。
Java 9 Process APIの詳細については、記事こちらをご覧ください。
いつものように、ソースはGithubにあります。