Java Program that exec commands won't continue - java

I try to get my penplotter to work from within java.
I have a start but I don't know how to continue.
This is what I have:
public static void main(String[] args) {
try {
Runtime runTime = Runtime.getRuntime();
Process process = runTime.exec("chiplotle");
BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line = null;
System.out.println("this prints fine");
while ((line = in.readLine()) != null) {
System.out.println(line);
}
System.out.println("it never reaches this...");
}
catch (IOException e) {
e.printStackTrace();
}
}
This is the output in the console:
I typed the 11 myself. But it doesn't do anything with it.
Also it never prints:
System.out.println("it never reaches this...");
So it looks like my program is halted for input, is that correct?
And how can I get further?

You should read from the InputStream in a bacgkround thread.
You need to get the Process's OutputStream and then write to it.
OutputStream os = process.getOutputStream();
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os));
PrintWriter pw = new PrintWriter(bw);
// now you can write to the Process, i.e., pw.println("11");
You will need to not just print but also to analyze the text that your InputStream sends you to decide when to write back to the process via the PrintWriter.

Related

how should I send the output from a system commands executed on server to client in continuous fashion

I'm executing a system command on server which gives continuous output till it gets completed. Since it takes a long time to get completed, I want to sent the output from this system command to client in continuous fashion. Any suggestions on how should I do it.
Here is my snippet of code which executes the system command and has the input stream to read from.
I'm invoking this code form my servlet doPost.
Process process = pb.start();
final InputStream is = process.getInputStream();
final StringBuffer strBuff = new StringBuffer();
Thread inputStreamThread = new Thread(){
public void run() {
String line;
BufferedReader br = new BufferedReader(new InputStreamReader(is));
try {
while((line = br.readLine()) != null) {
strBuff.append(line + "\n");
}
} catch (IOException e) {
logger.error("IO Exception occured in inputStreamThread=> "+e);
}
}
};
inputStreamThread.start();
inputStreamThread.join();
int exitVal = process.waitFor();

Redirect I/O of subprocess in Java (why doesn't ProcessBuilder.inheritIO() work?)

I'm launching a process in the following way.
try {
final Process mvnProcess = new ProcessBuilder("cmd", "/c", "mvn", "--version")
.directory(new File(System.getProperty("user.dir")))
.inheritIO()
.start();
System.exit(mvnProcess.waitFor());
} catch (final IOException ex) {
System.err.format(IO_EXCEPTION);
System.exit(1);
} catch (final InterruptedException ex) {
System.err.format(INTERRUPTED_EXCEPTION);
System.exit(1);
}
Since I invoke inheritIO() I was expecting the sub-process's output on the console, but nothing appears. What am I missing here?
Edit: I know that I can use mvnProcess.getInputStream() and read the process's output explicitly, writing it to the console (or where-ever) in a loop. I don't like this solution however, since the loop will block my thread. inheritIO() looked promising, but apparently I don't understand how it works. I was hoping someone here could shed some light on this.
Maybe it is an option the read it from the subprocess:
Add this code after start() and you will have it printed to stdout:
InputStream is = mvnProcess.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null)
{
System.out.println(line);
}
You can use the .redirectError(Redirect.INHERIT).
It sets the source and destination for sub process standard I/O to be the same as those of the current Java process.

Reading Java process status with InputStream and then sending command prompts with OutputStream

I'm working on reading the output of a script that is invoked using a Java process. However, in the middle of the script run, it will in SOME situations prompt the user to answer y/n to continue. However, after reading many posts on StackOverflow, I'm still stuck with detecting the prompt and then sending the response while the process is still running.
If anyone has any ideas, that would be awesome.
I've tried reading from Scanner class and System.console to no prevail.
Here is a portion of the code I'm using.
Process p;
String file = "./upgrade.sh";
cmds.add(file);
cmds.add(sourcePath);
cmds.add(outputDirectoryPath);
cmds.add(zip);
cmds.add("-c");
//}
pb = new ProcessBuilder(cmds);
pb.directory(new File(binDir));
p = pb.start();
BufferedReader reader = new BufferedReader (new InputStreamReader(p.getInputStream()));
BufferedReader reader2 = new BufferedReader (new InputStreamReader(p.getErrorStream()));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(p.getOutputStream()));
try
{
String line = reader.readLine();
while (line != null)
{
System.out.println(line);
line = reader.readLine();
}
reader.close();
writer.close();
}
catch (IOException e)
{
e.printStackTrace();
}
p.destroy();
The problem is that you use a BufferedReader. It will only return when it has read a full line, that is a line separator. But if the script asks for something with a prompt, there won't be a line separator! As a result it WILL NOT return.
You have to use some other kind of reader in order to control the process.

