Processbuilder without redirecting StdOut - java

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.

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

Creating named pipes in Java

I am experimenting with creating named pipes using Java. I am using Linux. However, I am running into a problem where writing to the pipe hangs.
File fifo = fifoCreator.createFifoPipe("fifo");
String[] command = new String[] {"cat", fifo.getAbsolutePath()};
process = Runtime.getRuntime().exec(command);
FileWriter fw = new FileWriter(fifo.getAbsoluteFile());
BufferedWriter bw = new BufferedWriter(fw);
bw.write(boxString); //hangs here
bw.close();
process.waitFor();
fifoCreator.removeFifoPipe(fifo.toString());
fifoCreator:
#Override
public File createFifoPipe(String fifoName) throws IOException, InterruptedException {
Path fifoPath = propertiesManager.getTmpFilePath(fifoName);
Process process = null;
String[] command = new String[] {"mkfifo", fifoPath.toString()};
process = Runtime.getRuntime().exec(command);
process.waitFor();
return new File(fifoPath.toString());
}
#Override
public File getFifoPipe(String fifoName) {
Path fifoPath = propertiesManager.getTmpFilePath(fifoName);
return new File(fifoPath.toString());
}
#Override
public void removeFifoPipe(String fifoName) throws IOException {
Files.delete(propertiesManager.getTmpFilePath(fifoName));
}
I am writing a string that consists of 1000 lines. Writing 100 lines work but 1000 lines doesn't.
However, if I run "cat fifo" on an external shell, then the program proceeds and writes everything out without hanging. Its strange how the cat subprocess launched by this program doesn't work.
EDIT: I did a ps on the subprocess and it has the status "S".
External processes have input and output that you need to handle. Otherwise, they may hang, though the exact point at which they hang varies.
The easiest way to solve your issue is to change every occurrence of this:
process = Runtime.getRuntime().exec(command);
to this:
process = new ProcessBuilder(command).inheritIO().start();
Runtime.exec is obsolete. Use ProcessBuilder instead.
UPDATE:
inheritIO() is shorthand for redirecting all of the Process's input and output to those of the parent Java process. You can instead redirect only the input, and read the output yourself:
process = new ProcessBuilder(command).redirectInput(
ProcessBuilder.Redirect.INHERIT).start();
Then you will need to read the process's output from process.getInputStream().

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.

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

runtime.exec sending EOF immediately to input?

This is my code to start a process in Windows via java (and gobble the output).
public static void main(String[] args) throws Exception {
String[] command = new String[3];
command[0] = "cmd";
command[1] = "/C";
command[2] = "test.exe";
final Process child = Runtime.getRuntime().exec(command);
new StreamGobbler(child.getInputStream(), "out").start();
new StreamGobbler(child.getErrorStream(), "err").start();
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(
child.getOutputStream()));
out.write("exit\r\n");
out.flush();
child.waitFor();
}
private static class StreamGobbler extends Thread {
private final InputStream inputStream;
private final String name;
public StreamGobbler(InputStream inputStream, String name) {
this.inputStream = inputStream;
this.name = name;
}
public void run() {
try {
BufferedReader in = new BufferedReader(new InputStreamReader(
inputStream));
for (String s = in.readLine(); s != null; s = in.readLine()) {
System.out.println(name + ": " + s);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Somehow the program in question (process) is recieving an EOF right away (as in right after I step pas the "exec" line) and thus throwing an error ( detected, invalid) message immediately after runtime.exec is called. I can run this program manually via command prompt without this issue, but have confirmed that sending a ctrl-z on windows is what causes this message.
Anyone know what could be causing this?
If it matters, I have tried running the process directly as "test.exe" instead of cmd /c test.exe, but when I do that I can't see the output via the inputStream. And when I do cmd test.exe without the /c, there is no difference.
Your code looks like it should work (with one caveat, see below).
I took your code verbatim and replaced test.ext with sort, which can read from piped stdin.
If I run the code as-is, it starts the sort command, which waits for input. It hangs at child.waitFor() because you don't close the output stream to indicate EOF. When I add the close() call, everything works correctly.
I suggest you look at test.exe and determine if it is capable of reading from piped stdin, or is expecting console input.
Get rid of "cmd" and "/c". At present you are feeding output to cmd.exe, not to test.exe.

Categories