This is a quick introduction to application of Java ProcessHandle through a use case.
Background:
I developed a website call xconvert.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. 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.
- Get all running processes on the system
- Filter ffmpeg processes that meets a certain criteria
- Check if they are running
- Check if they are running for more than 4 hours
- Check if input file fed to ffmpeg is deleted
- Kill all hanged ffmpeg processes
Step 1: Finding all processes
Stream<ProcessHandle> allProcesses = ProcessHandle.allProcesses();
Step 2: Filter processes
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
toDeleteProcessHandles.forEach(processHandle -> processHandle.destroy());
This interface combined with Stream API allow writing concise code. You may have noticed that I used orElse(null)
in some places. Which is from Java Optional 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
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 by reading their documentation.
This very code is in action every time someone convert a video or compresses a jpeg on XConvert.
Thank you for reading. You are welcome to ask questions or give a feedback.
Top comments (0)