The java.lang.Process class provides methods to get input or output from a process, wait for process to complete, check exit status or kill the process. From Java 7 onwards, the process streams could be redirected to files.

In Java 9, certain enhancements(JEP 102) were done to the Process API and a new interface called ProcessHandle was also introduced. The newly added methods of java.lang.Process class are:

Stream<ProcessHandle> children()

Returns a snapshot of direct children for the given process.

Returns a snapshot of direct children for the given process. Stream<ProcessHandle> descendants()

Gives a snapshot of all the children in the process tree including direct children and their descendents.

Gives a snapshot of all the children in the process tree including direct children and their descendents. long pid()

Gives native process ID of the process.

Gives native process ID of the process. ProcessHandle.Info info()

The ProcessHandle.Info returned by this method has methods that gives more information about the process.

The ProcessHandle.Info returned by this method has methods that gives more information about the process. CompletableFuture<Process> onExit()

The CompletableFuture returned allows to trigger important functions/actions that may be required while exiting the process.

The CompletableFuture returned allows to trigger important functions/actions that may be required while exiting the process. boolean supportsNormalTermination()

The method destroy() is used to kill a given process. The method supportsNormalTermination() returns true if implementation of destroy() is to terminate the process normally and false if it forcibly terminates the process immediately if invoked.

The method destroy() is used to kill a given process. The method supportsNormalTermination() returns true if implementation of destroy() is to terminate the process normally and false if it forcibly terminates the process immediately if invoked. ProcessHandle toHandle()

Returns a ProcessHandle for the process.

Let us learn more about the new changes with some examples

Creating A New Process And Getting Its ID

The methods ProcessHandle.pid() returns a long value that denotes the process id of the invoking process. Since native OS level processes have unsigned integer values for process IDs, the return value for this method is kept as long.

Consider the following example:

