Java 9-Prozess-API-Verbesserungen

Java 9-Prozess-API-Verbesserungen

 

1. Überblick

Die Prozess-API in Java war vor Java 5 recht primitiv. Die einzige Möglichkeit, einen neuen Prozess zu erzeugen, bestand in der Verwendung derRuntime.getRuntime().exec()-API. Dann wurde in Java 5 die API vonProcessBuildereingeführt, die eine sauberere Methode zum Laichen neuer Prozesse unterstützte.

Java 9 bietet eine neue Möglichkeit, Informationen über aktuelle und alle hervorgerufenen Prozesse abzurufen.

In diesem Artikel werden wir uns beide Verbesserungen ansehen.

2. Aktuelle Java-Prozessinformationen

Über die APIjava.lang.ProcessHandle.Infoder API können wir jetzt viele Informationen über den Prozess erhalten:

  • der Befehl zum Starten des Prozesses

  • die Argumente des Befehls

  • Zeitpunkt, zu dem der Prozess gestartet wurde

  • Gesamtzeit, die von ihm und dem Benutzer, der es erstellt hat, verbracht wurde

So können wir das machen:

@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());
}

Es ist wichtig zu beachten, dassjava.lang.ProcessHandle.Info eine öffentliche Schnittstelle ist, die in einer anderen Schnittstellejava.lang.ProcessHandle definiert ist. Der JDK-Anbieter (Oracle JDK, Open JDK, Zulu oder andere) sollte Implementierungen für diese Schnittstellen so bereitstellen, dass diese Implementierungen die relevanten Informationen für die Prozesse zurückgeben.

3. Informationen zum erzeugten Prozess

Es ist auch möglich, die Prozessinformationen eines neu erstellten Prozesses abzurufen. In diesem Fall rufen wir, nachdem wir den Prozess erzeugt und eine Instanz vonjava.lang.Process abgerufen haben, die MethodetoHandle() auf, um eine Instanz vonjava.lang.ProcessHandle abzurufen.

Der Rest der Details bleibt derselbe wie im obigen Abschnitt:

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

4. Auflisten von Live-Prozessen im System

Wir können alle aktuell im System vorhandenen Prozesse auflisten, die für den aktuellen Prozess sichtbar sind. Die zurückgegebene Liste ist ein Snapshot zum Zeitpunkt des Aufrufs der API. Daher ist es möglich, dass einige Prozesse nach der Erstellung des Snapshots beendet wurden oder einige neue Prozesse hinzugefügt wurden.

Zu diesem Zweck können wir die statische MethodeallProcesses() verwenden, die in derjava.lang.ProcessHandle-Schnittstelle verfügbar ist und unsStream vonProcessHandle: zurückgibt

@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. Aufzählung untergeordneter Prozesse

Hierfür gibt es zwei Varianten:

  • Holen Sie sich direkte Kinder des aktuellen Prozesses

  • Holen Sie sich alle Nachkommen des aktuellen Prozesses

Ersteres wird unter Verwendung der Methodechildren() erreicht, und letzteres wird unter Verwendung der Methodedescendants() erreicht:

@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. Auslösen abhängiger Aktionen zur Prozessbeendigung

Möglicherweise möchten wir nach Beendigung des Prozesses etwas ausführen. Dies kann erreicht werden, indem dieonExit()-Methode in derjava.lang.ProcessHandle-Schnittstelle verwendet wird. Die Methode gibt uns einCompletableFuture zurück, das die Möglichkeit bietet, abhängige Operationen auszulösen, wennCompletableFuture abgeschlossen ist.

Hier zeigtCompletableFuture an, dass der Prozess abgeschlossen wurde, es spielt jedoch keine Rolle, ob der Prozess erfolgreich abgeschlossen wurde oder nicht. Wir rufen dieget()-Methode fürCompletableFuture auf, um auf ihren Abschluss zu warten:

@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());
    });
}

Die MethodeonExit() ist auch in der Schnittstellejava.lang.Processverfügbar.

7. Fazit

In diesem Tutorial haben wir interessante Ergänzungen derProcess-API in Java 9 behandelt, die uns viel mehr Kontrolle über die laufenden und erzeugten Prozesse geben.

Der in diesem Artikel verwendete Code lautetover on GitHub.