While trying to run subprocess it prints only stdout then terminates - java

I'm using Apache Commons Exec and trying to start subprocess that would work for the entire duration of application. It should start process, accepttwo input commands and just stay in background. Now its only accepts one command (at least what stdout shows) and terminates. Can you aid me?
CommandLine cmdLine = new CommandLine("app.exe");
cmdLine.addArgument("argument");
DefaultExecutor executor = new DefaultExecutor();
OutputStream os = new ByteArrayOutputStream();
InputStream is = new ByteArrayInputStream(("command1;\ncommand2;\n").getBytes());
executor.setStreamHandler(new PumpStreamHandler(os,null,is));
DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();
executor.execute(cmdLine,resultHandler);
System.out.println(os.toString());
resultHandler.waitFor();

I think these two lines are in the wrong order:
System.out.println(os.toString());
resultHandler.waitFor();
Should be like this (to allow the process to complete it's output):
resultHandler.waitFor();
System.out.println(os.toString());
EDIT
Still not 100% sure what you are after, but think I missed the "just stay in background" part of your original request. One way to achieve this would be to use a PipedInputStream & PipedOutputStream pair to talk to the process. When you are done, you can close the output stream. If you wanted access to the output from the process before it finishes, you could use a similar technique for the output with the direction reversed.
I don't have a windows machine handy, but the following works for me:
public static void main(String[] args) {
try {
CommandLine cmdLine = new CommandLine("/bin/bash");
DefaultExecutor executor = new DefaultExecutor();
OutputStream os = new ByteArrayOutputStream();
PipedOutputStream pos = new PipedOutputStream();
PipedInputStream pis = new PipedInputStream(pos);
executor.setStreamHandler(new PumpStreamHandler(os, null, pis));
DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();
executor.execute(cmdLine, resultHandler);
PrintWriter pw = new PrintWriter(pos);
pw.println("ls -l /usr");
pw.println("pwd");
pw.close();
resultHandler.waitFor();
System.out.println(os.toString());
} catch (Exception e) {
e.printStackTrace();
}
}

Related

Linux zombies processes left unterminated with Java ProcessBuilder

I have a java Spring REST API with a controller that runs a linux command with the ProcessBuilder class
. The command is a generated 'find' command
The problem is that I found a lot of unterminated processes in the hosting server after fiew days of use. I don't know why they still there and not ended or destroyed . (I checked with a ps -ef command)
Here is my runCmd function:
public static final BufferedReader runCmd(String cmd) throws IOException, InterruptedException {
ProcessBuilder processBuilder = new ProcessBuilder();
processBuilder.command("bash", "-c", cmd);
processBuilder.redirectErrorStream(true);
Process process = processBuilder.start();
BufferedReader output = new BufferedReader(new InputStreamReader(process.getInputStream()));
int ret = process.waitFor();
return output;
}
Is there a way to make sur that there is no more process left behind ?
UPDATE
The problem comes only from commands with a very large output stream (std output) Thanks for the hint #DuncG
As this output is important, I can't ignore it. I have to find a way to consume it.
Any Idea on how to do it with Runnable Threads ?
Thanks
Are your commands generating a lot of output? The cause of the zombies may be simply that cmd has written a lot of output the STDOUT and the stream is blocking in the BufferedReader.
You can test if this the case by adding redirect to null - just append " > /dev/null" the end of cmd. This discards the sub-process output and means the BufferedReader is not full of unread data / blocking the sub-process.
processBuilder.command("bash", "-c", cmd + " > /dev/null");
If that fixes the zombie problem you can revert the redirect and make ProcessBuilder redirect output to files (before calling start()), or you'll need to add a thread to consume the IO as it is generated.
Path tmpdir = Path.of(System.getProperty("java.io.tmpdir"));
Path out = tmpdir.resolve("stdout.log");
processBuilder.redirectErrorStream(true);
processBuilder.redirectOutput(out.toFile());
At the end you should return the out file for caller to check, or could return Files.newBufferedReader(out).
If you don't use the redirect to file as above, this will store using thread to capture the output into memory buffer. Note you'd need to duplicate for STDERR too if not redirecting ERR->OUT:
Process p = pb.start();
ByteArrayOutputStream stdout = new ByteArrayOutputStream(8192);
new Thread(() -> copy(p.getInputStream(), stdout), "STDOUT").start();
int rc = p.waitFor();
byte[] sour = stdout.toByteArray()
Using method:
private static void copy(InputStream in, OutputStream buf)
{
try(var autoClose = in; var autoClose2 = buf)
{
in.transferTo(buf);
}
catch(IOException io)
{
throw new UncheckedIOException(io);
}
}

Wait until a command ends

There's a command, that runs an exe, it makes an output and when this command ends, then I should run the upload(); method.
run.command(exe_path, txt_path);
run.start();
TimeUnit.SECONDS.sleep(20);
upload();
How can I replace the static sleep method?
Using a ProcessBuilder, you should be able to call Process.waitFor() to wait for the command to finish.
Process p = new ProcessBuilder("foo.exe", "arg1").start();
// handle any input and output
InputStream stdout = p.getInputStream();
OutputStream stdin = p.getOutputStream();
InputStream stderr = p.getErrorStream();
int ret = p.waitFor(); // <- wait for exit

Control a command line program session via Java

I have a computer algebra program (called Reduce) that works in the shell in an interactive manner: launch Reduce in the shell, then you can define variables, compute this and that, and what not. Reduce prints the output into the shell. My idea is that I want to build a frontend for this text-based program that evaluates its output and converts it into a nice LaTeX style formula. For this I want to use Java.
I can start Reduce via exec(). But how can I emulate text input to the opened shell, and how can I read back what Reduce writes into the shell?
Thanks
Jens
Edit 1: Current Code
// get the shell
Runtime rt = Runtime.getRuntime();
// execute reduce
String[] commands = {"D:/Programme/Reduce/reduce.com", "", ""};
Process proc = null;
try {
proc = rt.exec(commands);
} catch (Exception e) {
System.out.println("Error!\n");
}
// get the associated input / output / error streams
BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream()));
BufferedWriter stdOutput = new BufferedWriter(new OutputStreamWriter(proc.getOutputStream()));
BufferedReader stdError = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
// read the output from the command
System.out.println("Here is the standard output of the command:\n");
String s = null;
try {
while ((s = stdInput.readLine()) != null) {
System.out.println(s);
}
} catch (Exception e) {
}
// read any errors from the attempted command
System.out.println("Here is the standard error of the command (if any):\n");
try {
while ((s = stdError.readLine()) != null) {
System.out.println(s);
}
} catch (Exception e) {
}
You need to get the streams associated with the process including the InputStream, OutputStream, and ErrorStream. You then can send messages to the process via the OutputStream and then read info from the process via the InputStream and the ErrorStream.
From some code of mine:
final ProcessBuilder pBuilder = new ProcessBuilder(TEST_PROCESS_ARRAY);
final Process proc = pBuilder.start();
procInputStream = proc.getInputStream();
errorStream = proc.getErrorStream();
errorSBuffer = new StringBuffer();
streamGobblerSb = new StreamGobblerSb(errorStream, "Autoit Error", errorSBuffer);
new Thread(streamGobblerSb).start();
final Scanner scan = new Scanner(procInputStream);
You may want to look into using the Process class.
http://docs.oracle.com/javase/7/docs/api/java/lang/Process.html
I believe you may be able to start the process, and then use getOutputStream() to feed commands into the process.
While this is not strictly an answer, I discovered that it is more convenient for me to stick with PHP's function proc_open(). That way I can include the output directly in the frontend and do not need to worry about the communication between my Java program and the html frontend.
For everybody who wants to stick to the Java method: the article http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html is a good reference.

