java ProcessBuilder run shell script is hanging - java

I'm trying to run shell script by using ProcessBuilder. The script works but it can't run after the java code. And error stream doesn't output message. I'm running it on centOS 6.9 computer. Please find below my code.
public static ArrayList<String> runCommand(ArrayList<String> command)throws IOException {
ProcessBuilder processBuilder = new ProcessBuilder();
processBuilder.command(command);
Process process = processBuilder.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
ArrayList<String> commandOutput = new ArrayList<>();
String str;
while((str = reader.readLine()) != null) {
commandOutput.add(str);
}
StringBuilder errorBuilder = new StringBuilder();
while((str = errorReader.readLine()) != null) {
errorBuilder.append(str);
}
String errorMessage = errorBuilder.toString();
if(!errorMessage.equals("")) {
String message = LOG_TAG + ",[runCommand] error:" + errorMessage;
System.out.println(message);
}
reader.close();
errorReader.close();
process.destroy();
return commandOutput;
}

In your case, you are reading something from the output stream of the process, till you consume everything. Then, you try to read error stream.
If the process writes some considerable number of characters on the error stream, the other process will block till they are consumed. To consume both error stream and output stream at the same time, you need to use threads.
You may follow the StreamGobbler technique. You may get some details from that page: https://www.javaworld.com/article/2071275/when-runtime-exec---won-t.html?page=2
This is some code influenced from the page:
public class StreamGobbler extends Thread {
private static final String EOL = System.lineSeparator();
private final InputStream inputStream;
private final StringBuilder output = new StringBuilder();
public StreamGobbler(InputStream inputStream) {
this.inputStream = inputStream;
}
public void run() {
try (InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader reader = new BufferedReader(inputStreamReader);
) {
String line;
while ((line = reader.readLine()) != null) {
output.append(line);
output.append(EOL);
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
public String getOutput() {
return output.toString();
}
}
In your code, you use StreamGobbler like this:
StreamGobbler outputGobbler = new StreamGobbler(process.getInputStream());
StreamGobbler errorGobbler = new StreamGobbler(process.getErrorStream());
process.waitFor();
String commandOutput = outputGobbler.getOutput();
String errorMessage = errorGobbler.getOutput();
process.destroy();

Related

error=2, No such file or directory at java.lang.UNIXProcess.forkAndExec(Native Method)

I wrote one java program to periodically monitor the status of the devices using fping protocol. It will execute the fping command for every 5 mins. I got an above exception sometimes only. We are using this directory (/usr/local/bin/). This where fping installed. Please suggest.
public static List<String> executeProcess(String command,List<String> args,String dirPath) throws Exception {
// delegate
args.add(0,command);
args.add(1,FpingSession.FPING_RETRY);
args.add(2,FpingSession.FPING_RETRY_VALUE);
ProcessBuilder processBuilder = new ProcessBuilder(args);
if(dirPath!=null && !dirPath.isEmpty())
processBuilder.directory(new File(dirPath));
List<String> stringList;
Process process = null;
InputStream inputStream = null;
InputStreamReader inputStreamReader = null;
BufferedReader bufferedReader = null;
try {
process = processBuilder.start();
inputStream = process.getInputStream();
inputStreamReader = new InputStreamReader(
inputStream);
bufferedReader = new BufferedReader(
inputStreamReader);
stringList = new LinkedList<String>();
while (true) {
// next line
final String string = bufferedReader.readLine();
if (string == null) {
break;
}
// track
stringList.add(string);
}
} finally{
if (process != null)
process.destroy();
if (inputStream != null)
inputStream.close();
if (inputStreamReader != null)
inputStreamReader.close();
if (bufferedReader != null)
bufferedReader.close();
}
// done
return stringList;
}

ProcessBuilder not displaying output stream if it is java

I got a code which is running and displaying output successfully if I am executing something like "dir" but not displaying the output if I am running "java -version" or other command from java. Please help:
public static void execJob(){
try{
ProcessBuilder pb = new ProcessBuilder("C:\\myPrograms\\jdk1.7.0_79\\bin\\java.exe", "-version");
pb.directory(new File("src"));
Process process = pb.start();
IOThreadHandler outputHandler = new IOThreadHandler(process.getInputStream());
outputHandler.start();
process.waitFor();
System.out.println(outputHandler.getOutput());
}catch(Exception e) {
System.out.println(e.toString());
}
}
private static class IOThreadHandler extends Thread {
private InputStream inputStream;
private StringBuilder output = new StringBuilder();
IOThreadHandler(InputStream inputStream) {
this.inputStream = inputStream;
}
public void run() {
Scanner br = null;
try {
br = new Scanner(new InputStreamReader(inputStream));
String line = null;
while (br.hasNextLine()) {
line = br.nextLine();
output.append(line + System.getProperty("line.separator"));
}
} finally {
br.close();
}
}
java -version writes to stderr, so you need pb.redirectErrorStream(true); to capture the output.
ProcessBuilder pb = new ProcessBuilder("C:\\myPrograms\\jdk1.7.0_79\\bin\\java.exe", "-version");
pb.redirectErrorStream(true);
...
private static class IOThreadHandler extends Thread {
private InputStream inputStream;
private StringBuilder output = new StringBuilder();
IOThreadHandler(InputStream inputStream) {
this.inputStream = inputStream;
}
public void run() {
try (Scanner br = new Scanner(new InputStreamReader(inputStream))) {
String line = null;
while (br.hasNextLine()) {
line = br.nextLine();
output.append(line).append(System.getProperty("line.separator"));
}
}
}
public String getOutput() {
return output.toString();
}
}

Executing 'adb logcat' command using Runtime class

I was trying to get the logcat content into a JTextPane. I used following code hoping it will return the content as String but it freeze and also, doesn't produce an error.
Process exec = null;
try {
exec = Runtime.getRuntime().exec("adb logcat -d");
InputStream errorStream = exec.getErrorStream();
BufferedReader ebr = new BufferedReader(new InputStreamReader(errorStream));
String errorLine;
while ((errorLine = ebr.readLine()) != null) {
System.out.println("[ERROR] :- " + errorLine);
}
if (exec.waitFor() == 0) {
InputStream infoStream = exec.getInputStream();
InputStreamReader isr = new InputStreamReader(infoStream);
BufferedReader ibr = new BufferedReader(isr);
String infoLine;
while ((infoLine = ibr.readLine()) != null) {
System.out.println("[INFO] :- " + infoLine);
}
}
} catch (IOException | InterruptedException ex) {
ex.printStackTrace();
} finally {
if (exec != null) {
exec.destroy();
}
}
I referred to some tutorials but, they were not filling my problem. Is this wrong? Are there any other methods to get the logcat content as a String programmatically? Sorry if this is a dumb question.
The issue you're seeing is that you're trying to process command streams and wait for the executing process, all in the same thread. It's blocking because the process reading the streams is waiting on the process and you're losing the stream input.
What you'll want to do is implement the function that reads/processes the command output (input stream) in another thread and kick off that thread when you start the process.
Second, you'll probably want to use ProcessBuilder rather than Runtime.exec.
Something like this can be adapted to do what you want:
public class Test {
public static void main(String[] args) throws Exception {
String startDir = System.getProperty("user.dir"); // start in current dir (change if needed)
ProcessBuilder pb = new ProcessBuilder("adb","logcat","-d");
pb.directory(new File(startDir)); // start directory
pb.redirectErrorStream(true); // redirect the error stream to stdout
Process p = pb.start(); // start the process
// start a new thread to handle the stream input
new Thread(new ProcessTestRunnable(p)).start();
p.waitFor(); // wait if needed
}
// mimics stream gobbler, but allows user to process the result
static class ProcessTestRunnable implements Runnable {
Process p;
BufferedReader br;
ProcessTestRunnable(Process p) {
this.p = p;
}
public void run() {
try {
InputStreamReader isr = new InputStreamReader(p.getInputStream());
br = new BufferedReader(isr);
String line = null;
while ((line = br.readLine()) != null)
{
// do something with the output here...
}
}
catch (IOException ex) {
ex.printStackTrace();
}
}
}
}

compiling java file from another java class

import java.io.BufferedReader;
import java.io.InputStreamReader;
public class ExecuteShellComand {
public static void main(String[] args) {
ExecuteShellComand obj = new ExecuteShellComand();
String className = "str.java";
String command = "javac " + className;
String output = obj.executeCommand(command);
System.out.println(output);// prints the output of the executed command
}
private String executeCommand(String command) {
StringBuffer output = new StringBuffer();
Process p;
try {
p = Runtime.getRuntime().exec(command);
p.waitFor();
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = "";
while ((line = reader.readLine()) != null) {
output.append(line + "\n");
}
} catch (Exception e) {
e.printStackTrace();
}
return output.toString();
}
}
I am trying to compile a Java file (str.java) from another Java class(ExecuteShellComand.java). What I am trying to do is if "str.java" compiles successfully then I want to execute "java str" command, but if the compilation fails then proper stacktrace or errors should be printed. I am storing the stacktrace or the errors in output variable.
But when I execute this code although "str.java" has somes errors in it System.out.println(output) is not printing the errors.
If you want to capture the errors from a command then you shall capture error stream instead of Input stream
So replace
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
with
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getErrorStream()));
The Process class tries to mimetize OS process. It means, process keep different output stream for error and normal messages and one stream for input. In UNIX, should be:
wc < file > wc.count 2> wc.error
In Java...
abstract InputStream getErrorStream()
Gets the error stream of the subprocess.
abstract InputStream getInputStream()
Gets the input stream of the subprocess.
abstract OutputStream getOutputStream()
So, you should use getErrorStream() to get errors..
Refactoring your code:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class ExecuteShellComand {
public static void main(String[] args) {
ExecuteShellComand obj = new ExecuteShellComand();
String className = "str.java";
String command = "javac " + className;
obj.executeCommand(command);
System.out.println(obj.output);
System.out.println(obj.errors);
}
private String errors;
private String output;
private void executeCommand(String command) {
Process p;
try {
p = Runtime.getRuntime().exec(command);
p.waitFor();
errors = readStream(p.getErrorStream());
output = readStream(p.getInputStream());
} catch (Exception e) {
e.printStackTrace();
}
}
private String readStream(InputStream inputStream) throws IOException {
StringBuffer output = new StringBuffer();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String line = "";
while ((line = reader.readLine()) != null) {
output.append(line + "\n");
}
return output.toString();
}
}

capture error from runtime process java

I'm running a Java program from another Java application using Runtime.getRuntime().exec like this
Process p1 = Runtime.getRuntime().exec("javac test.java");
Process p2 = Runtime.getRuntime().exec("java test");
The content of the test.java
import java.io.*;
class test
{
public static void main(String args[])
{
Scanner sc = new Scanner(System.in);
String s = sc.nextLine();
System.out.println(s);
}
}
I want to handle Input, Output and Error stream of the process p2.
I did capture of the output of the test.java, however, I do not know how to handle output and error.
Here is my code:
try {
String s = "";
InputStream istr = p2.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(istr));
BufferedReader bre = new BufferedReader
(new InputStreamReader(p2.getErrorStream()));
while ((s = br.readLine()) != null) {
System.out.println(s);
}
br.close();
while ((s = bre.readLine()) != null) {
System.out.println(s);
}
bre.close();
p2.waitFor();
} catch (IOException ex) {
ex.printStackTrace();
} catch (Exception err) {
err.printStackTrace();
}
The code above works fine for capturing the output of the test.java. But it does not display error of the test.java.
Could you please give me a sample code for fixing this problem and handling output stream or share idea? Thanks in advance
The solution I've always used is to create a separate thread to read one of the streams
So, in your case it should be something like
String s = "";
InputStream istr = p2.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(istr));
BufferedReader bre = new BufferedReader
(new InputStreamReader(p2.getErrorStream()));
new Thread(new Runnable() {
#Override
public void run() {
while ((s = br.readLine()) != null) {
System.out.println(s);
}
}
}).start();
new Thread(new Runnable() {
#Override
public void run() {
while ((s = bre.readLine()) != null) {
System.out.println(s);
}
}
}).start();
// when you are finished close streams

Categories

Resources