Java runtime.exec does not execute correctly - java

I am getting an exe-File, which I have to execute using Java (Version 6) on Windows Server 2008 R2. Now there is s problem I do not really understand. When executing the file with the commandline
"C:\test.exe param1 param2"
it works correctly, but when I execute the file with
Process proc = Runtime.getRuntime().exec("C:\\test.exe param1 param2");
proc.waitFor();
I can see the test.exe in the windows task manager and it starts running (it creates a log which states that), but then it simply doesn't do anything anymore. The test.exe endlessly runs with 0% and I have to kill the process manually. After doing so the java-program continues and
proc.exitValue()
is "1", therefore java recognizes that I have killed the process. I also tried writing the commandline in a batchfile and executing it with .exec() but it didn't change anything.
What really confuses me, is that it runs perfectly via windows command-line, but does not via .exec(). Does anyone have an idea what might cause such a problem? Or is it more likely that the test.exe is causing the problem?
In best regards
Edit: Wrote down the wrong path in .exec

Since your program procudes a lot of output, my hypothesis is that it is stuck trying to write to the standard output (which is a pipe under Linux, don't know for Windows).
Try this:
final byte[] devnull = new byte[1024];
final ProcessBuilder builder = new ProcessBuilder("C:\\test.exe", "param1", "param2")
.redirectErrorStream(true);
final Process p = builder.start();
final InputStream stdout = process.getInputStream();
// Purge stdout
while (stdout.read[devnull] != -1);
// Grab the process' exit code here

As fge pointed out in https://stackoverflow.com/a/21903969 , it is important to consume all the output that is produced by the process - not only on Linux, but also on Windows, and not only the standard output, but also the possible errors.
The general pattern for this could look like this:
private static void runCommand(String command) throws IOException
{
Process process = Runtime.getRuntime().exec(command);
String errorMessage =
new String(toByteArray(process.getErrorStream()));
String outputMessage =
new String(toByteArray(process.getInputStream()));
int exitValue = 0;
try
{
exitValue = process.waitFor();
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
}
System.out.println("Output message: "+outputMessage);
System.out.println("Error message: "+errorMessage);
System.out.println("Exit value: "+exitValue);
}
private static byte[] toByteArray(
InputStream inputStream) throws IOException
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte buffer[] = new byte[8192];
while (true)
{
int read = inputStream.read(buffer);
if (read == -1)
{
break;
}
baos.write(buffer, 0, read);
}
return baos.toByteArray();
}

"C:\test.exe param1 param2"
You have a tab in there. Try this:
"C:\\test.exe param1 param2"
If the process produces any output on either stdout or stderr you need to consume it. Otherwise it can block.

Related

Runtime exec Process hangs

I'm trying to make a javafx application which displays the logs from kubernetes. I'm using Runtime.getRuntime().exec to get the output of kubetail servicename in the following way:
Process exec = Runtime.getRuntime().exec(new String[]{"kubetail", "serviceName"});
InputStream inputStream = exec.getInputStream();
int read;
try {
while (((read = inputStream.read()) != -1)) {
System.out.print((char) read);
}
} catch (IOException e) {
e.printStackTrace();
}
The process starts normally and I see "Will tail 2 pods...". The problem appears when I make a request on that service and a lot of logs appears. The service returns a report and this report is archived and it is also logged. The report is quite big, 40K characters. The tail somehow hangs in the middle of the logged report and I don't get any more data, but if I send another request, it continues from where it hanged and stops again on the report of the second request.
I tried using buffered reader and also wrapped the inputstream with NIO, but the problem persist. I also tried to use kubectl logs and this works on but I don't have any identification for the line of log (I don't know which pod it belongs to). kubetail gives a stream of logs for all the pods in an application and these are identified by color and by pod name.
Thank you
You are not consuming the stdout and stderr streams properly, they will cause process output to hang if one fills without you reading it. You could try setting stderr to go to stdout class when using ProcessBuilder
ProcessBuilder pb = new ProcessBuilder(new String[]{"kubetail", "serviceName"});
pb.redirectErrorStream(true);
Process exec = pb.start();
... Your reading code
int rc = exec.waitFor();
OR: add threads to consume both stdout and stderr streams:
Process exec = Runtime.getRuntime().exec(new String[]{"kubetail", "serviceName"});
new Thread(() -> copy(exec.getInputStream(), System.out), "STDOUT").start();
new Thread(() -> copy(exec.getErrorStream(), System.err), "STDERR").start();
int rc = exec.waitFor();
with method:
static void copy(InputStream in, OutputStream out)
{
try(var autoClose = in; var autoClose2 = out)
{
in.transferTo(out);
}
catch(IOException io)
{
throw new UncheckedIOException(io);
}
}

How to run another java process with console in java (in Linux)