When running jar program from java code, the jar program's gui freezes on direct input

To understand the situation, I try to describe what I'm trying to achieve:
I would like to create a java program that starts another java program (a minecraft server), monitors it's output, and sends commands to it's input accordingly.
I was able to start the jar program from my code, and I was also able to write an output listener for it, that currently only prints the jar program's output to the console.
The jar program I'm running from my code has a gui with a text output panel, where it shows messages, and an input field (textarea) where I can give commands. My problem is that when I start my code and it starts the program everything works fine until I want to write directly into the program's input field. If I type any letter into the input field, the gui freezes.
What do I wrong?
Here's my code:
public static void main(String[] args) {
try {
System.out.println("Starting.");
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec("java -jar \"D:\\Program Files\\mc\\minecraft_server.jar\"");
// output both stdout and stderr data from proc to stdout of this process
StreamGobbler errorGobbler = new StreamGobbler(proc.getErrorStream());
StreamGobbler outputGobbler = new StreamGobbler(proc.getInputStream());
errorGobbler.start();
outputGobbler.start();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
The output reader:
public class StreamGobbler extends Thread {
InputStream is;
// reads everything from is until empty.
StreamGobbler(InputStream is) {
this.is = is;
}
public void run() {
try {
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line = null;
while ((line = br.readLine()) != null)
System.out.println(line);
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
Thanks in advance! :)
First of all, instead of using the stream gobbler, just inherit the process's io:
final ProcessBuilder p1 = new ProcessBuilder(command).inheritIO();
p1.start();
If you don't care about stdin/out, use this to direct everything to NUL (windows version of /dev/null):
p1.redirectInput(Redirect.from(new File("NUL"))).redirectOutput(Redirect.to(new File("NUL")))
.redirectError(Redirect.to(new File("NUL")));
Does the program still crash?

Writing command line output to a file with in Java

I am running a command line command from java:
ping localhost > output.txt
The command is send via a Java like this:
Process pr = rt.exec(command);
For some reason the file is not created, but when i run this command from the
command line itself, the file does create and the output is in that file.
Why doesn't the java command create the file?
Because you haven't directed it to a file.
On the command line, you've requested that it be redirected to a file. You have to do the same thing in Java, via the InputStream provided by the Process object (which corresponds to the output stream of the actual process).
Here's how you get the output from the process.
InputStream in = new BufferedInputStream( pr.getInputStream());
You can read from this until EOF, and write the output to a file. If you don't want this thread to block, read and write from another thread.
InputStream in = new BufferedInputStream( pr.getInputStream());
OutputStream out = new BufferedOutputStream( new FileOutputStream( "output.txt" ));
int cnt;
byte[] buffer = new byte[1024];
while ( (cnt = in.read(buffer)) != -1) {
out.write(buffer, 0, cnt );
}
1. After successfully executing the command from Java program, you need to read the output, and then divert the Output to the file.
Eg:
Process p = Runtime.getRuntime().exec("Your_Command");
InputStream i = p.getInputStream();
InputStreamReader isr = new InputStreamReader(i);
BufferedReader br = new BufferedReader(isr);
File f = new File("d:\\my.txt");
FileWriter fw = new FileWriter(f); // for appending use (f,true)
BufferedWriter bw = new BufferedWriter(fw);
while((br.readLine())!=null){
bw.write(br.readLine()); // You can also use append.
}
Complementing Andy's answer, I think you MUST read this article: http://www.javaworld.com/jw-12-2000/jw-1229-traps.html.
It is very important for who needs to deal with external processes in Java.
I you want to keep it simple, and you are using Windows, try:
Process pr = rt.exec("cmd /c \"ping localhost > output.txt\"");

Categories

Resources