0

I am using p = Runtime.getRuntime().exec("myScript.sh"); as part of a actionPerformed(ActionEvent evt) of button A's addActionListener(new ActionListener().

I would like to have a way to terminate the process. I tried to create another button (button B) which calls p.destroy(). However, it looks like after clicking on button A, it launches the process and only until the process finishes, can I click on other buttons (including the closing button on the top right) on the GUI. So I can't terminate the process in the middle.

Is there any way I can terminate the process? Or is there any way I can click other buttons while the process initiated by button A is still running?

I commented out the p.waitFor(); I still cannot click other buttons after clicking btnRun

    private Process p;
    btnRun.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent evt) {
            String s;
            try {
                p = Runtime.getRuntime().exec(command);
                BufferedReader br = new BufferedReader(
                    new InputStreamReader(p.getInputStream()));
                while ((s = br.readLine()) != null)
                    System.out.println(s);
                //p.waitFor();
                //System.out.println ("exit: " + p.exitValue());
                //p.destroy();
            } catch (Exception e) {}
        }
     });

    btnTerminate.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent evt) {
            p.destroy();
        }
    });
  • It seems that your GUI thread is blocked by the process launched by exec. Are you sure you don't call `waitFor()` on the process? Anyway a [MCVE](https://stackoverflow.com/help/mcve) would help a lot. – MatheM Apr 19 '18 at 20:07
  • From the sounds if it, you are blocking the Event Dispatching Thread, maybe something like [this example](https://stackoverflow.com/questions/15801069/printing-a-java-inputstream-from-a-process/15801490#15801490) or [this example](https://stackoverflow.com/questions/34858405/how-can-i-make-this-method-update-the-gui-within-my-loop/34864911#34864911) can help – MadProgrammer Apr 19 '18 at 20:38
  • @MadProgrammer Yes, you are right. I am blocking the EDT. I looked at your example. It's kinda hard for me to understand fully, but it's definitely a great tool. (SwingWalker)! I implemented MatheM's solution. But Thank you a lot! – Wenhao Geng Apr 20 '18 at 14:44
  • @WenhaoGeng You should also favour `ProcessBuilder` over `Runtime#exec` as it's more configurable and allows you to merge the error stream with the input stream - which is something MatheM's answer is ignoring – MadProgrammer Apr 20 '18 at 22:11

1 Answers1

1

The while loop processing lines from BufferedReader also blocks the EDT. BufferedReader will try to read the stream until it is closed and it will be closed when the Process finishes. Until then the EDT is blocked by the readLine(). You have to process the lines in another thread.

See this example

class InStreamPrinter extends Thread {
    InputStream is;

    InStreamPrinter(InputStream is) {
        this.is = is;
    }

    @Override
    public void run() {
        try (InputStreamReader isr = new InputStreamReader(is); 
            BufferedReader br = new BufferedReader(isr);) {
            String line = null;
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Then just pass the input stream from the Process to this Thread and start it.

...
p = Runtime.getRuntime().exec(command);
new InStreamPrinter(p.getInputStream()).start();
...
MatheM
  • 801
  • 7
  • 17