Introduction to Java ProcessHandle
Tags: java, processhandle, codequality, startup
Published: 2020-03-25 03:40:41

This is a quick introduction to application of Java [ProcessHandle](https://docs.oracle.com/javase/9/docs/api/java/lang/ProcessHandle.html "Java 9 ProcessHandle API documentation") through a use case.

###Background:

I developed a website call ps2pdf.com. It uses a range of free software to process user requests. To put it simply, it is an orchestrator for bunch of free opensource software such as [FFMPEG](https://www.ffmpeg.org/ "FFMPEG official website"). While there are libraries that allows me to use most of these software directly from my Java application (Mostly though JNI), they are always bit outdated or unstable. I, being a brave programmer, choose to use Java ProcessBuilder and Process to execute programs such as FFMPEG with arguments. Managing of processes spawn like that is bit chaotic. Until the release of Java 9, I had a code that mainly uses `Runtime.getRuntime().exec("ps aux...")` to find and kill processes that hangs or exceed time limit. I had quite a bit of code, an ugly code, written to manage processes. I got really exited when I came across Java 9 ProcessHandle interface. It exposes many information about processes running on a machine (specifically when used in Linux systems) which I could use to do a major cleanup.

###About ProcessHandle:

Java ProcessHandle provides a interface to native processes. Interface allows **reading information** about processes, such as checking whether a process is alive or **invoking simple operations** on those process such as killing it.

###Use case: 

I will walk you though following use case which I used to explain some of the useful part of the ProcessHandle interface.

1. Get all running processes on the system
2. Filter ffmpeg processes that meets a certain criteria
  1. Check if they are running
  2. Check if they are running for more than 4 hours
  3. Check if input file fed to ffmpeg is deleted
3. Kill all hanged ffmpeg processes

####Step 1: Finding all processes

```java
Stream<ProcessHandle> allProcesses = ProcessHandle.allProcesses();
```

####Step 2: Filter processes

```java
Stream<ProcessHandle> toDeleteProcessHandles = allProcesses.filter(processHandle -> {
    // Check if process is alive
    if(processHandle.isAlive()){
        Info info = processHandle.info();
        String executableWithPath = info.command().orElse(null);
        // Check if this is an ffmpeg instant
        if(executableWithPath != null && executableWithPath.endsWith("ffmpeg")){
            // Check if they are running for more than 4 hours
            if(Duration.between(Instant.now(), info.startInstant().get()).toHours() > 4){

                // Check if input file fed on to ffmpeg is deleted
                String[] arguments = info.arguments().orElse(null);
                String inputFile = Arrays.stream(arguments).filter(arg -> arg.startsWith("/home/username/content/input")).findFirst().orElse(null);
                if(inputFile != null && !Files.exists(Paths.get(inputFile))){
                    return true;
                }
            }
        }
    }
    return false;
});
```

####Step 3: Kill hanged processes

```java
toDeleteProcessHandles.forEach(processHandle -> processHandle.destroy());
```

This interface combined with [Stream API](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html "Java 1.8 Stream API documentation") allow writing concise code. You may have noticed that I used `orElse(null)` in some places. Which is from Java [Optional](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html "Java 1.8 Optional class documentation") class introduced in Java 1.8 with Stream API. You can further clean up this code by using methods such is `isPresent()` on Optional class instead of clustering with `someVariable != null && doSomethingWithSomeVariable` inside if expressions checks.

Another useful static method on this class is 
```java
ProcessHandle handle = ProcessHandle.of(long pid);
```

This is useful when you start a process using ProcessBuilder or have a Java Process object. Java Process object (Java 9+) exposes PID of a process though `someProcess.pid()`. Which you can use with `ProcessHandle.of()` to keep track of a process.

You could find more about ProcessHandle and [ProcessHandle.Info](https://docs.oracle.com/javase/9/docs/api/java/lang/ProcessHandle.Info.html "ProcessHandle.Info documentation") by reading their documentation. 

This very code is in action every time someone [convert a video](https://www.ps2pdf.com/compress-mp4 "PS2PDF video compressor") or [compresses a jpeg](https://www.ps2pdf.com/compress-jpeg "PS2PDF JPEG image compressor") on PDF2PDF.

Thank you for reading. You are welcome to ask questions or give a feedback.