Java Runtime.getRuntime() does not print any output or exit code - java

While running
send = Runtime.getRuntime().exec(myscript)
the script runs as intended but when I try to read any sort of output or exit code, I run into some problems
for example, if my .sh script is just the following two lines
#!/bin/bash
exit 10
printing send.exitValue() returns me exit code 0. Which is not what i am expecting.
This is also in fact after waiting for the program to finish with by using waitFor().
I am also attempting to read the input/output/error streams but they are all null with no values, with the following snippets
val stdInput = BufferedReader(InputStreamReader(send.inputStream))
val stdError = BufferedReader(InputStreamReader(send.errorStream))
send.waitFor()
println("Here is the standard output of the command:\n")
var s: String? = null
while (stdInput.readLine().also { s = it } != null) {
println(s)
}
println("Here is the standard error of the command:\n")
val e: String? = null
while (stdError.readLine().also { s = it } != null) {
println(e)
}
The program I am writing is heavily dependent on the error codes to determine where the program failed if such a case happened. I cannot seem to find out how or why I get 0 as my exit code regardless

Related

My code keeps hanging when it hits Thread.sleep(), and stuck in a while loop, how do I fix my code in order to run properly?

Thank you for all your help. I tried everything from creating a new Thread. To changing around the way I use the writer.flush() and the writer.newLine(). It seems like the code keeps getting hung up at the while loop, constantly running the Thread.sleep() nonstop. I cannot provide the batch file because it is sensitive information, but the String command variable on top of the code is the path I am using to access the command. Please if you do answer this question please run the code first with test bat file and two input field.
Batch Script:
#ECHO OFF
SET /P _inputname= Please enter an name:
SET /P _inputpassword= Please enter an password:
IF "%_inputpassword%"=="1234" GOTO :they_said_1234
ECHO You entered the wrong password!
pause
GOTO
:they_said_1234
ECHO You entered 1,2,3,4!
pause
Java Code:
import java.io.*;
import java.nio.CharBuffer;
import java.util.ArrayList;
public class command{
public static void main(String[] args) {
//String command="cmd /c d: && cd UPSDATA\\Virtualization Scripts\\EMC ESXi Grab\\EMC-ESXi-GRAB-1.3.7 && GRAB_RUN ";
//String command="cmd /c date";
String command = "cmd /c cd C:\\Users\\HFB2VZN\\Desktop\\folderG";
try {
Process process = Runtime.getRuntime().exec(command);
try (Writer writer = new OutputStreamWriter(process.getOutputStream());
Reader reader = new InputStreamReader(process.getInputStream())) {
CharBuffer buf = CharBuffer.allocate(80);
int tries = 2;
while (process.isAlive()) {
while (reader.ready() && reader.read(buf) > 0) {
//3
System.out.println("buf.flip() ran");
System.out.append(buf.flip());
buf.clear();
}
if (tries-- == 0) {
System.out.println("Destroyed");
process.destroy();
break;
}
//1
writer.write("random");
writer.flush();
while (!reader.ready()) {
//2
System.out.println("while() loop, Thread runs in non stop loop");
Thread.sleep(800);
}
}
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}`
Compared to this example, executing a batch file will be much slower and having multiple commands may imply that there’s no available output for some time before the next output is generated. Not considering this may cause the loops get out of sync and after the process terminated, you must not execute the while (!reader.ready()) {…} loop without checking whether the process is still alive.
Since your batch file’s second GOTO lacks a target (it’s likely supposed to branch backwards), this batch file may terminal earlier than intended. Since no-one reads the error channel, this stays unnoticed. That could be the reason for hanging in that loop. Note further, that you are generating one input to the batch file per loop iteration, but have limited the number of iteration to 2 per tries variable. For a batch file expecting three inputs (name, password, pause), that’s too little.
The main problem is, there is no way to detect whether a subprocess is actually waiting for our input. This is what we have to work-around here. But a temporary stopping of producing output does not always imply that the program now waits for input.
I fixed your last GOTO to jump to the beginning of the batch file and used the following code to make two attempts entering the right password on the second.
try {
Process process = Runtime.getRuntime().exec(command);
try(Writer writer = new OutputStreamWriter(process.getOutputStream());
Reader reader = new InputStreamReader(process.getInputStream())) {
CharBuffer buf = CharBuffer.allocate(80);
int tries = 2;
while(process.isAlive()) {
do {
if(!buf.hasRemaining()) {
buf.flip();
buf = CharBuffer.allocate(buf.capacity()*2).put(buf);
}
do {} while(reader.ready() && reader.read(buf) > 0);
if(buf.position() > 0) {
char c = buf.get(buf.position()-1);
if(c==':' || c=='.') break;
}
long deadLine = System.nanoTime() + TimeUnit.SECONDS.toNanos(1);
for(long remaining = 1;
!reader.ready() && process.isAlive() && remaining > 0;
remaining = deadLine - System.nanoTime()) {
LockSupport.parkNanos(Math.min(
TimeUnit.MILLISECONDS.toNanos(100),
remaining));
}
} while(reader.ready());
String input = buf.flip().toString();
buf.clear();
System.out.print(input);
String value;
if(input.endsWith("name:")) value = "aName";
else if(input.endsWith("password:")) value = tries>1? "wrongPassword": "1234";
else {
value = "<any key>";
tries--;
}
System.out.println("<- "+value);
writer.write(value);
writer.flush();
if(tries == 0) {
System.out.println("Destroying");
process.destroy();
break;
}
}
}
} catch(IOException e) {
e.printStackTrace();
}
Of course, if you can’t fix the GOTO statement, you have to provide the right password at the first attempt.
The code above will wait up to one second for the availability of new output, unless it recognizes one of the expected prompts in the output. And it won’t wait when the process is not alive anymore.

Not receiving output from ProcessBuilder in YARN job

I have a Hadoop YARN cluster set up on some machines at my university (all machines running Linux Fedora 25). When running a mapreduce job in YARN, I am unable to receive the output from a call I make to a separate program. Interestingly, if I run my job locally (configured in mapred-site.xml), my method for calling the program and receiving its output works just fine. Below is my executeShellCommand class, which is instantiated and used in my first map task.
public class ExecuteShellCommand {
public String executeCommand(String command) {
StringBuffer output = new StringBuffer();
Process p;
try {
String [] args = command.split(" ");
String cmd = args[0];
ProcessBuilder pb = new ProcessBuilder().command(cmd, args[1], args[2], args[3], args[4], args[5], args[6], args[7]).directory(new File("path to executable"));
p = pb.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
p.waitFor();
String line = "";
while ((line = reader.readLine())!= null) {
output.append(line + "\n");
}
} catch (Exception e) {
e.printStackTrace();
return e.toString();
}
return output.toString();
}
}
Things I have made sure to check:
1) Permissions are appropriately set for all files/directories needed
2) Map tasks are run as current user (me), so no issues with illegal access
3) I am not receiving a file not found exception, so the path to the program I'm calling is correct
4) Checked the input/output stream for Process p (input stream set as java.lang.UNIXProcess$ProcessPipeInputStream#1000e80, output stream is null)
5) Instead of calling the program I need to use, I have tried a simple "echo" command, and could not receive that output either.
6) I have also tried using
p = Runtime.getRuntime().exec("myCommand")
but the results are the same (no output received)
As I already mentioned, when I run a job locally, my executeCommand method functions perfectly, and returns the output from the program I call. Only in YARN do the issues occur. I have a feeling that the problem caused by either not reading from the correct buffer, or the command issued to ProcessBuilder is never actually executed. I am quite stumped as to how to debug what is going on here, and any tips would be greatly appreciated!
After hours of trying all sorts of solutions, I figured out how to get the error stream from the process spawned with ProcessBuilder. Turns out when I set the working directory for the process, I forgot to update the path to one of the arguments I was passing it. D'oh!!!

Some Perl lines won't execute from Java Runtime.getRuntime().exec()

I have a strange behavior when calling a perl script from Java.
The perl code is:
#!/usr/bin/env perl
use warnings;
print "dada\n";
$file = "settings.txt";
open $ds, "<", $file;
print "doudou\n";
while ($line = <$ds>){
print "$line\n";
last if $. == 4;
}
print "dodo\n";
close $ds;
As you can see, in this code I want to read the file "settings.txt" which contents :
1 : a
2 : b
3 : c
4 : d
And this script works when called from cygwin and the output is :
dada
doudou
1 : a
2 : b
3 : c
4 : d
dodo
But when I call it from Java using the following code:
String line;
String cmd = "perl C:\\Data\\Tests\\match_settings.pl";
try {
Process proc = Runtime.getRuntime().exec(cmd);
BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream()));
while ((line = in.readLine()) != null) {
System.out.println("Line :" + line);
}
in.close();
} catch (IOException a) {
// TODO Auto-generated catch block
a.printStackTrace();
}
Then when the Java function is executed, I get the output:
Line :dada
Line :doudou
Line :dodo
Which means that $line = <$ds> is not well executed, but I don't know why.
Any idea ?
Sorry for the long post, wanted to be as precise as possible.
PS : I know some of you are wondering "Why not read the settings.txt file by Java code himself ?" but that's not the question here ;)
It seems that the folder from which your perl code is executed is not the same when executing from Cygwin and Java. So the perl Script can't find your file.
You should add error checking in your perl code, and verify your current working dir directory.
I would write:
#!/usr/bin/env perl
use warnings;
use Cwd;
my $cwd = cwd();
print "dada\n";
print "In folder: $cwd";
$file = "settings.txt";
open ($ds, "<", $file) || die "Can't open settings.txt. Error code: $!";
print "doudou\n";
while ($line = <$ds>){
print "$line\n";
last if $. == 4;
}
print "dodo\n";
close $ds;
This will help you debug why, when your script is executed from Java, you don't get the expected results.
You should also redirect stderr to stdout in your Java code, so you will get error lines.

Displaying DOS error message using Rhino/JavaScript/Java

I am trying to run a DIR command using a program similar to Rhino language (the program is using Java/JavaScript).
If there are any syntax error or any other error, I want to print out the error message using the function below:
function CatchDOSError()
{
var ErrorMSG = new java.io.BufferedReader(new java.io.InputStreamReader(java.lang.Runtime.getRuntime().exec("cmd /c dir \\C:\Test\Data /s /b /a-D > c:\Test\fileRunDIR.txt").getErrorStream()));
while (( ErrorMSG.readLine()) != null)
{
println(ErrorMSG);
}
ErrorMSG.close();
}
If I run this function, all what I get as an output in the console is: "java.io.BufferedReader#71fbs019".
If I run the DOS command from CMD manually, the error message is "The System cannot find the path". This is the error message which I want it to be displayed using the function above. Not sure if there are conversion issues or something wrong with my function.
Any help is appreciated, thank you.
When you do
while (( ErrorMSG.readLine()) != null) {
println(ErrorMSG);
}
You discard the line after checking it wasn't null and then print the Reader itself (which doesn't override toString() and isn't the line). I think you wanted,
var line = "";
while (( line = ErrorMSG.readLine()) != null) {
println(line);
}

Tomcat fails to execute external java program

I am a total newbie in JSPs/Tomcat and to a large extent in Java as well. Here's what I have to do -- when a user clicks a button/visits a URL, I want to launch a Java program (which takes some command line arguments).
I can very easily do
Runtime.exec("C:\\Python27\\python.exe test.py")
OR
Runtime.exec("java -cp %CLASSPATH%;C:\\devprojects HelloWorld"
and this works fine. Where HelloWorld.class just prints "HelloWorld".
However, when I attempt a java program which takes command line arguments, the GET request just hangs doing nothing. I don't know what logs to look for or what could be wrong here. After having spent TWO days on trying various things, I am just about to give up now.
Runtime.exec("java -cp %CLASSPATH%;C:\\devprojects Run --username Blah --password Foo");
What user does Tomcat end up running this java program as? Can I make it to be Administrator? This is on Windows 2008, does UAC interfere with things?
I cannot modify the Run.class here, I HAVE to run it as is and with command line parameters.
Please advise.
One idea: you are relying on the default tokenization of your command line as one complete String, and it is not parsing the last one as you expect. Instead you should use the form of this method that takes a String[], after you have chopped up the command line yourself:
http://docs.oracle.com/javase/6/docs/api/java/lang/Runtime.html#exec(java.lang.String[])
Or, it is waiting for input from you, or waiting for you to read its output. This could explain the hang. Search the internet for the dangers of streams and Runtime.exec().
Consider ProcessBuilder instead.
Remember also that you have to be sure that the executed file dont run "forever", and
if you need to pass some arguments, you could use this:
static String startProcess(String command, String dir) throws IOException {
StringBuffer ret = new StringBuffer();
String[] comm = new String[3];
comm[0] = COMMAND_INTERPRETER[0];
comm[1] = COMMAND_INTERPRETER[1];
comm[2] = command;
long start = System.currentTimeMillis();
try {
//Start process
Process ls_proc = Runtime.getRuntime().exec(comm, null, new File(dir));
//Get input and error streams
BufferedInputStream ls_in = new BufferedInputStream(ls_proc.getInputStream());
BufferedInputStream ls_err = new BufferedInputStream(ls_proc.getErrorStream());
boolean end = false;
while (!end) {
int c = 0;
while ((ls_err.available() > 0) && (++c <= 1000)) {
ret.append(conv2Html(ls_err.read()));
}
c = 0;
while ((ls_in.available() > 0) && (++c <= 1000)) {
ret.append(conv2Html(ls_in.read()));
}
try {
ls_proc.exitValue();
//if the process has not finished, an exception is thrown
//else
while (ls_err.available() > 0)
ret.append(conv2Html(ls_err.read()));
while (ls_in.available() > 0)
ret.append(conv2Html(ls_in.read()));
end = true;
}
catch (IllegalThreadStateException ex) {
//Process is running
}
//The process is not allowed to run longer than given time.
if (System.currentTimeMillis() - start > MAX_PROCESS_RUNNING_TIME)
//this is very important
{
ls_proc.destroy();
end = true;
ret.append("!!!! Process has timed out, destroyed !!!!!");
}
try {
Thread.sleep(50);
}
catch (InterruptedException ie) {}
}
}
catch (IOException e) {
ret.append("Error: " + e);
}
return ret.toString();
}

Categories

Resources