Search code examples
javahttp-redirectjava-7processbuildersystem.out

ProcessBuilder.inheritIO() sending output to the wrong place


I am using inheritIO() to redirect output from a child process in my program to the System.out and System.err, and input to System.in.

These are all redirected by System.setOut() and the like:

// Reassign System IO
System.setIn(cpanel.getConsole().getInputStream());
System.setOut(new PrintStream(cpanel.getConsole().getOutputStream()));
System.setErr(new PrintStream(cpanel.getConsole().getOutputStream()));

However when I run the process:

String[] fullargs = new String[sargs.length+4];
fullargs[0] = "java";
fullargs[1] = "-classpath";   // Runtime classpath option.
fullargs[2] = cpath;          // Specify the classpath.
fullargs[3] = mname;          // Specify class to run.

for(int i=0; i<sargs.length; i++)
{
    fullargs[i+4] = sargs[i]; // Put together arguments.
}

ProcessBuilder proc = new ProcessBuilder()
                            .inheritIO()
                            .command(fullargs);


try
{
    System.out.println("RUNNING...");
    proc.start();
}
catch(IOException ioe)
{
    JOptionPane.showMessageDialog(null,
        "There was a system error invoking this program.",
        "ERROR",
        JOptionPane.ERROR_MESSAGE);
}

It redirects to what used to be System.out etc. rather than what they've been redirected to.

If I comment out the inheritIO() line, the output is lost to time and doesn't appear anywhere. With inheritIO() it goes to the standard console of the parent process rather than the redirected one. The line where I print "RUNNING" goes to the proper redirected location. In other words, inheritIO() is doing exactly what it should if I hadn't redirected the output streams of the parent process. It's going to the parent process's old console.

I have no idea why this is happening and I'm pulling my hair out here. I've seen that inheritIO() doesn't work in Windows, but this issue is the same on Mac OS and Linux. I'm using Java 7.


Solution

  • Please note my answer here: https://stackoverflow.com/a/32350856/5226711

    Applied to your question, this means you can use an adapted verion of the StreamGobbler proposed in https://stackoverflow.com/a/14165567/5226711:

    private class StreamGobbler extends Thread {
        private InputStream in;
        private PrintStream out;
    
        private StreamGobbler(InputStream in, PrintStream out) {
            this.in = in;
            this.out = out;
        }
    
        @Override
        public void run() {
            try {
                BufferedReader input = new BufferedReader(new InputStreamReader(in));
                String line = null;
                while ((line = input.readLine()) != null)
                    out.println(line);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    And use it like this:

    String[] fullargs = new String[sargs.length+4];
    fullargs[0] = "java";
    fullargs[1] = "-classpath";   // Runtime classpath option.
    fullargs[2] = cpath;          // Specify the classpath.
    fullargs[3] = mname;          // Specify class to run.
    
    for(int i=0; i<sargs.length; i++)
    {
        fullargs[i+4] = sargs[i]; // Put together arguments.
    }
    
    ProcessBuilder pb = new ProcessBuilder().command(fullargs);
    
    try
    {
        System.out.println("RUNNING...");
        Process p = pb.start();
        StreamGobbler pOut = new StreamGobbler(p.getInputStream(), new PrintStream(cpanel.getConsole().getOutputStream()));
        StreamGobbler pErr = new StreamGobbler(p.getErrorStream(), new PrintStream(cpanel.getConsole().getOutputStream()));
        pOut.start();
        pErr.start();
    }
    catch(IOException ioe)
    {
        JOptionPane.showMessageDialog(null,
            "There was a system error invoking this program.",
            "ERROR",
            JOptionPane.ERROR_MESSAGE);
    }
    

    Redirecting stdin of the child is not included in my example.