Java 9: Process API got a new boost!

Remember This hack :
Integer.parseInt(ManagementFactory.getRuntimeMXBean().getName().split("@")[0]); ?! This is a thing that is used in literally thousands of open source projects.

So far there has been a limited ability for controlling and managing operating system processes with Java. For example, in order to do something as simple as get your process PID in earlier versions of Java, you would need to either access native code or use some sort of a magical workaround. Also, it would require a different implementation for each platform to guarantee you’re getting the right result.

Moreover, I quite a few times needed to spawn new child processes from inside a Java process and manage them. The process of doing so is very cumbersome. A reference to the child process has to be kept throughout the program’s execution if the developer wishes to destroy that process later. Not to mention that getting the PIDs of the children processes is also a pain.

Process API Update

Java 9 comes to fix those issues and provide a clean API for interaction with processes. More specifically two new interfaces has been added to the JDK:

  1. java.lang.ProcessHandle
  2. java.lang.ProcessHandle.Info

The two new interfaces add quite a few methods. The first one methods for retrieving a PID, all the processes running in the system and also methods for relationships between processes. The second one mainly provides meta information about the process.

Get PID of current and created processes

System.out.println("Running java process with PID " + ProcessHandle.current().getPid()+ ". Parent: " + ProcessHandle.current().parent().get().getPid());

Process pr = Runtime.getRuntime().exec(cmdArray);

System.out.println("Create Process with PID "+pr.getPid());

What’s happening here is that we use ProcessHandle.current() to get the ProcessHandle reference for our own process. ProcessHandle is a new interface that exposes a lot of functionality for inspecting and understanding processes and you’ll get to learn about this functionality in the remainder of the article. The method getPid() on ProcessHandle returns the process id of the process in question, while parent() returns an Optional<ProcessHandle> for the parent process.

Enumerate Process List

We can list all the processes currently in the system, which are visible to the current process. The returned list is a snapshot at the time when the API was invoked, so it’s possible that some processes terminated after taking the snapshot or some new processes were added.

In order to do that, we can use the static method allProcesses() available in the java.lang.ProcessHandle interface which returns us a Stream of ProcessHandle. As there are many processes returned, I am going to filter out those not having any command associated with:

ProcessHandle.allProcesses().filter(ph -> ph.info().command().isPresent()).forEach(p -> System.out.println(p.getPid() + " " + p.info().commandLine()));

Finding our Long Running Processes

Another task that we might want to use the Process API for is to find all the processes owned by our user. We also want to sort the processes by how long they have been executing for and print those durations out. Here is what our program looks like:

final String userName = System.getProperty("user.name");
ProcessHandle.allProcesses().map(ProcessHandle::info).filter(info -> info.user().filter(name -> name.contains(userName)).isPresent()).sorted(Comparator.comparing(info -> info.totalCpuDuration().orElse(Duration.ZERO))).forEach(info -> info.command().ifPresent(command -> info.totalCpuDuration().ifPresent(duration -> System.out.println(command + " has been running for " + duration.toMinutes()))));

We’ll again start out by streaming all the processes running on the box. We are really mainly interested in getting the Info object related to the process - this is a snapshot of several properties about the running program - so we’ll map each ProcessHandle into its Info. We pull out the user that the program is executing as using the user() method and check that it’s the userName of our user. Next we sort the processes by duration, again the totalCpuDuration() method returns an Optional so have to use the aforementioned filter pattern. Finally we print out the relevant process information.

To sum up, The improvements in the Process API offer Java programmers a much better story when it comes to writing automated tooling for deploying, managing and upgrading their runtime. This is an increasingly popular and important area as more teams move to a devops and SRE driven environment and away from manual system administration. That isn’t to say that Java is always the right tool for every project in that space, but it’s great to see work being put into to making things better in Java 9.