How can I run an another java process (with a console) in Linux?
I know I should use ProcessBuilder or Runtime.getRuntime().exec() to execute a command,
but I want to know how I can show an separate X-window command terminal (ex. /usr/bin/xterm) or at least show an console-terminal looking window and run a java process with stdin,stdout,stderr on that.
Is there any solution? I heard the Process.getOutputStream() doesn't work on xterm, but haven't tried it yet (Because I'm using Windows..)
EDIT: The java program I want to execute uses jline, which uses JNI...
To start terminl you can simply run following code:
new ProcessBuilder("/usr/bin/xterm").start();
But I guess, that is not you really need. Because, even you pass command to run, let's say ls as argument:
new ProcessBuilder("/usr/bin/xterm", "-e", "ls").start();
You will get output from xterm(not from ls). If you want to start process and get output, you need to do it like that:
public static void main(String[] args) throws Exception {
// get content of the "/usr/bin" directory
ProcessBuilder pb = new ProcessBuilder("ls", "/usr/bin");
pb.redirectErrorStream(true);
Process p = pb.start();
InputStream is = p.getInputStream();
System.out.println(toString(is));
is.close();
}
public static String toString(InputStream is) throws IOException {
Writer writer = new StringWriter();
char[] buffer = new char[1024];
Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
int n;
while ((n = reader.read(buffer)) != -1) {
writer.write(buffer, 0, n);
}
return writer.toString();
}

Approach to implement Windows cmd communication - multiple commands

I'm trying to find a solution how to implement a multiple command - response interaction with the Windows cmd shell. Example:
Start the cmd shell
"dir"
wait for and Handle input
Execute new command depending on the input content
wait for and Handle input
etc.
PLEASE NOTE! Steps above were only to describe the way of communication, it is NOT my intention to browse the file system, i.e. the actual commands could be something else.
Approach so far:
try {
Runtime rt = Runtime.getRuntime();
p = rt.exec("cmd");
error = p.getErrorStream();
input = p.getInputStream();
output = new PrintStream(p.getOutputStream());
StreamGobbler errGobbler = new StreamGobbler(error, "ERROR");
StreamGobbler inGobbler = new StreamGobbler(input, "INPUT");
errGobbler.start();
inGobbler.start();
output.println("dir");
output.flush();
sleep(5);
output.println("dir");
output.flush();
} catch (IOException e) {
System.out.println(e.printStackTrace());
}
StreamGobbler class:
class StreamGobbler extends Thread
{
InputStream is;
String type;
ArrayList<String> cmdRespArr = new ArrayList<String>();
StreamGobbler(InputStream is, String type) {
this.is = is;
this.type = type;
}
public void run() {
try {
InputStreamReader isr = new InputStreamReader(is);
BufferedReader bf = new BufferedReader(isr);
String line = null;
while ( ( line = bf.readLine() ) != null ) {
cmdRespArr.add(line);
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
In this example however the while loop in the run method never returns between the issued commands (this is not part of the problem).
So, would the approach be to let the while method add the line read to a collection or other container, and then monitor that one for something indicating that the cmd shell is ready for input (which would in turn indicate that all available input from previous command have been read). And then fire off the next command?
In the example above this indication would get rid off the sleep call which right now is only there for debugging purposes.
I have a vague memory that this was the approach when doing it with Python.
Or is this totally wrong?
Will it be a solution to start multiple command processors, i.e. one per command?
I'm asking because with keeping one command processor open, it is very hard to determine when a command has been processed, unless you parse the output line by line and wait until you see the prompt in the output.
With multiple processors, i.e. executing "cmd /c dir" then input output redirs will close when the command has completed (and the associated process terminated).
Of course this will not work, if some commands depend on others, e.g. doing a chdir and expecting the next command to work in that dir.

Processbuilder without redirecting StdOut

Is it possible to redirect the output stream back into a process, or not redirect it at all?
The backstory:
I am trying to launch an executable using processbuilder.
(Source dedicated server / srcds.exe to be exact)
As a result of launching it with the processbuilder, the console window of this executable remains empty. A few seconds after launch, the executable crashes with the error "CTextConsoleWin32::GetLine: !GetNumberOfConsoleInputEvents" because its console is empty.
I think you're talking about making the launched process' stdout go to the current process' stdout. If you're using JDK7, that's as simple as:
.redirectOutput(ProcessBuilder.Redirect.INHERIT)
Update: (too much for a comment) I think you're confused. When you launch a process from a terminal, the process becomes a child of that terminal process, and the stdout is sent to that terminal. When you launch a process from Java, then the process is a child of the Java process, and its stdout goes to Java.
In the first case, there's a terminal showing stdout because you launched it from a terminal yourself, and that's what terminals do with stdout. When launching from Java, however, there wouldn't be a terminal window unless something in the process you launched opened a terminal, and stdout of the process you launched is handed back to you, the programmer, to do with as you will. The equivalent behavior to what you see when launching from a terminal is the Redirect.INHERIT that I already mentioned.
Your problem right now isn't Java. Your problem is not understanding how this "srcds.exe" expects stdin and stdout to be handled. Figure that out, and then come back and ask how to do that with Java.
I'm just guessing now, but you could try reading from the process' stdout and feeding it back into the stdin. Maybe that's what it's expecting? That sounds crazy, though.
you can get the output like this
ProcessBuilder pb = new ProcessBuilder(args);
Process p = pb.start();
//below code gets the output from the process
InputStream in = p.getInputStream();
BufferedInputStream buf = new BufferedInputStream(in);
InputStreamReader inread = new InputStreamReader(buf);
BufferedReader bufferedreader = new BufferedReader(inread);
String line;
while ((line = bufferedreader.readLine()) != null) {
*do something / collect output*
}
I've been struggling with this for a while, but the simplest i guess you could do is by transferring the streams yourself, using some kind of StreamTransfer class, you can say which InputStream gets written to which OutputStream in separate threads to avoid deadlocks.
In this example, i execute ls then cat and manually wire the stdout of ls into the stdin of cat, and then cat's stdout to System.out to print the final result :
public class TestRedirectingStreams {
public static void main(String[] args) throws IOException, InterruptedException {
ExecutorService threads = Executors.newFixedThreadPool(10);
Process echo = Runtime.getRuntime().exec("ls"),
cat = Runtime.getRuntime().exec("cat");
threads.submit(StreamTransfer.transfer(echo.getInputStream(), cat.getOutputStream()));
threads.submit(StreamTransfer.transfer(cat.getInputStream(), System.out));
threads.shutdown();
}
}
class StreamTransfer implements Callable<Void> {
public static final int BUFFER_SIZE = 1024;
private InputStream in;
private OutputStream out;
public static StreamTransfer transfer(InputStream in, OutputStream out) {
return new StreamTransfer(in, out);
}
private StreamTransfer(InputStream in, OutputStream out) {
this.in = in;
this.out = out;
}
#Override
public Void call() throws Exception { // write to streams when thread executes
byte[] buffer = new byte[BUFFER_SIZE];
int read = 0;
while ((read = in.read(buffer)) != -1)
out.write(buffer, 0, read);
in.close();
out.close();
return null;
}
}
If you want to use ProcessBuilder instead the example stays the same, since ProcessBuilder.start() returns the process you can still retrieve the desired streams and transfer accordingly.

Forking a process in Java redirecting input/output/error stream efficiently

in a bash script, if I execute an external program (i.e. 'bash') that is executed "inline". I mean that the process is not spanned in background and the stdin/stdout/stderr of the child process coincide with the one of the bash script itself.
So if my bash script contains
#!/bin/sh
bash
what happens is that the user can run my script and when bash is executed he will be able to type commands in the standard input of bash and see the results of the command on stdout/stderr.
This is what I mean that the child process is run "inline".
In java the process is spanned in background, so the Process.getOutputStream()/Process.getInputStream()/Process.getErrorStream are not "inline" with System.in/System.out/System.err.
What I want to do in my java program is to reproduce the behavior of what happens when I execute a process as in the above example of the bash script.
After some google searches I come out with this
public static void main(String[] args) throws IOException,
InterruptedException {
String[] cmdarray = {"bash"};
Process proc = Runtime.getRuntime().exec(cmdarray);
StreamGobbler errorGobbler = new StreamGobbler(proc.getErrorStream(),
System.err);
StreamGobbler outputGobbler = new StreamGobbler(proc.getInputStream(),
System.out);
StreamGobbler inputGobbler = new StreamGobbler(System.in,
proc.getOutputStream());
errorGobbler.start();
outputGobbler.start();
inputGobbler.start();
int exitVal = proc.waitFor();
errorGobbler.join(); // Handle condition where the
outputGobbler.join(); // process ends before the threads finish
System.exit(exitVal);
}
class StreamGobbler extends Thread {
InputStream is;
OutputStream os;
StreamGobbler(InputStream is, OutputStream os) {
this.is = is;
this.os = os;
}
public void run() {
try {
int c;
while ((c = is.read()) != -1) {
os.write(c);
os.flush();
}
} catch (IOException x) {
throw new RuntimeException(x);
}
}
}
But hey, there are 3 threads! plus the thread spanned by executing the process!
There must be a better way. Something like:
Runtime.execForeground("bash", System.in, System.out, System.err);
or just:
Runtime.execForeground("bash");
which executes the process "inline" as it works with many script languages.
Maybe another possibility is to use nonblocking I/O to have the stdin/stdout/stderr copied to System.in/out/err in a single thread? Any example?

Categories