How to pipe InputStream to ProcessBuilder

Please move down to the 2nd update. I didn't want to change the previous context of this question.
I'm using wkhtmltoimage from a Java app.
The standard way of using it is - path-to-exe http://url.com/ image.png.
According to their docs, if we write a - instead of an input URL, the input shifts to STDIN.
I'm starting the process using ProcessBuilder -
ProcessBuilder pb = new ProcessBuilder(exe_path, " - ", image_save_path);
Process process = pb.start();
Now I'm unable to figure out how to pipe an input stream to this process.
I have a template file read into a DataInputStream, and I'm appending a string at the end:
DataInputStream dis = new DataInputStream (new FileInputStream (currentDirectory+"\\bin\\template.txt"));
byte[] datainBytes = new byte[dis.available()];
dis.readFully(datainBytes);
dis.close();
String content = new String(datainBytes, 0, datainBytes.length);
content+=" <body><div id='chartContainer'><small>Loading chart...</small></div></body></html>";
How do I pipe content to the STDIN of the process?
UPDATE---
Following the answer by Andrzej Doyle:
I've used the getOutputStream() of the process:
ProcessBuilder pb = new ProcessBuilder(full_path, " - ", image_save_path);
pb.redirectErrorStream(true);
Process process = pb.start();
System.out.println("reading");
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
bw.write(content);
Doing so gives an error saying:
Exception in thread "main" java.io.IOException: The pipe has been ended
2nd UPDATE--------
The current code block is as such:
try {
ProcessBuilder pb = new ProcessBuilder(full_path, "--crop-w", width, "--crop-h", height, " - ", image_save_path);
System.out.print(full_path+ "--crop-w"+ width+ "--crop-h"+ height+" "+ currentDirectory+"temp.html "+ image_save_path + " ");
pb.redirectErrorStream(true);
Process process = pb.start();
process.waitFor();
OutputStream stdin = process.getOutputStream();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(stdin));
// content is the string that I want to write to the process.
writer.write(content);
writer.newLine();
writer.flush();
writer.close();
} catch (Exception e) {
System.out.println("Exception: " + e);
e.printStackTrace();
}
Running the above code gives me an IOException: The pipe is being closed.
What else do I need to do to keep the pipe open?
Exception in thread "main" java.io.IOException: The pipe has been ended
This means the process you have started has died. I suggest you read the output to see why. e.g. did it give you an error.
Is there a reason you are using DataInputStream to read a simple text file? From the Java documentation
A data input stream lets an application read primitive Java data types
from an underlying input stream in a machine-independent way
It's possible that the way you are reading the file causes an EOF to be sent to the outputstream causing the pipe to end before it gets to your string.
You requirements seems to be to read the file simply to append to it before passing it on to the wkhtmltoimage process.
You're also missing a statement to close the outputstream to the process. This will cause the process to wait (hang) until it gets an EOF from the input stream, which would be never.
I'd recommend using a BufferedReader instead, and writing it directly to the outputstream before appending your additional string. Then call close() to close the stream.
ProcessBuilder pb = new ProcessBuilder(full_path, " - ", image_save_path);
pb.redirectErrorStream(true);
Process process = null;
try {
process = pb.start();
} catch (IOException e) {
System.out.println("Couldn't start the process.");
e.printStackTrace();
}
System.out.println("reading");
try {
if (process != null) {
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
BufferedReader inputFile = new BufferedReader(new InputStreamReader(new FileInputStream(currentDirectory+"\\bin\\template.txt")));
String currInputLine = null;
while((currInputLine = inputFile.readLine()) != null) {
bw.write(currInputLine);
bw.newLine();
}
bw.write("<body><div id='chartContainer'><small>Loading chart...</small></div></body></html>");
bw.newLine();
bw.close();
}
} catch (IOException e) {
System.out.println("Either couldn't read from the template file or couldn't write to the OutputStream.");
e.printStackTrace();
}
BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
String currLine = null;
try {
while((currLine = br.readLine()) != null) {
System.out.println(currLine);
}
} catch (IOException e) {
System.out.println("Couldn't read the output.");
e.printStackTrace();
}
Remove the whitespace from " - " -- the normal whitespaces are removed by the shell parser, but here in the ProcessBuilder, it's interpreted as the (literal) filename beginning and ending with a whitespace.
(Actually looking at the process's output as Peter suggested would probably have told you so...)
After you create the Process object, you can call getOutputStream() in order to get hold of a stream that sends its contents to the process' standard input.
Once you have hold of this you can use Java's standard IO to write whatever bytes to the this stream you want (including wrapping it in a Writer, adding buffering, etc.) - and as you write them, they'll be read by the process as soon as they're flushed.
The following code works as well:
import java.io.*;
import java.util.*;
public class ProcessTest {
public static void main(String[] args) throws Exception {
ProcessBuilder pb = new ProcessBuilder("/home/me/stdinecho");
pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
Process proc = pb.start();
// Input file
DataInputStream din = new DataInputStream((new FileInputStream("/home/me/stdinecho.cp")));
byte[] dinBytes = new byte[din.available()];
din.readFully(dinBytes);
din.close();
String content = new String(dinBytes, 0, dinBytes.length);
content = "header\n" + content + "\nfooter";
BufferedInputStream procStdout = new BufferedInputStream(proc.getInputStream());
OutputStream stdin = proc.getOutputStream();
stdin.write(content.getBytes());
stdin.flush();
}
}
Here stdinecho.cpp is the program that outputs the line entered on its prompt:
#include <iostream>
#include <fstream>
#include <cstdio>
using namespace std;
int main()
{
string strOutput;
string str;
while(getline(cin, str)) {
cout << str << endl;
}
}

Java Runtime.exec() asynchronous output

I'd like to get the output from a long running shell command as it is available instead of waiting for the command to complete. My code is run in a new thread
Process proc = Runtime.getRuntime().exec("/opt/bin/longRunning");
InputStream in = proc.getInputStream();
int c;
while((c = in.read()) != -1) {
MyStaticClass.stringBuilder.append(c);
}
The problem with this is that my program in /opt/bin/longRunning has to complete before the InputStream gets assigned and read. Is there any good way to do this asynchronously? My goal is that an ajax request will return the current value MyStaticClass.stringBuilder.toString()
every second or so.
I'm stuck on Java 5, fyi.
Thanks!
W
Try with Apache Common Exec. It has the ability to asynchronously execute a process and then "pump" the output to a thread. Check the Javadoc for more info
Runtime.getRuntime().exec does not wait for the command to terminate, so you should be getting the output straight away. Maybe the output is being buffered because the command knows it is writing to a pipe rather than a terminal?
Put the reading in a new thread:
new Thread() {
public void run() {
InputStream in = proc.getInputStream();
int c;
while((c = in.read()) != -1) {
MyStaticClass.stringBuilder.append(c);
}
}
}.start();
Did you write the program you're calling? If so try flushing your output after writing. The text could be stuck in a buffer and not getting to your java program.
I use this code to do this and it works:
Runtime runtime = Runtime.getRuntime();
Process proc = runtime.exec(command);
BufferedReader br = new BufferedReader(new InputStreamReader(proc.getInputStream()));
while (true) {
// enter a loop where we read what the program has to say and wait for it to finish
// read all the program has to say
while (br.ready()) {
String line = br.readLine();
System.out.println("CMD: " + line);
}
try {
int exitCode = proc.exitValue();
System.out.println("exit code: " + exitCode);
// if we get here then the process finished executing
break;
} catch (IllegalThreadStateException ex) {
// ignore
}
// wait 200ms and try again
Thread.sleep(200);
}
Try :
try {
Process p = Runtime.getRuntime().exec("/opt/bin/longRunning");
BufferedReader in = new BufferedReader(
new InputStreamReader(p.getInputStream()));
String line = null;
while ((line = in.readLine()) != null) {
System.out.println(line); }

Categories