I'm trying to run Handbrake through a Java app I'm writing, and am having trouble waiting for Handbrake to finish.
When I try this :
ProcessBuilder builder = new ProcessBuilder(
"cmd.exe", "/c", command);
Process p = builder.start();
BufferedReader inputreader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = null;
while((line = inputreader.readLine()) != null)
{
System.out.println(line);
}
The output I get is :
Encoding: task 1 of 1, 0.00 %
Over and over, and the file never gets converted.
When I change it to the following:
BufferedReader inputreader = new BufferedReader(new InputStreamReader(p.getInputStream()));
BufferedReader errorreader = new BufferedReader(new InputStreamReader(p.getErrorStream()));
String line = null;
String line2 = null;
while((line = inputreader.readLine()) != null && (line2 = errorreader.readLine()) != null)
{
System.out.println(line);
System.out.println(line2);
}
It works on my test files, however it gets hung-up when the errorreader runs out of lines to read and the readLine() locks the thread infinitely. On full length files the file gets converted but this portion of code gets locked so it never continues with the application.
Any suggestions?
Call builder.redirectErrorStream(true); before creating the process (this will merge the input and the error stream into one: the input stream), and only read from the InputStream.
That should solve the problem of the error stream running out of data before the input stream.
If you do want to keep them separate, then you can start two threads, on to read from the input stream and one from the error stream.
Related
I am new to java and i am calling a Python script from java using processbuilder and trying read python output in java.
ProcessBuilder pb = new ProcessBuilder(Arrays.asList("python","PyScript.py",""+path));
Process p = pb.start();
String line;
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
while ((line = in.readLine()) != null)
{
System.out.println(line);
logger.debug("Value of python output is"+line);
System.out.println("in while loop");
}
readline is getting null. when i run through command prompt its running fine.
I want to write a java program to retrieve the status of all the services running on different servers (approx 20). For this i am using SC command, i am able to do so using the java program. But now i am stuck in a situation where i want to run the SC command as a different user by using RUNAS, the problem that i am facing is that i am not able to input the password once the command has been executed for the first time. Following is the code that i am using :-
String[] command = new String[3];
command[0] = "cmd";
command[1] = "/c";
command[2] = "runas /noprofile /user:domain\\admin \"sc \\\\serverName queryex type= service state= all\"";
Process p = Runtime.getRuntime().exec(command);
PrintWriter writer = new PrintWriter(new OutputStreamWriter(new BufferedOutputStream(p.getOutputStream())), true);
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = reader.readLine();
while (line != null) {
new PrintWriter(p.getOutputStream(),true).println("AdminPassword");
System.out.println(line);
line = reader.readLine();
}
BufferedReader stdInput = new BufferedReader(newInputStreamReader(p.getInputStream()));
BufferedReader stdError = new BufferedReader(new InputStreamReader(p.getErrorStream()));
String Input;
while ((Input = stdInput.readLine()) != null) {
System.out.println(Input);
}
String Error;
while ((Error = stdError.readLine()) != null) {
System.out.println(Error);
}
But i am not been able to print the states of all the services. I am not not sure after providing the password whether i need to capture some other stream or else??
Any help on this?
Thanks
In your while (line != null) loop you open a new PrintWriter for each line you read. You print the admin password to these writers, but never close or flush them.
Try the PrintWriter writer you created above, and flush() it after writing the password, otherwise it will still be in the buffer.
You also create several BufferedReader on the inputStream of the Process, which might interfere with each other.
So: create only one reader resp. writer for the inputStream, errorStream and outputStream of the Process.
I have a weird problem when trying to execute a shell command from within a java program. Since there exist thousands of websites that explain how to do it I used the following recommended code:
public String executeShellCommand (String command)
{
try
{
StringBuffer sb = new StringBuffer();
String line = "";
Process p = Runtime.getRuntime().exec(command);
BufferedReader reader = new BufferedReader(new InputStreamReader(
p.getInputStream()));
while ((line = reader.readLine()) != null)
sb.append(line + "\n");
p.waitFor();
return sb.toString();
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
Acutally, when I try to execute for instance ls -aF is works fine and I get some output as a result. Therefore I'm pretty sure that the above code is, in principal, correct. However, I got another program I'd like to run and that produces a file as an output. I would like to execute it the above way but it never is executed and no output file is generated. Also I do not get any error, warnings or whatsoever in java. When copy and pasting the actual command argument string into the console the execution of the programm/command directly in the shell works fine and the output file is generated. So the command I pass to the method is also correct.
Are there additional things I need to pay attention to when trying to execute a shell command from within java?
UPDATE: I modified my code according to the suggestions. However, it is still hanging:
public String executeShellCommand(List<String> command, String logfile, boolean waitForProcess) { try {
ProcessBuilder pb = new ProcessBuilder(command);
System.out.println("pb.toString() = " + pb.toString());
Process p = pb.start();
System.out.println("2");
BufferedReader err = new BufferedReader(new InputStreamReader(p.getErrorStream()));
BufferedReader out = new BufferedReader(new InputStreamReader(p.getInputStream()));
System.out.println("3");
StringBuilder errSb = new StringBuilder();
StringBuilder outSb = new StringBuilder();
String line;
System.out.println("4");
while ((line = err.readLine()) != null) { // <--- code hangs here
errSb.append(line + "\n");
System.out.println("errSb = " + errSb.toString());
}
System.out.println("4a");
while ((line = out.readLine()) != null) {
outSb.append(line + "\n");
System.out.println("outSb = " + outSb.toString());
}
System.out.println("5");
if(waitForProcess) {
System.out.println("Wait for process");
p.waitFor();
} else {
System.out.println("Sleep 5000");
Thread.sleep(5000);
}
System.out.println("6");
//Log result to file
if(logfile != null) {
OutputStreamWriter outWriter = new OutputStreamWriter(new FileOutputStream(logfile));
outWriter.write(errSb.toString());
outWriter.close();
}
return errSb.toString();
} catch(Exception e) { e.printStackTrace(); } return null; }
This will block if your command writes too many characters to stderr. Like for sdtout, Java redirect stderr through a pipe, and if you do not read the pipe, it can fill up and block (size of the pipe is probably less than 256 bytes). To avoid that, you need to read from the Process.getErrorStream(), preferable from another thread as the main thread is busy reading from the Process.getInputStream().
A simpler way to avoid that is to use the ProcessBuilder class instead of Runtime.exec() and ProcessBuilder.redirectErrorStream(true) so that both stdout and stderr are merged into the Process.getInputStream()
As per Process javadoc :
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.
You are calling p.waitFor(). If we carefully read the waitFor() documentation:
Causes the current thread to wait, if necessary, until the process represented by this Process object has terminated.
You are waiting for a process which hangs, because its error stream and output stream are never read.
What you should do, is to read these streams:
p.start();
BufferedReader err= new BufferedReader(new InputStreamReader(p.getErrorStream()));
BufferedReader out = new BufferedReader(new InputStreamReader(p.getOutputStream()));
StringBuilder errSb = new StringBuilder();
StringBuilder outSb = new Stringbuilder();
String line;
while ((line = err.readLine()) != null) {
errSb.append(line);
}
while ((line = out.readLine()) != null) {
outSB.append(line);
}
int retCode = p.waitFor(); //0 for success
System.out.println(retCode);
System.err.println(errSB.toString());
You should always read the error stream when calling external programs via the Process class, else you may find yourself in this odd situation where a process hangs forever. (well until someone else -the operating system, another application, etc- kills it, more exactly).
I've also noticed that you use the Runtime.getRuntime() which is not the recommended way to run external programs, starting with java 1.5, as per javadoc:
As of 1.5, ProcessBuilder.start() is the preferred way to create a Process.
ProcessBuilder pb = new ProcessBuilder("ls" , "-aF");
Process p = pb.start();
I am writing a javacode to call a interactive shellscript and using process builder for calling shellscript. I know that to pass parameters to this shell script i have to take its inputstream to check it's output and need to use output stream to pass command to it. My question is that how would I know using Input Stream that it's prompting for entering values ?
My code :
ProcessBuilder pb2=new ProcessBuilder("/home/abhijeet/sample1.sh","--ip="+formobj.getUpFile().getFileName(),"--seqs="+seqs);
script_exec = pb2.start();
OutputStream in = script_exec.getOutputStream();
InputStreamReader rd=new InputStreamReader(script_exec.getInputStream());
pb2.redirectError();
BufferedReader reader1 =new BufferedReader(new InputStreamReader(script_exec.getInputStream()));
StringBuffer out=new StringBuffer();
String output_line = "";
while ((output_line = reader1.readLine())!= null)
{
out=out.append(output_line+"/n");
System.out.println("val of output_line"+output_line);
//---> i need code here to check that whether script is prompting for taking input ,so i can pass it values using output stream
}
Is there any way to know directly that script is waiting for an input from user?
Read the process output stream and check for the input prompt, if you know the input prompt, then put the values to process inupt stream.
Otherwise you have no way to check.
ProcessBuilder pb = new ProcessBuilder(command);
pb.redirectErrorStream(true);
BufferedReader b1 = new BufferedReader(new InputStreamReader(p.getInputStream()));
BufferedWriter w1 = new BufferedWriter(new OutputStreamWriter(p.getOutputStream()));
String line = "";
String outp = "";
while ((line = b1.readLine()) != null) {
if (line.equals("PLEASE INPUT THE VALUE:")) {
// output to stream
w1.write("42");
}
outp += line + "\n";
}
...
UPD: for your code it should be something like that
ProcessBuilder pb2=new ProcessBuilder("/home/ahijeet/sample1.sh","--ip="+formobj.getUpFile().getFileName(),"--seqs="+seqs);
script_exec = pb2.start();
OutputStream in = script_exec.getOutputStream();
InputStreamReader rd=new InputStreamReader(script_exec.getInputStream());
pb2.redirectError();
BufferedReader reader1 =new BufferedReader(new InputStreamReader(script_exec.getInputStream()));
StringBuffer out=new StringBuffer();
String output_line = "";
while ((output_line = reader1.readLine())!= null)
{
out=out.append(output_line+"/n");
System.out.println("val of output_line"+output_line);
//---> i need code here to check that whether script is prompting for taking input ,so i can pass it values using output stream
if (output_line.equals("PLEASE INPUT THE VALUE:")) {
// output to stream
in.write("42");
}
}
I'm trying to run /usr/bin/perl -e 'for(my $i=0;$i<1000;$i++){print 1x1000;print STDERR 2x1000;}' (which works in terminal) with my program.
ProcessBuilder pb = new ProcessBuilder(go); //go is the command
process = pb.start();
BufferedReader incommandbuf = new BufferedReader(new InputStreamReader(process.getInputStream()),1024*1000);
BufferedReader errcommandbuf = new BufferedReader(new InputStreamReader(process.getErrorStream()),1024*1000);
stdString = "";
while ((line = incommandbuf.readLine()) != null)
{
stdString += line + "\n";
}
String errorstrtemp = "";
while ((line = errcommandbuf.readLine()) != null)
{
errorstrtemp += line + "\n";
}
If I try to run this it hangs on while ((line = incommandbuf.readLine()) != null). The program runs if I change the command to /usr/bin/perl -e 'for(my $i=0;$i<64;$i++){print 1x1000;print STDERR 2x1000;}'. If it goes up to 65 and higher it doesn't work. At first I thought I just have to change the size of the my BufferedReaders but it didn't help. Any clue on what is causing this? I will provide any additional info if needed.
Thanks.
You are reading one stream at a time. When the other stream fills up the buffer, your Process will stop waiting for you to read it. The solution is to either read the streams in different threads or use ProcessBuilder.redirectErrorStream