Améliorations de l’API Java 9 Process

Améliorations de l'API Java 9 Process

 

1. Vue d'ensemble

L'API de processus en Java était assez primitive avant Java 5, la seule façon de générer un nouveau processus était d'utiliser l'APIRuntime.getRuntime().exec(). Ensuite, dans Java 5, l'APIProcessBuilder a été introduite, qui prend en charge une manière plus propre de générer de nouveaux processus.

Java 9 ajoute une nouvelle façon d'obtenir des informations sur les processus actuels et les processus générés.

Dans cet article, nous examinerons ces deux améliorations.

2. Informations sur le processus Java actuel

Nous pouvons désormais obtenir de nombreuses informations sur le processus via l'API APIjava.lang.ProcessHandle.Info:

  • la commande utilisée pour démarrer le processus

  • les arguments de la commande

  • instant instantané du début du processus

  • temps total passé par l'utilisateur et l'utilisateur qui l'a créé

Voici comment nous pouvons y parvenir:

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

Il est important de noter quejava.lang.ProcessHandle.Info est une interface publique définie dans une autre interfacejava.lang.ProcessHandle. Le fournisseur JDK (Oracle JDK, Open JDK, Zulu ou autres) doit fournir des implémentations à ces interfaces de manière à ce que ces implémentations renvoient les informations pertinentes pour les processus.

3. Informations sur le processus généré

Il est également possible d'obtenir les informations de processus d'un processus nouvellement généré. Dans ce cas, après avoir engendré le processus et obtenu une instance dejava.lang.Process, nous invoquons la méthodetoHandle() dessus pour obtenir une instance dejava.lang.ProcessHandle.

Le reste des détails reste le même que dans la section ci-dessus:

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

4. Énumération des processus en direct dans le système

Nous pouvons répertorier tous les processus actuellement dans le système, visibles par le processus en cours. La liste renvoyée est un instantané au moment où l'API a été appelée. Il est donc possible que certains processus se soient arrêtés après avoir pris l'instantané ou que de nouveaux processus aient été ajoutés.

Pour ce faire, nous pouvons utiliser la méthode statiqueallProcesses() disponible dans l'interfacejava.lang.ProcessHandle qui nous renvoie unStream deProcessHandle:

@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. Énumération des processus enfants

Il existe deux variantes pour cela:

  • obtenir des enfants directs du processus en cours

  • obtenir tous les descendants du processus en cours

La première est obtenue en utilisant la méthodechildren() et la seconde est réalisée en utilisant la méthodedescendants():

@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. Déclenchement d'actions dépendantes lors de l'arrêt du processus

Nous voudrons peut-être lancer quelque chose à la fin du processus. Ceci peut être réalisé en utilisant la méthodeonExit() dans l'interfacejava.lang.ProcessHandle. La méthode nous renvoie unCompletableFuture qui offre la possibilité de déclencher des opérations dépendantes lorsque leCompletableFuture est terminé.

Ici, leCompletableFuture indique que le processus est terminé, mais peu importe si le processus s'est terminé avec succès ou non. Nous invoquons la méthodeget() sur leCompletableFuture, pour attendre son achèvement:

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

La méthodeonExit() est également disponible dans l'interfacejava.lang.Process.

7. Conclusion

Dans ce didacticiel, nous avons abordé des ajouts intéressants à l'APIProcess de Java 9 qui nous donnent beaucoup plus de contrôle sur les processus en cours d'exécution et générés.

Le code utilisé dans cet article se trouveover on GitHub.