Thursday, 5 April 2018

ProcessBuilder: Execute system commands

In this post, you are going to learn.
a.   What is ProcessBuilder
b.   Understanding Runtime class
b.1 How to get runtime of the application.
c.   Explore Process API
c.1 How to get an instance of Process class
d.   Explore ProcessBuilder API
d.1 How can you execute system commands using ProcessBuilder API.
d.2 Attributes managed by ProcessBuilder

What is ProcessBuilder?
'java.lang.ProcessBuilder' class is used to create operating system processes. This class is introduced in Java 1.5. You can use this class to execute the operating system commands. Before dig into further, let us explore Runtime and Process APIs.

Understanding Runtime class
Every Java application has a single instance of Runtime class, this contains the run time environment details of the system, in which your application is running.

How to get runtime of the application?
By calling the getRuntime() api of the Runtime class, you can get the Runtime instance.

Runtime runTime = Runtime.getRuntime();

Below application prints the number of processors and the memory details of the application.

Test.java

package com.sample.runtime;

public class Test {
 public static void main(String args[]) {
  Runtime runTime = Runtime.getRuntime();

  int availableProcessors = runTime.availableProcessors();

  long freeMemory = runTime.freeMemory(); // In bytes
  long totalMemory = runTime.totalMemory();
  long usedMemory = totalMemory - freeMemory;

  System.out.println("Available Processors : " + availableProcessors);
  System.out.println("Free memory : " + freeMemory + " bytes");
  System.out.println("Total memory : " + totalMemory + " bytes");
  System.out.println("Used Memory : " + usedMemory + " bytes");

 }
}

There are several methods available in Runtime class to execute system command, load the libraries, forcibly terminate the jvm, trace method calls etc.,

Explore Process API
java.lang.Process class represent the native process.

How to get an instance of Process class?
There are two ways to get an instance of Process class.
         a. By using ProcessBuilder.start() and
         b. By using Runtime.exec method
once you got the Process instance, you can get the input stream and output streams associated with the process, you can wait for the process to complete, you can kill the process, and get the exit status of the process.

Below application uses the Runtime class to get the Process instance to execute a system command.

SystemCommand.java
package com.sample.runtime;

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class SystemCommand {
 private static void runCmd(String cmd) {
  String s;
  String cmd1 = cmd;
  String output = "";

  try {
   Process proc = Runtime.getRuntime().exec(cmd1);
   BufferedReader stdInput;
   BufferedReader stdError;

   stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream()));
   stdError = new BufferedReader(new InputStreamReader(proc.getErrorStream()));

   while ((s = stdInput.readLine()) != null) {
    output = output + s + "\n";
   }

   while ((s = stdError.readLine()) != null) {
    output = output + s + "\n";
   }
  } catch (Exception E) {
   System.out.println("Exception caught in RunWindowsCmd " + E);
  }

  System.out.println(output);
 }

 private static void runWindowsCmd(String cmd) {
  String s;
  String cmd1 = "cmd.exe /c ";
  cmd1 = cmd1 + cmd;
  String output = "";

  try {
   Process proc = Runtime.getRuntime().exec(cmd1);
   BufferedReader stdInput;
   BufferedReader stdError;

   stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream()));
   stdError = new BufferedReader(new InputStreamReader(proc.getErrorStream()));

   while ((s = stdInput.readLine()) != null) {
    output = output + s + "\n";
   }

   while ((s = stdError.readLine()) != null) {
    output = output + s + "\n";
   }
  } catch (Exception E) {
   System.out.println("Exception caught in RunWindowsCmd " + E);
  }
  System.out.println(output);
 }

 public static void runMyCmd(String s) {
  System.out.println(s + "\n");
  String osType = getOsDetails();

  if (osType.contains("windows") || osType.contains("Windows") || osType.contains("WINDOWS")) {
   runWindowsCmd(s);
   return;
  }
  runCmd(s);

 }

 private static String getOsDetails() {
  return System.getProperty("os.name");
 }
}

Test.java
package com.sample.runtime;

public class Test {
 public static void main(String args[]) {
  String command = "tasklist";
  
  SystemCommand.runMyCmd(command);
 }
}


Explore ProcessBuilder API
ProcessBuilder class is used to create Operating system processes.

How to get an instance of ProcessBuilder?
ProcessBuilder class provides below constructros to get an instance of ProcessBuilder.
ProcessBuilder(List<String> command)
ProcessBuilder(String... command)

Example
ProcessBuilder processBuilder = new ProcessBuilder("myCommand", "myArg1", "myArg2");

How to start a new process?
By calling the start() method, you can start a new process.

Example
ProcessBuilder processBuilder = new ProcessBuilder(command);
Process process = processBuilder.start();

How can you execute system commands using ProcessBuilder API?
Find the below working application.


SystemCommand.java
package com.sample.runtime;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.List;

public class SystemCommand {
 public static void runCmd(List<String> command) {
  
  ProcessBuilder processBuilder = new ProcessBuilder(command);
  
  String s;
  String output = "";

  try {
   Process proc = processBuilder.start();
   BufferedReader stdInput;
   BufferedReader stdError;

   stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream()));
   stdError = new BufferedReader(new InputStreamReader(proc.getErrorStream()));

   while ((s = stdInput.readLine()) != null) {
    output = output + s + "\n";
   }

   while ((s = stdError.readLine()) != null) {
    output = output + s + "\n";
   }
  } catch (Exception E) {
   System.out.println("Exception caught in RunWindowsCmd " + E);
  }

  System.out.println(output);
 }

}


Test.java
package com.sample.runtime;

import java.util.Arrays;
import java.util.List;

public class Test {
 public static void main(String args[]) {
  String command = "tasklist";
  String argument1 = "/SVC";
  
  List<String> cmdWithArgs = Arrays.asList(command, argument1);
  
  SystemCommand.runCmd(cmdWithArgs);
 }
}

The start() method can be invoked repeatedly from the same instance to create new subprocesses with identical or related attributes.

Attributes managed by ProcessBuilder
A ProcessBuilder instance manages below process related attributes.

Attribute
Description
Command
Command to be executed.
environment
The initial value is a copy of System.getEnv() of current process. You can get the current environment variables by calling 'environment' api of ProcessBuilder instance.

Ex
ProcessBuilder processBuilder = new ProcessBuilder("myCommand", "myArg1", "myArg2");

Map<String, String> env = processBuilder.environment();
env.put("name", "Krishna");
working directory
Default value is the current working directory (Value of the system property ‘user.dir’).

Ex
ProcessBuilder processBuilder  = new ProcessBuilder("myCommand", "myArg1", "myArg2");
processBuilder .directory(new File("myDirectory"));
Output and error streams
By default, the subprocess writes standard output and standard error to pipes. Java code can access these pipes via the input streams returned by Process.getInputStream() and Process.getErrorStream(). However, standard output and standard error may be redirected to other destinations using redirectOutput and redirectError.

Ex
ProcessBuilder processBuilder  = new ProcessBuilder("myCommand", "myArg1", "myArg2");

File log = new File("outputLog");
processBuilder.redirectErrorStream(true);
processBuilder.redirectOutput(Redirect.appendTo(log));
Input stream
By default, the subprocess reads input from a pipe. Java code can access this pipe via the input stream returned by Process.getInputStream(). However, standard input may be redirected to another source using redirectInput.

Reference

You may like



No comments:

Post a Comment