Руководство по java.lang.Process API

Руководство по API java.lang.Process

1. Вступление

В этом руководстве мы возьмемan in-depth look at the Process API.

Для более подробного изучения того, как использоватьProcess для выполнения команды оболочки, мы можем обратиться к нашему предыдущему руководствуhere.

Процесс, к которому он относится, является исполняемым приложением. КлассProcess предоставляет методы для взаимодействия с этими процессами, включая извлечение вывода, выполнение ввода, мониторинг жизненного цикла, проверку статуса выхода и его уничтожение (уничтожение).

2. Использование классаProcess для компиляции и запуска Java-программы

Давайте посмотрим на пример компиляции и запуска другой программы Java с помощью APIProcess:

@Test
public void whenExecutedFromAnotherProgram_thenSourceProgramOutput3() throws IOException {

    Process process = Runtime.getRuntime()
      .exec("javac -cp src src\\main\\java\\com\\example\\java9\\process\\OutputStreamExample.java");
    process = Runtime.getRuntime()
      .exec("java -cp src/main/java com.example.java9.process.OutputStreamExample");
    BufferedReader output = new BufferedReader(new InputStreamReader(process.getInputStream()));
    int value = Integer.parseInt(output.readLine());

    assertEquals(3, value);
}

Таким образом, приложения для выполнения кода Java в существующем коде Java практически безграничны.

3. Создание процесса

Наше Java-приложение может вызывать любое приложение, которое работает в нашей компьютерной системе, в зависимости от ограничений операционной системы.

Поэтому мы можем выполнять приложения. Давайте посмотрим, какие варианты использования мы можем запустить с помощью Process API.

КлассProcessBuilder позволяет нам создавать подпроцессы в нашем приложении.

Давайте посмотрим на демонстрацию открытия приложения Блокнот для Windows:

ProcessBuilder builder = new ProcessBuilder("notepad.exe");
Process process = builder.start();

4. Уничтожающий процесс

Process также предоставляет нам методы для уничтожения подпроцессов или процессов. Although, how the application is killed is platform-dependent.

Давайте посмотрим на возможные варианты использования.

4.1. Уничтожение процесса по ссылке

Допустим, мы используем ОС Windows и хотим создать приложение «Блокнот» и уничтожить его.

Как и раньше, мы можем создать экземпляр приложения «Блокнот», используя классProcessBuilder и методstart().

Затем мы можем вызвать методdestroy() для нашего объектаProcess.

4.2. Уничтожение процесса по идентификатору

Мы также можем уничтожать процессы, которые выполняются в нашей операционной системе, которые могут не быть созданы нашим приложением.

Caution should be advised while doing this, as we can unknowingly destroy a critical process which might make the operating system unstable.

Сначала нам нужно узнать идентификатор текущего запущенного процесса, проверив диспетчер задач и выяснив pid.

Давайте посмотрим на пример:

Optional optionalProcessHandle = ProcessHandle.of(5232);
optionalProcessHandle.ifPresent(processHandle -> processHandle.destroy());

4.3. Разрушение процесса силой

При выполнении методаdestroy() подпроцесс будет остановлен, как мы видели ранее в статье.

В случае, когдаdestroy() не работает, у нас есть опцияdestroyForcibly().

Мы всегда должны начинать с методаdestroy(). После этого мы можем выполнить быструю проверку подпроцесса, выполнивisAlive().

Если он вернет истину, выполнитеdestroyForcibly():

ProcessBuilder builder = new ProcessBuilder("notepad.exe");
Process process = builder.start();
process.destroy();
if (process.isAlive()) {
    process.destroyForcibly();
}

5. Ожидание завершения процесса

У нас также есть два перегруженных метода, с помощью которых мы можем обеспечить ожидание завершения процесса.

5.1. waitfor()с

Когда этот метод выполняется, он помещаетthe current execution process thread in a blocking-wait state unless the sub-process gets terminated.

Давайте посмотрим на пример:

ProcessBuilder builder = new ProcessBuilder("notepad.exe");
Process process = builder.start();
assertThat(process.waitFor() >= 0);

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

5.2. waitfor(long timeOut, TimeUnit time)с

Когда этот метод выполняется, он помещаетthe current execution process thread in the blocking-wait state unless the sub-process gets terminated or runs out of time.

Давайте посмотрим на пример:

ProcessBuilder builder = new ProcessBuilder("notepad.exe");
Process process = builder.start();
assertFalse(process.waitFor(1, TimeUnit.SECONDS));

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

Когда этот метод выполняется, он возвращает логическое значение true, если подпроцесс завершился, или логическое значение false, если время ожидания истекло до выхода из подпроцесса.

6. exitValue()с

Когда этот метод запущен, текущий поток не будет ждать завершения или уничтожения подпроцесса, однако он выдастIllegalThreadStateException, если подпроцесс не завершен.

Another way around if the subprocess has been successfully terminated then it will result in an exit value of the process.

Это может быть любое возможное положительное целое число.

Давайте посмотрим на пример, когда метод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. Обработка потоков процессов

По умолчанию созданный подпроцесс не имеет своего терминала или консоли. Все его стандартные операции ввода-вывода (т.е. операции 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\\example\\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\\example\\java9\\process\\OutputStreamExample.java");
    process = Runtime.getRuntime()
      .exec("java -cp  src/main/java com.example.java9.process.OutputStreamExample");
    BufferedReader output = new BufferedReader(new InputStreamReader(process.getInputStream()));
    int value = Integer.parseInt(output.readLine());

    assertEquals(3, value);
}

8.3. getOutputStream()

Мы можем отправить входные данные подпроцессу из родительского процесса:

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 ознакомьтесь с нашей статьейhere.

Как всегда, вы найдете источникиover on Github.