Process API (original) (raw)

7/7

The Process API lets you start, retrieve information about, and manage native operating system processes.

With this API, you can work with operating system processes as follows:

Topics

Process API Classes and Interfaces

ProcessBuilder Class

The ProcessBuilder class lets you create and start operating system processes.

See Creating a Process for examples on how to create and start a process. The ProcessBuilder class manages various processes attributes, which the following table summarizes:

Process Class

The methods in the Process class let you to control processes started by the methods ProcessBuilder.start and Runtime.exec. The following table summarizes these methods:

The following table summarizes the methods of the Process class.

ProcessHandle Interface

The ProcessHandle interface lets you identify and control native processes. The Process class is different from ProcessHandle because it lets you control processes started only by the methods ProcessBuilder.start and Runtime.exec; however, the Process class lets you access process input, output, and error streams.

See Filtering Processes with Streams for an example of the ProcessHandle interface. The following table summarizes the methods of this interface:

ProcessHandle.Info Interface

The ProcessHandle.Info interface lets you retrieve information about a process, including processes created by the ProcessBuilder.start method and native processes.

See Getting Information About a Process for an example of the ProcessHandle.Info interface. The following table summarizes the methods in this interface:

Table 4-4 ProcessHandle.Info Interface Methods

Method Description
arguments() Returns the arguments of the process as a String array.
command() Returns the executable path name of the process.
commandLine() Returns the command line of the process.
startInstant() Returns the start time of the process.
totalCpuDuration() Returns the total CPU time accumulated of the process.
user() Returns the user of the process.

Creating a Process

To create a process, first specify the attributes of the process, such as the command name and its arguments, with the ProcessBuilder class. Then, start the process with the ProcessBuilder.start method, which returns a Process instance.

The following lines create and start a process:

ProcessBuilder pb = new ProcessBuilder("echo", "Hello World!"); Process p = pb.start();

In the following excerpt, the setEnvTest method sets two environment variables, horse and oats, then prints the value of these environment variables (as well as the system environment variable HOME) with the echo command:

public static void setEnvTest() throws IOException, InterruptedException { ProcessBuilder pb = new ProcessBuilder("/bin/sh", "-c", "echo horsehorse horsedog $HOME").inheritIO(); pb.environment().put("horse", "oats"); pb.environment().put("dog", "treats"); pb.start().waitFor(); }

This method prints the following (assuming that your home directory is /home/admin):

oats treats /home/admin

Getting Information About a Process

The method Process.pid returns the native process ID of the process. The method Process.info returns a ProcessHandle.Info instance, which contains additional information about the process, such as its executable path name, start time, and user.

In the following excerpt, the method getInfoTest starts a process and then prints information about it:

public static void getInfoTest() throws IOException { ProcessBuilder pb = new ProcessBuilder("echo", "Hello World!"); String na = ""; Process p = pb.start(); ProcessHandle.Info info = p.info(); System.out.printf("Process ID: %s%n", p.pid()); System.out.printf("Command name: %s%n", info.command().orElse(na)); System.out.printf("Command line: %s%n", info.commandLine().orElse(na));

System.out.printf("Start time: %s%n",
  info.startInstant().map(i -> i.atZone(ZoneId.systemDefault())
                                .toLocalDateTime().toString())
                     .orElse(na));

System.out.printf("Arguments: %s%n",
  info.arguments().map(a -> Stream.of(a)
                                  .collect(Collectors.joining(" ")))
                  .orElse(na));

System.out.printf("User: %s%n", info.user().orElse(na));

}

This method prints output similar to the following:

Process ID: 18761 Command name: /usr/bin/echo Command line: echo Hello World! Start time: 2017-05-30T18:52:15.577 Arguments: User: administrator

Note:

Redirecting Output from a Process

By default, a process writes standard output and standard error to pipes. In your application, you can access these pipes through the input streams returned by the methods Process.getOutputStream and Process.getErrorStream. However, before starting the process, you can redirect standard output and standard error to other destinations, such as a file, with the methods redirectOutput and redirectError.

In the following excerpt, the method redirectToFileTest redirects standard input to a file, out.tmp, then prints this file:

public static void redirectToFileTest() throws IOException, InterruptedException { File outFile = new File("out.tmp"); Process p = new ProcessBuilder("ls", "-la") .redirectOutput(outFile) .redirectError(Redirect.INHERIT) .start(); int status = p.waitFor(); if (status == 0) { p = new ProcessBuilder("cat" , outFile.toString()) .inheritIO() .start(); p.waitFor(); } }

The excerpt redirects standard output to the file out.tmp. It redirects standard error to the standard error of the invoking process; the value Redirect.INHERIT specifies that the subprocess I/O source or destination is the same as that of the current process. The call to the inheritIO() method is equivalent to redirectInput(Redirect.INHERIT).redirectOuput(Redirect.INHERIT).redirectError(Redirect.INHERIT).

Filtering Processes with Streams

The method ProcessHandle.allProcesses returns a stream of all processes visible to the current process. You can filter the ProcessHandle instances of this stream the same way that you filter elements from a collection.