package com.javagists; import java.io.IOException; public class ProcessCreation { public static void main(String[] args) throws IOException { Process process = new ProcessBuilder("notepad.exe").start(); System.out.println("**** NEW PROCESS SPAWNED ****"); System.out.println("============================="); System.out.println(" Process ID : " + process.pid()); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package com . javagists ; import java . io . IOException ; public class ProcessCreation { public static void main ( String [ ] args ) throws IOException { Process process = new ProcessBuilder ( "notepad.exe" ) . start ( ) ; System . out . println ( "**** NEW PROCESS SPAWNED ****" ) ; System . out . println ( "=============================" ) ; System . out . println ( " Process ID : " + process . pid ( ) ) ; } }

Running the above program will give the following output:

**** NEW PROCESS SPAWNED **** ============================= Process ID : 11600 1 2 3 **** NEW PROCESS SPAWNED * *** ============================= Process ID : 11600

Obtaining Process Information

To get more details about a given process, we can get an instance of ProcessHandle.Info by invoking Process.info() on the process object. The ProcessHandle.Info returned by the method contains access methods to get more information about the given process. An instance of ProcessHandle for a given process can be obtained using Process.toHandle().

Each method that is available in the ProcessHandle.Info returns an instance of java.util.Optional. The Optional class is just a container object that may or may not contain values. To make sure that our sample programs works seamlessly, we have added the .orElse() construct wherever necessary in subsequent sections. The methods like orElse(), orElseGet() and orElseThrow() are used to suggest what has to be done in case the Optional instance does not have any value associated with it.

The following code illustrates how the ProcessHandle.Info can be used to obtain information about a given process programmatically:

package com.javagists; import java.io.IOException; import java.time.Duration; import java.time.Instant; import java.util.concurrent.ExecutionException; public class ProcessDemo { public static void main(String[] args) throws IOException, InterruptedException, ExecutionException { Process process = new ProcessBuilder("notepad.exe").start(); System.out.println("**** PROCESS INFORMATION OF CURRENT PROCESS ****"); printInfo(ProcessHandle.current()); System.out.println("**** PROCESS INFORMATION FOR NOTEPAD.EXE****"); printInfo(process.toHandle()); } private static void printInfo(ProcessHandle ph) { System.out.println("============================="); System.out.println(" Process ID : " + ph.pid()); ProcessHandle.Info pInfo = ph.info(); System.out.println(" Executable path : " + pInfo.command().orElse("No path")); System.out.println(" Arguments : "); String[] argArray = pInfo.arguments().orElse(new String[] {}); for(String s: argArray) System.out.println(s); System.out.println(" Process Owner: " + pInfo.user().orElse("No owner")); System.out.println(" Process Start time : " + pInfo.startInstant().orElse(Instant.now()).toString()); System.out.println(" Total Up Time : " + pInfo.totalCpuDuration().orElse(Duration.ofMillis(0)).toMillis() + " milli seconds"); System.out.println("============================="); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package com . javagists ; import java . io . IOException ; import java . time . Duration ; import java . time . Instant ; import java . util . concurrent . ExecutionException ; public class ProcessDemo { public static void main ( String [ ] args ) throws IOException , InterruptedException , ExecutionException { Process process = new ProcessBuilder ( "notepad.exe" ) . start ( ) ; System . out . println ( "**** PROCESS INFORMATION OF CURRENT PROCESS ****" ) ; printInfo ( ProcessHandle . current ( ) ) ; System . out . println ( "**** PROCESS INFORMATION FOR NOTEPAD.EXE****" ) ; printInfo ( process . toHandle ( ) ) ; } private static void printInfo ( ProcessHandle ph ) { System . out . println ( "=============================" ) ; System . out . println ( " Process ID : " + ph . pid ( ) ) ; ProcessHandle . Info pInfo = ph . info ( ) ; System . out . println ( " Executable path : " + pInfo . command ( ) . orElse ( "No path" ) ) ; System . out . println ( " Arguments : " ) ; String [ ] argArray = pInfo . arguments ( ) . orElse ( new String [ ] { } ) ; for ( String s : argArray ) System . out . println ( s ) ; System . out . println ( " Process Owner: " + pInfo . user ( ) . orElse ( "No owner" ) ) ; System . out . println ( " Process Start time : " + pInfo . startInstant ( ) . orElse ( Instant . now ( ) ) . toString ( ) ) ; System . out . println ( " Total Up Time : " + pInfo . totalCpuDuration ( ) . orElse ( Duration . ofMillis ( 0 ) ) . toMillis ( ) + " milli seconds" ) ; System . out . println ( "=============================" ) ; } }

In the above program, information about the current process (java process) is obtained in main() method using ProcessHandle.current(). Process.toHandle() gives a process handle for the newly created process for notepad.exe. Both these handles are passed to printInfo() method to get the respective process information.

Output for the above code will be as follows:

**** PROCESS INFORMATION OF CURRENT PROCESS **** ============================= Process ID : 2192 Executable path : C:\Program Files\Java\jdk-9\bin\javaw.exe Arguments : Process Owner : LAPTOP-K3BSJPDS\Matrix Process Start time : 2017-08-17T11:41:52.394Z Total Up Time : 234 milli seconds ============================= **** PROCESS INFORMATION FOR NOTEPAD.EXE**** ============================= Process ID : 7060 Executable path : C:\Windows\System32

otepad.exe Arguments : Process Owner : LAPTOP-K3BSJPDS\Matrix Process Start time : 2017-08-17T11:41:52.585Z Total Up Time : 62 milli seconds ============================= 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 **** PROCESS INFORMATION OF CURRENT PROCESS * *** ============================= Process ID : 2192 Executable path : C : \ Program Files \ Java \ jdk - 9 \ bin \ javaw . exe Arguments : Process Owner : LAPTOP - K3BSJPDS \ Matrix Process Start time : 2017 - 08 - 17T11 : 41 : 52.394Z Total Up Time : 234 milli seconds ============================= **** PROCESS INFORMATION FOR NOTEPAD . EXE* *** ============================= Process ID : 7060 Executable path : C : \ Windows \ System32 \ notepad . exe Arguments : Process Owner : LAPTOP - K3BSJPDS \ Matrix Process Start time : 2017 - 08 - 17T11 : 41 : 52.585Z Total Up Time : 62 milli seconds =============================

Obtaining Process Information On Termination

It is possible to get the state of a process right after it is terminated. While creating a process, we can invoke process.waitFor(). The waitFor() method is similar to the normal wait() used in multi-threaded applications in synchronized context. It directs the current process to wait till the invoked process is terminated.

Consider the following example:

package com.javagists; import java.io.IOException; import java.time.Duration; import java.time.Instant; import java.util.concurrent.ExecutionException; public class ProcessDemo { public static void main(String[] args) throws IOException, InterruptedException, ExecutionException { Process process = new ProcessBuilder("mspaint.exe").start(); System.out.println("**** PROCESS INFORMATION FOR MSPAINT.EXE AT START****"); printInfo(process.toHandle()); process.waitFor(); System.out.println("**** PROCESS INFORMATION FOR MSPAINT.EXE DURING TERMINATION****"); printInfo(process.toHandle()); } private static void printInfo(ProcessHandle ph) { System.out.println("============================="); System.out.println(" Process ID : " + ph.pid()); ProcessHandle.Info pInfo = ph.info(); System.out.println(" Executable path : " + pInfo.command().orElse("No path")); System.out.println(" Arguments : "); String[] argArray = pInfo.arguments().orElse(new String[] {}); for(String s: argArray) System.out.println(s); System.out.println(" Process Owner: " + pInfo.user().orElse("No owner")); System.out.println(" Process Start time : " + pInfo.startInstant().orElse(Instant.now()).toString()); System.out.println(" Total Up Time : " + pInfo.totalCpuDuration().orElse(Duration.ofMillis(0)).toMillis() + " milli seconds"); System.out.println("============================="); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package com . javagists ; import java . io . IOException ; import java . time . Duration ; import java . time . Instant ; import java . util . concurrent . ExecutionException ; public class ProcessDemo { public static void main ( String [ ] args ) throws IOException , InterruptedException , ExecutionException { Process process = new ProcessBuilder ( "mspaint.exe" ) . start ( ) ; System . out . println ( "**** PROCESS INFORMATION FOR MSPAINT.EXE AT START****" ) ; printInfo ( process . toHandle ( ) ) ; process . waitFor ( ) ; System . out . println ( "**** PROCESS INFORMATION FOR MSPAINT.EXE DURING TERMINATION****" ) ; printInfo ( process . toHandle ( ) ) ; } private static void printInfo ( ProcessHandle ph ) { System . out . println ( "=============================" ) ; System . out . println ( " Process ID : " + ph . pid ( ) ) ; ProcessHandle . Info pInfo = ph . info ( ) ; System . out . println ( " Executable path : " + pInfo . command ( ) . orElse ( "No path" ) ) ; System . out . println ( " Arguments : " ) ; String [ ] argArray = pInfo . arguments ( ) . orElse ( new String [ ] { } ) ; for ( String s : argArray ) System . out . println ( s ) ; System . out . println ( " Process Owner: " + pInfo . user ( ) . orElse ( "No owner" ) ) ; System . out . println ( " Process Start time : " + pInfo . startInstant ( ) . orElse ( Instant . now ( ) ) . toString ( ) ) ; System . out . println ( " Total Up Time : " + pInfo . totalCpuDuration ( ) . orElse ( Duration . ofMillis ( 0 ) ) . toMillis ( ) + " milli seconds" ) ; System . out . println ( "=============================" ) ; } }

In the above program, the first printInfo() executes right after launching the paint process. The second printInfo() statement does not execute till we close the paint window.

Output for the above program will be as follows:

**** PROCESS INFORMATION FOR MSPAINT.EXE AT START**** ============================= Process ID : 7060 Executable path : C:\Windows\System32\mspaint.exe Arguments : Process Owner : LAPTOP-K3BSJPDS\Matrix Process Start time : 2017-08-17T13:27:18.740Z Total Up Time : 0 milli seconds ============================= **** PROCESS INFORMATION FOR MSPAINT.EXE DURING TERMINATION**** ============================= Process ID : 7060 Executable path : No path Arguments : Process Owner : LAPTOP-K3BSJPDS\Matrix Process Start time : 2017-08-17T13:27:18.740Z Total Up Time : 406 milli seconds ============================= 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 **** PROCESS INFORMATION FOR MSPAINT . EXE AT START* *** ============================= Process ID : 7060 Executable path : C : \ Windows \ System32 \ mspaint . exe Arguments : Process Owner : LAPTOP - K3BSJPDS \ Matrix Process Start time : 2017 - 08 - 17T13 : 27 : 18.740Z Total Up Time : 0 milli seconds ============================= **** PROCESS INFORMATION FOR MSPAINT . EXE DURING TERMINATION* *** ============================= Process ID : 7060 Executable path : No path Arguments : Process Owner : LAPTOP - K3BSJPDS \ Matrix Process Start time : 2017 - 08 - 17T13 : 27 : 18.740Z Total Up Time : 406 milli seconds =============================

Note that while printing the process information for second time, the original executable path obtained from ProcessHandle.Info.command() earlier is not printed. Instead, the string “No path” is printed. This is because we had used the .orElse() construct to print “No path” if the Optional object returned by command() function does not have any value associated with it. The printInfo() method is invoked again for a second time by current process only after it is notified that the process created programmatically is terminated. Note that the “Total Up Time” value printed during termination of the process is larger than the one printed by process handle while starting the mspaint application.

Obtaining Process Information Of Children

It is possible to get a list of children for the current process and print information for them programmatically. The sample code below demonstrates how this can be achieved by using lambdas and streams.

Consider the following example:

package com.javagists; import java.io.IOException; import java.time.Duration; import java.time.Instant; import java.util.concurrent.ExecutionException; public class ProcessInformation { public static void main(String[] args) throws IOException, InterruptedException, ExecutionException { Process process = new ProcessBuilder("mspaint.exe").start(); Process notepadproc = new ProcessBuilder("notepad.exe").start(); Process notepadplus = new ProcessBuilder("explorer.exe").start(); ProcessHandle.current().children() .forEach((p) -> printInfo(p)); } private static void printInfo(ProcessHandle ph) { System.out.println("============================="); System.out.println(" Process ID : " + ph.pid()); ProcessHandle.Info pInfo = ph.info(); System.out.println(" Executable path : " + pInfo.command().orElse("No path")); System.out.println(" Arguments : "); String[] argArray = pInfo.arguments().orElse(new String[] {}); for(String s: argArray) System.out.println(s); System.out.println(" Process Owner: " + pInfo.user().orElse("No owner")); System.out.println(" Process Start time : " + pInfo.startInstant().orElse(Instant.now()).toString()); System.out.println(" Total Up Time : " + pInfo.totalCpuDuration().orElse(Duration.ofMillis(0)).toMillis() + " milli seconds"); System.out.println("============================="); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package com . javagists ; import java . io . IOException ; import java . time . Duration ; import java . time . Instant ; import java . util . concurrent . ExecutionException ; public class ProcessInformation { public static void main ( String [ ] args ) throws IOException , InterruptedException , ExecutionException { Process process = new ProcessBuilder ( "mspaint.exe" ) . start ( ) ; Process notepadproc = new ProcessBuilder ( "notepad.exe" ) . start ( ) ; Process notepadplus = new ProcessBuilder ( "explorer.exe" ) . start ( ) ; ProcessHandle . current ( ) . children ( ) . forEach ( ( p ) - > printInfo ( p ) ) ; } private static void printInfo ( ProcessHandle ph ) { System . out . println ( "=============================" ) ; System . out . println ( " Process ID : " + ph . pid ( ) ) ; ProcessHandle . Info pInfo = ph . info ( ) ; System . out . println ( " Executable path : " + pInfo . command ( ) . orElse ( "No path" ) ) ; System . out . println ( " Arguments : " ) ; String [ ] argArray = pInfo . arguments ( ) . orElse ( new String [ ] { } ) ; for ( String s : argArray ) System . out . println ( s ) ; System . out . println ( " Process Owner: " + pInfo . user ( ) . orElse ( "No owner" ) ) ; System . out . println ( " Process Start time : " + pInfo . startInstant ( ) . orElse ( Instant . now ( ) ) . toString ( ) ) ; System . out . println ( " Total Up Time : " + pInfo . totalCpuDuration ( ) . orElse ( Duration . ofMillis ( 0 ) ) . toMillis ( ) + " milli seconds" ) ; System . out . println ( "=============================" ) ; } }

In the above code, the current process creates a new process for notepad, paint and window explorer applications. Hence those new processes become children of the current java process.

Output of the above program will be as follows:

============================= Process ID : 6944 Executable path : C:\Windows\System32\mspaint.exe Arguments : Process Owner : LAPTOP-K3BSJPDS\Matrix Process Start time : 2017-08-17T12:32:15.002Z Total Up Time : 46 milli seconds ============================= ============================= Process ID : 3252 Executable path : C:\Windows\System32

otepad.exe Arguments : Process Owner : LAPTOP-K3BSJPDS\Matrix Process Start time : 2017-08-17T12:32:15.041Z Total Up Time : 46 milli seconds ============================= ============================= Process ID : 2508 Executable path : C:\Windows\explorer.exe Arguments : Process Owner : LAPTOP-K3BSJPDS\Matrix Process Start time : 2017-08-17T12:32:15.043Z Total Up Time : 62 milli seconds ============================= 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 ============================= Process ID : 6944 Executable path : C : \ Windows \ System32 \ mspaint . exe Arguments : Process Owner : LAPTOP - K3BSJPDS \ Matrix Process Start time : 2017 - 08 - 17T12 : 32 : 15.002Z Total Up Time : 46 milli seconds ============================= ============================= Process ID : 3252 Executable path : C : \ Windows \ System32 \ notepad . exe Arguments : Process Owner : LAPTOP - K3BSJPDS \ Matrix Process Start time : 2017 - 08 - 17T12 : 32 : 15.041Z Total Up Time : 46 milli seconds ============================= ============================= Process ID : 2508 Executable path : C : \ Windows \ explorer . exe Arguments : Process Owner : LAPTOP - K3BSJPDS \ Matrix Process Start time : 2017 - 08 - 17T12 : 32 : 15.043Z Total Up Time : 62 milli seconds =============================

ProcessHandle.children() returns a Stream containing handles these three processes. Data of each process is printed by invoking printInfo() for each handle using lambda expression:

ProcessHandle.current().children() .forEach((p) -> printInfo(p)); 1 2 3 ProcessHandle . current ( ) . children ( ) . forEach ( ( p ) - > printInfo ( p ) ) ;

To print the information on all processes that are visible to the current process, we can alter the above lambda expression to the following:

ProcessHandle.allProcesses() .filter(ph -> ph.info().command().isPresent()) .forEach((p) -> printInfo(p)); 1 2 3 ProcessHandle . allProcesses ( ) . filter ( ph - > ph . info ( ) . command ( ) . isPresent ( ) ) . forEach ( ( p ) - > printInfo ( p ) ) ;

Note the filter applied in above statement. Optional.isPresent() returns true if value is present in the given object. Thus, the filter makes sure that information is printed only if the process is running and not terminated. In the section “Obtaining Process Information On Termination” we saw how a ProcessHandle.Info.command() does not contain value once a given process is terminated. So, this is a better approach to make sure that only data of active processes are printed to the console.

Summary

Process API has been improved in Java 9 for controlling and managing operating-system process. With Java 9 it is possible to get information about any spawned process including the current process. And hence these changes are definitely going to make it easier to work with Process API.

Share this: Facebook

LinkedIn

Twitter

Tumblr

Pinterest



Like this: Like Loading...