For controlling and managing processes on your operating system, Java provides the ProcessAPI. But the API lacks some key functionality, which makes handling with processes in Java a mess. With Java 9, this API will get a considerable update. What this means, what the benefits are and what it looks like is discussed in this article.

What are new features of the ProcessAPI?

The primary goal of the developers is, to make it easier for you to manage processes on your operating system. They start with giving you the ability to enumerate the system processes via the updated API (no hacky solutions anymore). That means, you can get different properties for each process, e.g. the PID, the process name, the user who started it, the path of the process, resource usage, etc. It is also planned, to make it easier to deal with process trees, especially destructing entire process trees and managing processes with over 100 sub-processes. To do so, it is planned to multiplex their output streams into one output stream, so you do no longer have one thread per monitored process listening for its output stream.



How would it look like?

We have two examples for you. In the first example, one tries to get the process-id of the current process. In the second example, one tries to get a list of all processes.

So let’s see, how retrieving the process-ID of the current process looks like, before the ProcessAPI-Update.

private static int getProcessOwnID() { // pName is equal to PID@COMPUTER_NAME String pName = ManagementFactory.getRuntimeMXBean().getName(); return Integer.parseInt(pName.split("@")[0]); }

As you can see, the name is retrieved via the runtime-mx-bean. This returns a String which looks like 1234@Some_Computer. So the String is splitten and the number left of the ‘@’ is converted to an Integer. Very hacky.

How would it look like with the new API?

private static int getOwnProcessID(){ return ProcessHandle.current().getPid(); }

Much more beautiful, isn’t it?

As you can see, you do not need any more String-Splitting. The new class “ProcessHandle” contains a static method “current”, which returns you an instance of ProcessHandle representing a handle for the current process. This handle-object contains various information about the process it is attached to. This includes handles to the sub-processes, its process-id and an internal info-object containing more detailed information about the process.

Now let’s see a slightly more complex example, where one is getting a list of all processes running on the operating system.

This is an old implementation with pure Java:

// for Windows: private static void doStuffWithProcesses() { try { Process p = Runtime.getRuntime().exec( System.getenv("windir") + "\\system32\\tasklist.exe"); BufferedReader input = new BufferedReader( new InputStreamReader(p.getInputStream())); String line = null; while ((line = input.readLine()) != null) { System.out.println(line); //If line describes a correct process, //reformat the string to get your information out here. } input.close(); } catch (IOException ioe) { ioe.printStackTrace(); } }

As you see in lines 4ff, a program delivered with Windows called “tasklist.exe” is executed. This programs output stream is read (lines 7ff).

Very hacky, isn’t it? It works only for Windows and so have to use a slightly different solution for every operating system. And this snippet does not contain the Pattern-Regex that you need to get some valuable information out of the stream in line 11, because the output looks like this:



Image Name PID Session Name Session# Mem Usage

========================= ======== ================ =========== =============

System Idle Process 0 Services 0 4K

System 4 Services 0 10.604K

csrss.exe 504 Services 0 2.792K

wininit.exe 572 Services 0 2.900K

svchost.exe 808 Services 0 18.568K

(...)



You would also have to adjust this regex for every operating system, too.

But now look at this:

private static void listProcesses(){ ProcessHandle.allProcesses().forEach((h) -> printHandle(h)); } private static void printHandle(ProcessHandle procHandle) { // get info from handle ProcessHandle.Info procInfo = procHandle.info(); System.out.println("PID: " + procHandle.getPid()); System.out.print("Start Parameters: "); String[] empty = new String[]{"-"}; for (String arg : procInfo.arguments().orElse(empty)) { System.out.print(arg + " "); } System.out.println(); System.out.println("Path: " + procInfo.command().orElse("-")); System.out.println("Start: " + procInfo.startInstant(). orElse(Instant.now()).toString()); System.out.println("Runtime: " + procInfo.totalCpuDuration(). orElse(Duration.ofMillis(0)).toMillis() + "ms"); System.out.println("User: " + procInfo.user()); }

No more execution of operating-system-dependent applications and no more regex for the resulting output.

The call in line 2 retrieves a List of all processes, already in a structured format (as a list of ProcessHandles). You can now easily iterate those handles and do what you want to do. In line 8, the internal info-object is retrieved from the handle. This info-object contains various information about the process, which will be retrieved shortly. In the following lines, the id of the process (line 10), the start-parameters (lines 12ff), the path (line 19), the start-time (lines 21ff), the runtime (lines 24ff) and the user (line 27) are retrieved from the handler.

You can also get the handle of a process directly, if you know the process id. You simply call ProcessHandle.of(pid).

No doubt, this is a much cleaner way, then check the operating system and splitting the String with an appropriate regex.

You can now imagine what it looks like, if you are trying to deal with sub-processes and their output-streams. Thanks to the update to the ProcessAPI, much of these hacky solutions are no longer needed and the problems can be solved with clean, operating-system-independent code.

Like this: Like Loading...

Related