Рубрики
Без рубрики

Улучшения API процесса Java 9

Быстрое введение в интересные дополнения Java 9 к API процесса и как они дают нам гораздо больше контроля над запущенными и порожденными процессами.

Автор оригинала: baeldung.

1. Обзор

API процесса на Java был довольно примитивным до Java 5, единственный способ породить новый процесс — использовать Runtime.getRuntime ().exec() API. Затем на Java 5, ПроцессСтроитель Был введен API, который поддерживает более чистый способ нереста новых процессов.

Java 9 добавляет новый способ получения информации о текущих и любых нерестовых процессах.

В этой статье мы будем смотреть на оба эти усовершенствования.

2. Текущая информация о процессе Java

Теперь мы можем получить много информации о процессе через API java.lang.ProcessHandle.Info API:

  • команда, используемая для запуска процесса
  • аргументы команды
  • момент времени, когда процесс был начат
  • общее время, затраченные им и пользователем, который создал его

Вот как мы можем это сделать:

@Test
public void givenCurrentProcess_whenInvokeGetInfo_thenSuccess() 
  throws IOException {
 
    ProcessHandle processHandle = ProcessHandle.current();
    ProcessHandle.Info processInfo = processHandle.info();
 
    assertNotNull(processHandle.getPid());
    assertEquals(true, processInfo.arguments().isPresent());
    assertEquals(true, processInfo.command().isPresent());
    assertTrue(processInfo.command().get().contains("java"));
    assertEquals(true, processInfo.startInstant().isPresent());
    assertEquals(false, 
      processInfo.totalCpuDuration().isPresent());
    assertEquals(true, processInfo.user().isPresent());
}

Важно отметить, что java.lang.ProcessHandle.Info это общедоступный интерфейс, определенный в другом интерфейсе java.lang.ProcessHandle . Поставщик JDK (Oracle JDK, Open JDK, Зулу или другие) должен предоставлять реализации этих интерфейсов таким образом, чтобы эти реализации вернули соответствующую информацию для процессов.

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, поэтому возможно, что некоторые процессы были прекращены после получения снимка или добавлены некоторые новые процессы.

Для этого мы можем использовать статический метод allProcesses () доступны в java.lang.ProcessHandle интерфейс, который возвращает нам Поток ProcessHandle:

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

5. Перечисление детских процессов

Для этого есть два варианта:

  • получить прямых детей текущего процесса
  • получить все потомки текущего процесса

Первый достигается с помощью метода детей () и последнее достигается с помощью метода потомки () :

@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. Инициирование зависимых действий по прекращению процесса

Возможно, мы захим запустить что-то о прекращении процесса. Этого можно достичь с помощью onExit () метод в java.lang.ProcessHandle интерфейс. Метод возвращает нам КомплируемыеФутурные которая обеспечивает возможность запуска зависимых операций при КомплируемыеФутурные завершена.

Вот, КомплируемыеФутурные указывает на то, что процесс завершен, но не имеет значения, успешно завершен процесс или нет. Мы ссылаемся на получить () метод на КомплируемыеФутурные , чтобы дождаться его завершения:

@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. Заключение

В этом учебнике мы рассмотрели интересные дополнения к Процесс API в Java 9, которые дают нам гораздо больше контроля над запущенными и порожденными процессами.

Код, используемый в этой статье, можно найти более на GitHub .