How to wait for a process in Java [duplicate] - java

This question already has answers here:
Wait for process to finish before proceeding in Java
(4 answers)
Closed 3 years ago.
In the following code, I tried to execute a script, which takes quite a while to finish.So, I already tried to use process.waitfor() but it didn't let the script finish. Are there any ideas how to make it work?
ProcessBuilder pb = new ProcessBuilder(osShell);
Process process = pb.start();
PrintWriter pyCon = new PrintWriter(process.getOutputStream());
pyCon.println("cd " + videoDir);
System.out.println("Executing python file: "+ command);
pyCon.println(command);
//Here, I need a piece of code which let's my command run in peace
pyCon.close();
System.out.println(convertStreamToString(process.getInputStream()));
process.waitFor();

By closing the stdin of the shell process, you pretty much told it it's over. Chances are the shell ended killing the child python cmd, or at least not draining nor forwarding the python stdout/stderr to its own shell stdout/stderr.
Try waiting for some expected end marker from your python cmd. Then only close stdin. Drain all the stdout/stderr from the process of course.
It common to see 2 more threads just to pump out the bytes from stdout/stderr while the main thread is controlling/waiting for the process. Keep in mind that if you don't pump the bytes out, the pipes may fill up and block on the child side, preventing it from terminating. These pipes are often pretty small (512 to 2k bytes for instance - that is obviously testable).

Sending commands to a shell’s standard input is unlikely to work, especially in Windows.
The correct way to execute something in a particular directory is not by trying to send a cd command, but by specifying the directory in the ProcessBuilder itself:
ProcessBuilder pb = new ProcessBuilder("python", pythonFileName);
pb.directory(new File(videoDir));
Process process = pb.start();

Related

How to improve performance of Java Process?

Hello stackoverflow community,
I try to run a command on the console and read the input stream of this process:
String command[] = {"ffmpeg"}; //minimal command example
Process proc = processBuilder.command(command).start();
byte[] bytes = IOUtils.toByteArray(proc.getInputStream());
proc.waitFor();
The command is a very long ffmpeg command, which prints bytes to the output. Everything works just fine, but it is very slow using java. When I run this command on my regular command line tool, it takes round about 20ms, everything is printed and done.
For the Java process is takes more than 2s. I also tried to redirect all I/O streams to the std, but the performance is the same, since I thought my reading is too slow. Also, reading the stream in additional threads or using other stream readers etc. did not change anything. The only thing which has an effect is adding cmd (currently working with Windows 10) to the command:
String command[] = {"cmd","/C","ffmpeg"};
resulting in a execution time of round about 400ms. I did not know before that this makes a difference, but it really does.
Since this is a Spring Boot web application and the command is used to output images from a video, 400ms is still a lot. The issue here is, that the frontend/browser requests a bunch of images (lets say 36). Apparently the simultaneous requests of a browser to one host is limited (chrome 6 requests), see here. Therefore it takes at best 6 x 400ms to deliver the content.
So is there a way to improve the performance to the java process or maybe keep it open and fire commands to avoid overhead?
Here is the pseudo code to your question in the comments above:
ByteArrayOutputStream out = new ByteArrayOutputStream();
ProcessBuilder pb = new ProcessBuilder(command);
// Consuming STDOUT and STDERR in same thread can lead to the process freezing if it writes large amounts.
pb.redirectErrorStream(true);
Process process = pb.start();
try (var infoStream = process.getInputStream()) {
infoStream.transferTo(out);
}
status = process.waitFor();
if (status > 0)
// errorhandling
#transferTo(out) Consumes the output stream inside the processbuilder thread. That's it! It runs inside the thread of process.

Java Process Builder not executing multiple commands

