Runtime exec Process hangs - java

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);
}
}

Related

Java runtime.exec does not execute correctly

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.

Redirecting output of a process with Process Builder

I am trying to run plink in my own console window. I started by using Process.exec() and that worked fine. The I moved to using ProcessBuilder and now the output is not sent out until I kill the process.
My code looks like this:
class ConsoleOutputThread extends Thread {
public void start(String processName) {
// this was old code: Runtime r = Runtime.getRuntime();
try {
builder = new ProcessBuilder("plink", "-ssh", "192.168.3.21");
builder.redirectErrorStream(true);
process = builder.start();
//this was old code: process = r.exec (processName);
} catch (IOException ex) {
}
start();
}
#Override
public void run() {
try {
BufferedReader is = new BufferedReader(new InputStreamReader(process.getInputStream()));
writer = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
try {
process.waitFor();
} catch (InterruptedException ex) {
}
char b[];
b = new char[1];
while(is.read(b, 0, 1)> 0) {
// this is for debug, normally sent to console
System.out.println("Got character: " + b[0]);
}
} catch (IOException ex) {
}
}
}
So, when using Runtime.exec() everything worked fine. Now, with ProcessBuilder, the read function blocks forever (actually until I kill the process, when everuthing is spitted out). However, the error stream works, i.e. if I put a bad option I get the messages in the console.
I am probably missing something here and looking for help.
Thank you
You've set the plink process to write its output to a pipe which is connected to the java process. Anything output by the plink process will be saved in an operating-system buffer until your process reads it. The OS buffer has a limited capacity,, and if plink writes too much data, then it will block until your process reads some data from the buffer.
Unfortunately, the java process waits for the plink process to complete before reading anything from the pipe. So, if the plink process writes too much output, it will block indefinitely.
You should change the java logic to read the plink process's output before calling waitfor().

Error handling in java

Am running .exe file from java code using ProcessBulider, the code I have written is given below. The .exe file takes Input.txt(placed in same directory) as input and provide 3 output file in same directory.
public void ExeternalFileProcessing() throws IOException, InterruptedException {
String executableFileName = "I:/Rod/test.exe;
ProcessBuilder processBuilderObject=new ProcessBuilder(executableFileName,"Input.txt");
File absoluteDirectory = new File("I:/Rod");
processBuilderObject.directory(absoluteDirectory);
Process process = processBuilderObject.start();
process.waitFor();
}
this process is working fine by call ExeternalFileProcessing(). Now am doing validation process, If there is any crash/.exe file doesn't run, I should get the error message how can I get error message?
Note: It would be better that error message be simple like run successful/doesn't run successful or simply true/false, so that I can put this in If condition to continue the remaining process.
You can add exception handlers to get the error message.
public void externalFileProcessing() {
String executableFileName = "I:/Rod/test.exe";
ProcessBuilder processBuilderObject = new ProcessBuilder(
executableFileName, "Input.txt");
File absoluteDirectory = new File("I:/Rod");
processBuilderObject.directory(absoluteDirectory);
try {
Process process = processBuilderObject.start();
process.waitFor();
// this code will be executed if the process works
System.out.println("works");
} catch (IOException e) {
// this code will be executed if a IOException happens "e.getMessage()" will have an error
e.printStackTrace();
} catch (InterruptedException e) {
// this code will be executed if the thread is interrupted
e.printStackTrace();
}
}
But it would be better to handle it in the calling function by put a try catch handler in the calling function and handling it there.
Is it a third party .exe or do you have access to its sources? If so, you could work with basic System outputs (for example couts to the console).
Those outputs can be redirected to your java app using something like this:
InputStream is = process.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line = "";
while ((line = br.readLine()) != null) {
if(line.equals("something")) {
// do something
}
}
br.close();
This is how i do things like that and it works very well in general. But i must admit, that i can not say/garuantee, that this is THE way to do it. A more advanced approach might be the use of StreamGobbler (see Listing 4.5) to handle the outputs of the .exe.
Let me know if it helped you or not.

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.

Categories