In the following excerpt, the method filterProcessesTest prints information about all the processes owned by the current user, sorted by the process ID of their parent's process:

public class ProcessTest {

// ...

static void filterProcessesTest() { Optional currUser = ProcessHandle.current().info().user(); ProcessHandle.allProcesses() .filter(p1 -> p1.info().user().equals(currUser)) .sorted(ProcessTest::parentComparator) .forEach(ProcessTest::showProcess); }

static int parentComparator(ProcessHandle p1, ProcessHandle p2) { long pid1 = p1.parent().map(ph -> ph.pid()).orElse(-1L); long pid2 = p2.parent().map(ph -> ph.pid()).orElse(-1L); return Long.compare(pid1, pid2); }

static void showProcess(ProcessHandle ph) { ProcessHandle.Info info = ph.info(); System.out.printf("pid: %d, user: %s, cmd: %s%n", ph.pid(), info.user().orElse("none"), info.command().orElse("none")); }

// ... }

Note that the allProcesses method is limited by native operating system access controls. Also, because all processes are created and terminated asynchronously, there is no guarantee that a process in the stream is alive or that no other processes may have been created since the call to the allProcesses method.

Handling Processes When They Terminate with the onExit Method

The Process.onExit and ProcessHandle.onExit methods return a CompletableFuture instance, which you can use to schedule tasks when a process terminates. Alternatively, if you want your application to wait for a process to terminate, then you can call onExit().get().

In the following excerpt, the method startProcessesTest creates three processes and then starts them. Afterward, it calls onExit().thenAccept(onExitMethod) on each of the processes; onExitMethod prints the process ID (PID), exit status, and output of the process.

public class ProcessTest {

// ...

static public void startProcessesTest() throws IOException, InterruptedException { List greps = new ArrayList<>(); greps.add(new ProcessBuilder("/bin/sh", "-c", "grep -c "java" *")); greps.add(new ProcessBuilder("/bin/sh", "-c", "grep -c "Process" *")); greps.add(new ProcessBuilder("/bin/sh", "-c", "grep -c "onExit" *")); ProcessTest.startSeveralProcesses (greps, ProcessTest::printGrepResults);
System.out.println("\nPress enter to continue ...\n"); System.in.read();
}

static void startSeveralProcesses ( List pBList, Consumer onExitMethod) throws InterruptedException { System.out.println("Number of processes: " + pBList.size()); pBList.stream().forEach( pb -> { try { Process p = pb.start(); System.out.printf("Start %d, %s%n", p.pid(), p.info().commandLine().orElse("")); p.onExit().thenAccept(onExitMethod); } catch (IOException e) { System.err.println("Exception caught"); e.printStackTrace(); } } ); }

static void printGrepResults(Process p) { System.out.printf("Exit %d, status %d%n%s%n%n", p.pid(), p.exitValue(), output(p.getInputStream())); }

private static String output(InputStream inputStream) { String s = ""; try (BufferedReader br = new BufferedReader(new InputStreamReader(inputStream))) { s = br.lines().collect(Collectors.joining(System.getProperty("line.separator"))); } catch (IOException e) { System.err.println("Caught IOException"); e.printStackTrace(); } return s; }

// ... }

The output of the method startProcessesTest is similar to the following. Note that the processes might exit in a different order than the order in which they were started.

Number of processes: 3 Start 12401, /bin/sh -c grep -c "java" * Start 12403, /bin/sh -c grep -c "Process" * Start 12404, /bin/sh -c grep -c "onExit" *

Press enter to continue ...

Exit 12401, status 0 ProcessTest.class:0 ProcessTest.java:16

Exit 12404, status 0 ProcessTest.class:0 ProcessTest.java:8

Exit 12403, status 0 ProcessTest.class:0 ProcessTest.java:38

This method calls the System.in.read() method to prevent the program from terminating before all the processes have exited (and have run the method specified by the thenAccept method).

If you want to wait for a process to terminate before proceeding with the rest of the program, then call onExit().get():

static void startSeveralProcesses ( List pBList, Consumer onExitMethod) throws InterruptedException { System.out.println("Number of processes: " + pBList.size()); pBList.stream().forEach( pb -> { try { Process p = pb.start(); System.out.printf("Start %d, %s%n", p.pid(), p.info().commandLine().orElse("")); p.onExit().get(); printGrepResults(p);
} catch (IOException|InterruptedException|ExecutionException e ) { System.err.println("Exception caught"); e.printStackTrace(); } } ); }

The ComputableFuture class contains a variety of methods that you can call to schedule tasks when a process exits including the following:

Because ComputableFuture implements the Future interface, this class also contains synchronous methods:

Controlling Access to Sensitive Process Information

Process information may contain sensitive information such as user IDs, paths, and arguments to commands. Control access to process information with a security manager.

When running as a normal application, a ProcessHandle has the same operating system privileges to information about other processes as a native application; however, information about system processes may not be available.

If your application uses the SecurityManager class to implement a security policy, then to enable it to access process information, the security policy must grant RuntimePermission("manageProcess"). This permission enables native process termination and access to the process ProcessHandle information. Note that this permission enables code to identify and terminate processes that it did not create.