Hi a Java newbie here.
I am currently building a Java application that executes multiple linux commands with Java Process builder.
I am planning to execute a shell scipt, and since it this shell script is an external program that takes about a second to fully execute, let the process sleep for a second and write the result into a txt file.
This is an external program and it must take in "q" to exit the program, so I finally need to type in q in the terminal.
I have gotten help on this community before and the code I constructed with that help is as follows.
public static void Linux(String fileName){
try {
File dir = new File("/usr/local/bin");
ProcessBuilder pb = new ProcessBuilder(new String[]{"/bin/bash", "-c", "./test_elanprv2.2.sh > \"$1\"; sleep 1; q", "_", fileName + ".txt"});
System.out.println("wrote text");
pb.directory(dir);
Process start = pb.start();
start.destroy();
}catch (Exception e){
e.printStackTrace();
System.out.println("failed to write text");
}
The process builder does create a txt file but it seems to be empty, and no matter how long I set the sleep to, the programs seems to instanly return the print statement.
It would really be appreciated if anyone could tell me what I am doing wrong.
Thank you in advance!!
As mentioned by #VGR, try using redirectOutput
public static void Linux(String fileName){
try {
File dir = new File("/usr/local/bin");
ProcessBuilder pb = new ProcessBuilder(new String[]{"/bin/bash", "-c", "./test_elanprv2.2.sh");
File output = new File("_", fileName + ".txt");
pb.redirectOutput(output);
System.out.println("wrote text");
pb.directory(dir);
Process start = pb.start();
start.destroy();
} catch (Exception e) {
e.printStackTrace();
System.out.println("failed to write text");
}
Most of your issues are due to a misunderstanding of how processes work. These concepts are not Java concepts; you would have the same issues in any other language.
First, you are destroying your process before it runs, and possibly before it even gets started. This is because pb.start() starts the process, and then you immediately destroy it without giving it a chance to complete.
You shouldn’t need to destroy the process at all. Just let it finish:
Process start = pb.start();
start.waitFor();
All processes have their own standard input and standard output. Again, this is not a Java concept; this has been a fundamental feature in Unix and Windows operating systems for a long time.
Normally, when a process prints information by writing it to its standard output. That is in fact what Java’s System.out.println does. In Unix shells (and in Windows), the > character redirects the standard output of a process to a file; the program still writes to its standard output, without ever knowing that the operating system is sending that output to a destination other than the terminal. Since it’s a fundamental operating system function, Java can do it for you:
ProcessBuilder pb =
new ProcessBuilder("/bin/bash", "-c", "./test_elanprv2.2.sh");
pb.redirectOutput(new File(fileName + ".txt"));
Similarly, when a process wants to take input, it normally does so by reading from its standard input. This is not the same as executing another command. When you do this:
./test_elanprv2.2.sh > "$1"; sleep 1; q
You are not sending q to the shell script. The above commands wait for the shell script to finish, then execute a sleep, then try to execute a program named q (which probably doesn’t exist).
Since the test_elanprv2.2.sh shell script probably accepts commands by reading its standard input, you want to send the q command to the standard input of that process:
ProcessBuilder pb =
new ProcessBuilder("/bin/bash", "-c", "./test_elanprv2.2.sh");
pb.redirectOutput(new File(fileName + ".txt"));
Process start = pb.start();
Thread.sleep(1000);
try (Writer commands = new OutputStreamWriter(start.getOutputStream())) {
commands.write("q\n");
}
// Caution: Call this AFTER writing commands. You don't want to write
// to the standard input of a process that has already finished!
start.waitFor();

How to get command prompt output in java?

How to get the output of the command prompt which means i have opend a command prompt like this.
Process process = Runtime.getRuntime().exec("cmd /c start cmd.exe /K \"C:\\Editor\\editorTemp.exe\"");
i can not get the cmd output like this
BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line = null;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
So how can i get the command prompt output ?
This is not Java question. Basically what you doing is running Java (Java Main Process A) and from it starting another process (Windows CMD B). This is fine and you can get input/output streams of this process (B) in Java(A).
However this process (B) starts another process (again Windows CMD C) with its own standard input/output. This process has nothing common with processes A&B and uses Windows' standard Input/Output streams. So, there are no connections between A and C.
I'm not sure but I think there are some ways to run Windows CMD with different or not standard IO. Maybe something like this will work:
cmd <tty >tty
but there is no tty in Windows. Pragmatically you can do this as described here - Creating a Child Process with Redirected Input and Output but that would not work for regular CMD.
Nevertheless it became even more problematic when you start your own process from the editorTemp.exe (process D). D has even more disconnection with process A. And all for what? What don't you simply start process D directly from A and have full control on the IO streams and process itself?
Here is good example how to do so.
Your java thread is working independently of CMD call. The java code is beating the STDOUT pipe before anything is written.
If you call Process.waitFor(), it will wait until the CMD call is done. The STDOUT should be in the buffer, and then you can read it.
When you do a readLine(), your java thread is blocked until you have an actual full line or the input stream is closed.
If the program prints a partial line (without CR or LF at the end), and then waits for input, the readLine will be stuck.
So you will need to read character by character, until you think the proces has no more things to say.
See e.g. Is it possible to read from a InputStream with a timeout?
import java.util.Scanner;
Inside the main write this.
Scanner output = new Scanner(System.in);
System.out.println(“Enter your name”);
String name = output.next();
If you want user to enter an int then you need to do this.
int number = output.nextInt();

Executing a WMIC command line argument in Java

I'm trying to use the WMIC command 'DESKTOP' in an application through a method which is implemented from an interface. Before using WMIC commands in cmd, it's necessary to first input 'WMIC' and press enter, as I'm sure you all know. I can do this just fine, however after the command line enters WMIC mode, I need to subsequently enter the DESKTOP command, or any other WMIC command (see list : http://ss64.com/nt/wmic.html). Here's the overridden method code I'm currently using to enter WMIC mode :
#Override
public void Desktop(){
try {
Runtime rt = Runtime.getRuntime();
String cmd = "cmd /c WMIC";
Process pr = rt.exec(cmd);
}
Perhaps I'm going about this the wrong way? Any help would be much appreciated.
You need to get the output stream of the process you started and write to it.
OutputStream os = pr.getOutputStream();
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os));
bw.write("DESKTOP");
bw.newLine();
bw.flush();
If you need to see the output/errors from the process you started you need to use getInputStream() and getErrorStream() methods to get the output/error streams and read from it.
Check the javadoc of Process for more details.
http://docs.oracle.com/javase/7/docs/api/java/lang/Process.html
Pay special attention to this part -
By default, the created subprocess does not have its own terminal or
console. All its standard I/O (i.e. stdin, stdout, stderr) operations
will be redirected to the parent process, where they can be accessed
via the streams obtained using the methods getOutputStream(),
getInputStream(), and getErrorStream(). The parent process uses these
streams to feed input to and get output from the subprocess. Because
some native platforms only provide limited buffer size for standard
input and output streams, failure to promptly write the input stream
or read the output stream of the subprocess may cause the subprocess
to block, or even deadlock.
It's a fairly silly answer.
WMIC is normally used in non interactive mode.
wmic desktop get
However you are a programmer. Therefore you are supposed to program.
Here's vbs script that does it. The help is filled with sample scripts in C, VB, VBS, and JScript.
Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
Set colItems = objWMIService.ExecQuery("Select * From Win32_Desktop")
For Each objItem in colItems
msgbox objItem.Name & " " & objItem.Wallpaper
Next

multiple processbuilder in a programme

i want to use two process builder in same program how can i do this both processbuilder are performing different task on same file how can i execute both together.
ProcessBuilder pb = new ProcessBuilder( "nusmv ", "inputfile.smv");
Process p = pb.start();
ProcessBuilder pb123 = new ProcessBuilder("nusmv","-int","inputfile.smv");
Process process123 = pb123.start();
it is just executing first processbuilder second one has been ignored.
There are two issues which I can think of:
1. call process.waitFor() for the process so that each one gets completed before starting other.
2. Flush the output and error streams of one process before proceeding to the other.Many operating systems provide limited memory buffer for standard input and output streams. So if you do not flush or process the streams immediately they might cause the process to block or even deadlock.
For more info u can refer this link.
http://www.javaworld.com/article/2071275/core-java/when-runtime-exec---won-t.html

Categories