Java 9プロセスAPIの改良

Java 9プロセスAPIの改善

 

1. 概要

JavaのプロセスAPIは、Java 5より前は非常に原始的でした。新しいプロセスを生成する唯一の方法は、Runtime.getRuntime().exec()APIを使用することでした。 次に、Java 5で、新しいプロセスを生成するよりクリーンな方法をサポートするProcessBuilderAPIが導入されました。

Java 9は、現在および生成されたプロセスに関する情報を取得する新しい方法を追加しています。

この記事では、これらの両方の機能強化について説明します。

2. 現在のJavaプロセス情報

これで、APIjava.lang.ProcessHandle.InfoAPIを介してプロセスに関する多くの情報を取得できます。

  • プロセスを開始するために使用されるコマンド

  • コマンドの引数

  • プロセスが開始された時刻

  • それとそれを作成したユーザーが費やした合計時間

これを行う方法は次のとおりです。

@Test
public void givenCurrentProcess_whenInvokeGetInfo_thenSuccess()
  throws IOException {

    ProcessHandle processHandle = ProcessHandle.current();
    ProcessHandle.Info processInfo = processHandle.info();

    assertNotNull(processHandle.getPid());
    assertEquals(false, processInfo.arguments().isPresent());
    assertEquals(true, processInfo.command().isPresent());
    assertTrue(processInfo.command().get().contains("java"));
    assertEquals(true, processInfo.startInstant().isPresent());
    assertEquals(true,
      processInfo.totalCpuDuration().isPresent());
    assertEquals(true, processInfo.user().isPresent());
}

java.lang.ProcessHandle.Infoは、別のインターフェイスjava.lang.ProcessHandle内で定義されたパブリックインターフェイスであることに注意することが重要です。 JDKプロバイダー(Oracle JDK、Open JDK、Zuluなど)は、これらの実装がプロセスに関連する情報を返すように、これらのインターフェースに実装を提供する必要があります。

3. 生成されたプロセス情報

新しく生成されたプロセスのプロセス情報を取得することもできます。 この場合、プロセスを生成してjava.lang.Processのインスタンスを取得した後、そのプロセスでtoHandle()メソッドを呼び出して、java.lang.ProcessHandleのインスタンスを取得します。

残りの詳細は、上記のセクションと同じままです。

String javaCmd = ProcessUtils.getJavaCmd().getAbsolutePath();
ProcessBuilder processBuilder = new ProcessBuilder(javaCmd, "-version");
Process process = processBuilder.inheritIO().start();
ProcessHandle processHandle = process.toHandle();

4. システム内のライブプロセスの列挙

現在システムにあるすべてのプロセスを一覧表示できます。これらのプロセスは、現在のプロセスに表示されます。 返されるリストは、APIが呼び出されたときのスナップショットであるため、スナップショットを取得した後に一部のプロセスが終了したか、一部の新しいプロセスが追加された可能性があります。

これを行うには、ProcessHandle:Streamを返すjava.lang.ProcessHandleインターフェイスで使用可能な静的メソッドallProcesses()を使用できます。

@Test
public void givenLiveProcesses_whenInvokeGetInfo_thenSuccess() {
    Stream liveProcesses = ProcessHandle.allProcesses();
    liveProcesses.filter(ProcessHandle::isAlive)
      .forEach(ph -> {

        assertNotNull(ph.getPid());
        assertEquals(true, ph.info()
          .command()
          .isPresent());
      });
}

5. 子プロセスの列挙

これを行うには2つのバリアントがあります。

  • 現在のプロセスの直接の子を取得する

  • 現在のプロセスのすべての子孫を取得する

前者はメソッドchildren()を使用して実現され、後者はメソッドdescendants()を使用して実現されます。

@Test
public void givenProcess_whenGetChildProcess_thenSuccess()
  throws IOException{

    int childProcessCount = 5;
    for (int i = 0; i < childProcessCount; i++){
        String javaCmd = ProcessUtils.getJavaCmd()
          .getAbsolutePath();
        ProcessBuilder processBuilder
          = new ProcessBuilder(javaCmd, "-version");
        processBuilder.inheritIO().start();
    }
    Stream children
      = ProcessHandle.current().children();

    children.filter(ProcessHandle::isAlive)
      .forEach(ph -> log.info("PID: {}, Cmd: {}",
        ph.getPid(), ph.info().command()));

    // and for descendants
    Stream descendants
      = ProcessHandle.current().descendants();
    descendants.filter(ProcessHandle::isAlive)
      .forEach(ph -> log.info("PID: {}, Cmd: {}",
        ph.getPid(), ph.info().command()));
}

6. プロセス終了時の依存アクションのトリガー

プロセスの終了時に何かを実行したい場合があります。 これは、java.lang.ProcessHandleインターフェイスでonExit()メソッドを使用することで実現できます。 このメソッドはCompletableFutureを返します。これは、CompletableFutureが完了したときに依存操作をトリガーする機能を提供します。

ここで、CompletableFutureはプロセスが完了したことを示しますが、プロセスが正常に完了したかどうかは関係ありません。 CompletableFutureget()メソッドを呼び出し、その完了を待ちます。

@Test
public void givenProcess_whenAddExitCallback_thenSuccess()
  throws Exception {

    String javaCmd = ProcessUtils.getJavaCmd()
      .getAbsolutePath();
    ProcessBuilder processBuilder
      = new ProcessBuilder(javaCmd, "-version");
    Process process = processBuilder.inheritIO()
      .start();
    ProcessHandle processHandle = process.toHandle();

    log.info("PID: {} has started", processHandle.getPid());
    CompletableFuture onProcessExit
      = processHandle.onExit();
    onProcessExit.get();
    assertEquals(false, processHandle.isAlive());
    onProcessExit.thenAccept(ph -> {
        log.info("PID: {} has stopped", ph.getPid());
    });
}

onExit()メソッドは、java.lang.Processインターフェイスでも使用できます。

7. 結論

このチュートリアルでは、Java 9のProcess APIに追加された興味深い機能について説明しました。これにより、実行中のプロセスと生成されたプロセスをより細かく制御できます。

この記事で使用されているコードはover on GitHubにあります。