1. Introduction
In this tutorial, we’re going to take an in-depth look at the Process API.
For a shallower look into how to use Process to execute a shell command, we can refer to our previous tutorial here.
The process that it refers to is an executing application. The Process class provides methods for interacting with these processes including extracting output, performing input, monitoring the lifecycle, checking the exit status, and destroying (killing) it.
2. Using Process Class for Compiling and Running Java Program
Let’s see an example to compile and run another Java program with the help of Process API:
@Test
public void whenExecutedFromAnotherProgram_thenSourceProgramOutput3() throws IOException {
Process process = Runtime.getRuntime()
.exec("javac -cp src src\\main\\java\\com\\baeldung\\java9\\process\\OutputStreamExample.java");
process = Runtime.getRuntime()
.exec("java -cp src/main/java com.baeldung.java9.process.OutputStreamExample");
BufferedReader output = new BufferedReader(new InputStreamReader(process.getInputStream()));
int value = Integer.parseInt(output.readLine());
assertEquals(3, value);
}
Thus, the applications of executing Java code within an existing Java code is virtually limitless.
3. Creating Process
Our Java application can call upon any application which is running within our computer system subjective to Operating System restrictions.
Therefore we can execute applications. Let’s see what the different use cases we can run by utilizing the Process API are.
The ProcessBuilder class allows us to create subprocesses within our application.
Let’s see a demo of opening Windows-based Notepad application:
ProcessBuilder builder = new ProcessBuilder("notepad.exe");
Process process = builder.start();
4. Destroying Process
Process also provides us with methods to destroy sub-processes or process. Although, how the application is killed is platform-dependent.
Let’s see different use cases by which are possible.
4.1. Destroying a Process by Reference
Let’s say we’re using Windows OS and want to spawn the Notepad application and destroy it.
As before, we can create an instance of Notepad application by using the ProcessBuilder class and the start() method.
Then we can call the destroy() method on our Process object.
4.2. Destroying a Process by ID
We can also kill processes which are running within our Operating System that might not be created by our application.
Caution should be advised while doing this, as we can unknowingly destroy a critical process that might make the operating system unstable.
We first need to find out the process ID of the current running process by checking the task manager and find out the pid.
Let’s see an example:
long pid = /* PID to kill */;
Optional<ProcessHandle> optionalProcessHandle = ProcessHandle.of(pid);
optionalProcessHandle.ifPresent(processHandle -> processHandle.destroy());
4.3. Destroying a Process by Force
On the execution of the destroy() method, the subprocess will get killed as we saw earlier in the article.
*In the case when destroy() doesn’t work, we have the option of destroyForcibly().*
We should always start with destroy() method first. After that, we can perform a quick check on the sub-process whether by executing isAlive().
If it returns true then execute destroyForcibly():
ProcessBuilder builder = new ProcessBuilder("notepad.exe");
Process process = builder.start();
process.destroy();
if (process.isAlive()) {
process.destroyForcibly();
}
5. Waiting for a Process to Complete
We also have two overloaded methods, through which we can ensure we can wait for completion of a process.
5.1. waitfor()
When this method is executed, then it will place the current execution process thread in a blocking-wait state unless the sub-process gets terminated.
Let’s take a look at the example:
ProcessBuilder builder = new ProcessBuilder("notepad.exe");
Process process = builder.start();
assertThat(process.waitFor() >= 0);
We can see from the above example for the current thread to continue execution it will keep on waiting for the subprocess thread to end. Once the subprocess ends, the current thread will continue its execution.
5.2. waitfor(long timeOut, TimeUnit time)
When this method is executed, then it will place the current execution process thread in the blocking-wait state unless the sub-process gets terminated or runs out of time.
Let’s take a look at the example:
ProcessBuilder builder = new ProcessBuilder("notepad.exe");
Process process = builder.start();
assertFalse(process.waitFor(1, TimeUnit.SECONDS));
We can see from the above example for the current thread to continue execution it will keep on waiting for the subprocess thread to end or if the specified time interval has elapsed.
When this method is executed, then it will return a boolean value of true if the subprocess has exited or a boolean value false if the wait time had elapsed before the subprocess exited.
6. exitValue()
When this method is run then the current thread won’t wait for the sub-process to get terminated or destroyed, however, it will throw an IllegalThreadStateException if the subprocess isn’t terminated.
Another way around if the subprocess has been successfully terminated then it will result in an exit value of the process.
It can be any possible positive integer number.
Let’s look at an example when the exitValue() method returns a positive integer when the subprocess has been terminated successfully:
@Test
public void
givenSubProcess_whenCurrentThreadWillNotWaitIndefinitelyforSubProcessToEnd_thenProcessExitValueReturnsGrt0()
throws IOException {
ProcessBuilder builder = new ProcessBuilder("notepad.exe");
Process process = builder.start();
assertThat(process.exitValue() >= 0);
}
7. isAlive()
When we’d like to perform business processing which is subjective whether the process is alive or not.
We can perform a quick check to find whether the process is alive or not which returns a boolean value.
Let’s see a quick example of it:
ProcessBuilder builder = new ProcessBuilder("notepad.exe");
Process process = builder.start();
Thread.sleep(10000);
process.destroy();
assertTrue(process.isAlive());
8. Handling Process Streams
By default, the created subprocess does not have its terminal or console. All its standard I/O (i.e., stdin, stdout, stderr) operations will be sent to the parent process. Thereby the parent process can use these streams to feed input to and get output from the subprocess.
Consequently, this gives us a huge amount of flexibility as it gives us control over the input/output of our sub-process.
8.1. getErrorStream()
Interestingly we can fetch the errors generated from the subprocess and thereon perform business processing.
After that, we can execute specific business processing checks based on our requirements.
Let’s see an example:
@Test
public void givenSubProcess_whenEncounterError_thenErrorStreamNotNull() throws IOException {
Process process = Runtime.getRuntime().exec(
"javac -cp src src\\main\\java\\com\\baeldung\\java9\\process\\ProcessCompilationError.java");
BufferedReader error = new BufferedReader(new InputStreamReader(process.getErrorStream()));
String errorString = error.readLine();
assertNotNull(errorString);
}
8.2. getInputStream()
We can also fetch the output generated by a subprocess and consume within the parent process thus allowing share information between the processes:
@Test
public void givenSourceProgram_whenReadingInputStream_thenFirstLineEquals3() throws IOException {
Process process = Runtime.getRuntime().exec(
"javac -cp src src\\main\\java\\com\\baeldung\\java9\\process\\OutputStreamExample.java");
process = Runtime.getRuntime()
.exec("java -cp src/main/java com.baeldung.java9.process.OutputStreamExample");
BufferedReader output = new BufferedReader(new InputStreamReader(process.getInputStream()));
int value = Integer.parseInt(output.readLine());
assertEquals(3, value);
}
8.3. getOutputStream()
We can send input to a subprocess from a parent process:
Writer w = new OutputStreamWriter(process.getOutputStream(), "UTF-8");
w.write("send to child\n");
8.4. Filter Process Streams
It’s a perfectly valid use-case to interact with selective running processes.
Process provides us the facility to selectively filter running processes based on a certain predicate.
After that we can perform business operations on this selective process set:
@Test
public void givenRunningProcesses_whenFilterOnProcessIdRange_thenGetSelectedProcessPid() {
assertThat(((int) ProcessHandle.allProcesses()
.filter(ph -> (ph.pid() > 10000 && ph.pid() < 50000))
.count()) > 0);
}
9. Conclusion
Process is a powerful class for Operating System level interaction. Triggering terminal commands as well as launching, monitoring and killing applications.
For more reading on the Java 9 Process API, take a look at our article here.
As always, you’ll find the sources over on Github.