1

I'm importing a file to a project and once the user clicks select this file I do some things, including one through the command line.

I had done it using this method:

p = Runtime.getRuntime().exec(command, null, new File(directory));
p.waitFor();

As this process may take some time I decided to put a progress bar popup showing all the output of the current process in a new frame. I've followed this code from @trashgod adapted to my project. For the purpose of this question let's say I have it exactly as it is.

I just want this window to appear while the command is working and then close itself. I can do this by disposing the frame from the override done method.

My problem is that I'm creating this class from other class and while this is being executed it continues with the code. For example:

 method1();
 method2();
 new SwingWorkerExample().displayGUI(); //Process continues in background
 method3();

Here my method3() is being executed before the process of the SwingWorkerExample class is finished. How could I solve this?

I already tried putting at the end of the displayGUI method this:

while(!backgroundTask.isDone()){
//do nothing
}

But it makes the whole frame not work or maybe freeze it. I also tried to write a method after making p a global variable in backgrandtask class:

public Process getProcess(){
return p;
}

And then calling from displaGUI() the following:

backgroundTask.getProcess().waitFor();

But it returns a null pointer exception.

What else could I do to wait for the process to end while displaying the output of the command window?

Thank you. Update: Here is a copy of the code of the link with some changes to show what I mean. I don't want the second text to be printed until the process has finished:

package mcve;
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import javax.swing.*;

/**
* @se https://stackoverflow.com/a/20603012/230513
 * @see https://stackoverflow.com/a/17763395/230513
 * @seehttps://stackoverflow.com/questions/20600721/
 */
public class SwingWorkerExample {

private JFrame frame;
private final JLabel statusLabel = new JLabel("Status: ", JLabel.CENTER);
private final JTextArea textArea = new JTextArea(20, 20);
private JButton startButton = new JButton("Start");
private JButton stopButton = new JButton("Stop");
private JProgressBar bar = new JProgressBar();
private BackgroundTask backgroundTask;
private final ActionListener buttonActions = new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent ae) {
        JButton source = (JButton) ae.getSource();
        if (source == startButton) {
            textArea.setText(null);
            startButton.setEnabled(false);
            stopButton.setEnabled(true);
            backgroundTask = new BackgroundTask();
            backgroundTask.execute();
            bar.setIndeterminate(true);
        } else if (source == stopButton) {
            backgroundTask.cancel(true);
            backgroundTask.done();
        }
    }
};

private void displayGUI() {
    frame = new JFrame("Swing Worker Example");
    frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

    JPanel panel = new JPanel();
    panel.setBorder(
        BorderFactory.createEmptyBorder(5, 5, 5, 5));
    panel.setLayout(new BorderLayout(5, 5));

    JScrollPane sp = new JScrollPane();
    sp.setBorder(BorderFactory.createTitledBorder("Output: "));
    sp.setViewportView(textArea);

    startButton.addActionListener(buttonActions);
    stopButton.setEnabled(false);
    stopButton.addActionListener(buttonActions);
    JPanel buttonPanel = new JPanel();
    buttonPanel.add(startButton);
    buttonPanel.add(stopButton);
    buttonPanel.add(bar);

    panel.add(statusLabel, BorderLayout.PAGE_START);
    panel.add(sp, BorderLayout.CENTER);
    panel.add(buttonPanel, BorderLayout.PAGE_END);

    frame.setContentPane(panel);
    frame.pack();
    frame.setLocationByPlatform(true);
    frame.setVisible(true);
}

private class BackgroundTask extends SwingWorker<Integer, String> {

    private int status;

    public BackgroundTask() {
        statusLabel.setText((this.getState()).toString());
    }

    @Override
    protected Integer doInBackground() {
        try {
            ProcessBuilder pb = new ProcessBuilder("ping", "-c", "5","google.com");//change -c for -n in windows
            pb.redirectErrorStream(true);
            Process p = pb.start();
            String s;
            BufferedReader stdout = new BufferedReader(
                new InputStreamReader(p.getInputStream()));
            while ((s = stdout.readLine()) != null && !isCancelled()) {
                publish(s);
            }
            if (!isCancelled()) {
                status = p.waitFor();
            }
            p.getInputStream().close();
            p.getOutputStream().close();
            p.getErrorStream().close();
            p.destroy();
        } catch (IOException | InterruptedException ex) {
            ex.printStackTrace(System.err);
        }
        return status;
    }

    @Override
    protected void process(java.util.List<String> messages) {
        statusLabel.setText((this.getState()).toString());
        for (String message : messages) {
            textArea.append(message + "\n");
        }
    }

    @Override
    protected void done() {
        statusLabel.setText((this.getState()).toString() + " " + status);
        stopButton.setEnabled(false);
        startButton.setEnabled(true);
        bar.setIndeterminate(false);
        frame.dispose();
    }

}

public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
        @Override
        public void run() {
            System.out.println("Here I start");
            new SwingWorkerExample().displayGUI();
            System.out.println("I'll show up when the SwingWorkerExample has finnished");
        }
    });
}

Update2: I've also tried to put the process in the main method (displayGUI) and send a reference to the process but It doesn't work, the window just freezes and doesn't show up:

frame.setVisible(true);
    bar.setIndeterminate(true);
    try {
        ProcessBuilder pb = new ProcessBuilder("ping", "-c", "20", "google.com");
        pb.redirectErrorStream(true);
        Process p;
        p = pb.start();
        backgroundTask = new BackgroundTask(p);
        backgroundTask.execute();
        p.waitFor();
    } catch (IOException | InterruptedException ex) {
        ex.printStackTrace(System.err);
    }

Update 3: Following @Hovercraft Full of Eals answer I've end up with this, which still doesn't work as expected as the string appears before the end of the process. Maybe I've understood it wrong or it just doesn't work in this context:

package mcve;

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
import static javax.swing.Action.MNEMONIC_KEY;

public class SwingWorkerExample {

private JFrame frame;
private ExecuteFrame backgroundTask;


private class properyListener implements PropertyChangeListener {
    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        // the above percolates from the SwingWorker to the containing
        // JPanel
        if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
            int processState = backgroundTask.getProcessStatus();
            System.out.println("Process State: " + processState + "\n");
        }
    }
}
private void displayGUI() {
    frame = new JFrame("Swing Worker Example");
    frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

    backgroundTask = new ExecuteFrame();
    frame.add(backgroundTask);
    backgroundTask.addPropertyChangeListener(new properyListener());

    frame.pack();
    frame.setLocationByPlatform(true);
    frame.setVisible(true);

    backgroundTask.startProcess();
    }
 public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
        @Override
        public void run() {
            System.out.println("Here I start");
            SwingUtilities.invokeLater(() -> new SwingWorkerExample().displayGUI());
            System.out.println("I'll show up when the SwingWorkerExample has finnished");
        }
    });
}
}

@SuppressWarnings("serial")
class ExecuteFrame extends JPanel {

private final JLabel statusLabel = new JLabel("Status: ", JLabel.CENTER);
private final JTextArea textArea = new JTextArea(20, 30);
private StartAction startAction = new StartAction("Start", KeyEvent.VK_S);
private StopAction stopAction = new StopAction("Stop", KeyEvent.VK_T);
private JProgressBar bar = new JProgressBar();
private BackgroundTask backgroundTask;
private int processStatus;

public ExecuteFrame() {
    setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
    setLayout(new BorderLayout(5, 5));

    textArea.setFocusable(false);
    JScrollPane sp = new JScrollPane();
    sp.setBorder(BorderFactory.createTitledBorder("Output: "));
    sp.setViewportView(textArea);

    stopAction.setEnabled(false);
    JPanel buttonPanel = new JPanel();
    buttonPanel.add(new JButton(startAction));
    buttonPanel.add(new JButton(stopAction));
    buttonPanel.add(bar);

    add(statusLabel, BorderLayout.PAGE_START);
    add(sp, BorderLayout.CENTER);
    add(buttonPanel, BorderLayout.PAGE_END);
}

public void startProcess() {
    if (backgroundTask != null && !backgroundTask.isDone()) {
        return; // background task not yet done
    }
    textArea.setText("");
    startAction.setEnabled(false);
    stopAction.setEnabled(true);
    backgroundTask = new BackgroundTask();
    backgroundTask.addPropertyChangeListener(new BGTaskListener());
    backgroundTask.execute();
    bar.setIndeterminate(true);
}

public void cancelProcess() {
    if (backgroundTask != null && !backgroundTask.isDone()) {
        backgroundTask.cancel(true);
    }
}

public void processStopped() {
    statusLabel.setText((backgroundTask.getState()).toString() + " "
            + processStatus);
    stopAction.setEnabled(false);
    startAction.setEnabled(true);
    bar.setIndeterminate(false);

    // Window thisWindow = SwingUtilities.getWindowAncestor(textArea);
    // thisWindow.dispose();
}

public int getProcessStatus() {
    return processStatus;
}

public class BGTaskListener implements PropertyChangeListener {

    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
            processStopped();
        }

        // percolate this listener up to the main JPanel's Prop Chng
        // Listener
        ExecuteFrame.this.firePropertyChange(evt.getPropertyName(), evt.getOldValue(),
                evt.getNewValue());
    }
}

private class BackgroundTask extends SwingWorker<Integer, String> {

    @Override
    protected Integer doInBackground() throws Exception {
        List<String> list = new ArrayList<>();
        list.add("ping");
        String name = System.getProperty("os.name");
        if (name.startsWith("Win")) {
            list.add("-n");
        } else {
            list.add("-c");
        }
        list.add("5");
        list.add("google.com");
        try {
            ProcessBuilder pb = new ProcessBuilder(list);
            pb.redirectErrorStream(true);
            Process p = pb.start();
            String s;
            BufferedReader stdout = new BufferedReader(
                    new InputStreamReader(p.getInputStream()));
            while ((s = stdout.readLine()) != null && !isCancelled()) {
                publish(s);
            }
            if (!isCancelled()) {
                processStatus = p.waitFor();
            }
            p.getInputStream().close();
            p.getOutputStream().close();
            p.getErrorStream().close();
            p.destroy();
        } catch (IOException | InterruptedException ex) {
            ex.printStackTrace(System.err);
        }
        return processStatus;
    }

    @Override
    protected void process(java.util.List<String> messages) {
        statusLabel.setText((this.getState()).toString());
        for (String message : messages) {
            textArea.append(message + "\n");
        }
    }

}

private class StartAction extends AbstractAction {

    public StartAction(String name, int mnemonic) {
        super(name);
        putValue(MNEMONIC_KEY, mnemonic);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        startProcess();
    }
}

private class StopAction extends AbstractAction {

    public StopAction(String name, int mnemonic) {
        super(name);
        putValue(MNEMONIC_KEY, mnemonic);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        cancelProcess();
    }
}
 }

The output is:

Here I start
I'll show up when the SwingWorkerExample has finnished
Process State: 0

Instead of:

Here I start
Process State: 0
I'll show up when the SwingWorkerExample has finnished
Community
  • 1
  • 1
nck
  • 1,673
  • 16
  • 40

2 Answers2

2

The best solution is to add a PropertyChangeListener to your SwingWorker and in its call back method, check for the process to end. This will allow your Swing event thread to run, and will notify you, on the Swing event thread, when the worker is done. You can even listen for changes to the worker's progress property, and make changes to your JProgressBar from within this listener. For example (code not tested):

private class TaskListener implements PropertyChangeListener {
    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        SwingWorker task = (SwingWorker) evt.getSource();

        // here we can listen for changes to the worker's progress property
        // if we desire
        if ("progress".equals(evt.getPropertyName())) {
            int progress = task.getProgress();
            progressBar.setValue(progress);
        }

        // listen for the worker to be done
        if (SwingWorker.StateValue.DONE == evt.getNewValue()) {
            // always need to know when the SW is done
            // so we can call get() and trap exceptions
            try {
                task.get();  // do something with the result returned?

                // !! here do things that you want done when the SwingWorker has completed ***

            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        }
    }
}

elsewhere...

MySwingWorker myWorker = new MySwingWorker();
myWorker.addPropertyChangeListener(new TaskListener());
myWorker.execute();

Regarding:

What else could I do to wait for the process to end while displaying the output of the command window?

Getting and displaying the command window output is a completely different question, and would require your getting the InputStream from the background Process that you're running.


Edit I was playing with this, code that shows two windows, the 2nd doing the swing worker work, the first listening for property changes on the JPanel that is part of the 2nd, but it's not very clean code as it does not have good M-V-C separation of concerns:

import java.awt.BorderLayout;
import java.awt.Dialog.ModalityType;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;

public class SwingWorkerExample2 {

    private static void createAndShowGui() {
        SWEx2MainPanel mainPanel = new SWEx2MainPanel();

        JFrame frame = new JFrame("SwingWorker Example");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.getContentPane().add(mainPanel);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}

@SuppressWarnings("serial")
class SWEx2MainPanel extends JPanel {
    public static final ModalityType DLG_MODALITY_TYPE = ModalityType.MODELESS;
    private JTextArea statusArea = new JTextArea(15, 30);
    private JDialog workerDialog;
    private SWEx2WorkerPanel workerPanel = new SWEx2WorkerPanel();

    public SWEx2MainPanel() {
        JPanel buttonPanel = new JPanel();
        buttonPanel.add(new JButton(new ShowWorkerDialogAction()));
        statusArea.setFocusable(false);
        JScrollPane scrollPane = new JScrollPane(statusArea);
        scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);

        setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        setLayout(new BorderLayout(5, 5));
        add(scrollPane);
        add(buttonPanel, BorderLayout.PAGE_END);

        workerPanel.addPropertyChangeListener(new WorkerPanelListener());
    }

    private class WorkerPanelListener implements PropertyChangeListener {
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            // the above percolates from the SwingWorker to the containing
            // JPanel
            if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
                int processState = workerPanel.getProcessStatus();
                statusArea.append("Process State: " + processState + "\n");
            }
        }
    }

    private class ShowWorkerDialogAction extends AbstractAction {
        public ShowWorkerDialogAction() {
            super("Show Worker");
            putValue(MNEMONIC_KEY, KeyEvent.VK_S);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            if (workerDialog == null) {
                Window window = SwingUtilities.getWindowAncestor(statusArea);
                workerDialog = new JDialog(window, "Worker Dialog", DLG_MODALITY_TYPE);
                workerDialog.add(workerPanel);
                workerDialog.pack();
                workerDialog.setLocationByPlatform(true);
            } else if (workerDialog.isVisible()) {
                // dialog is still visible -- do nothing
                return;
            }
            workerDialog.setVisible(true);
        }
    }
}

// class that holds the JPanel that does background communication
@SuppressWarnings("serial")
class SWEx2WorkerPanel extends JPanel {
    private final JLabel statusLabel = new JLabel("Status: ", JLabel.CENTER);
    private final JTextArea textArea = new JTextArea(20, 30);
    private StartAction startAction = new StartAction("Start", KeyEvent.VK_S);
    private StopAction stopAction = new StopAction("Stop", KeyEvent.VK_T);
    private JProgressBar bar = new JProgressBar();
    private BackgroundTask backgroundTask;
    private int processStatus;

    public SWEx2WorkerPanel() {
        setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        setLayout(new BorderLayout(5, 5));

        textArea.setFocusable(false);
        JScrollPane sp = new JScrollPane();
        sp.setBorder(BorderFactory.createTitledBorder("Output: "));
        sp.setViewportView(textArea);

        stopAction.setEnabled(false);
        JPanel buttonPanel = new JPanel();
        buttonPanel.add(new JButton(startAction));
        buttonPanel.add(new JButton(stopAction));
        buttonPanel.add(bar);

        add(statusLabel, BorderLayout.PAGE_START);
        add(sp, BorderLayout.CENTER);
        add(buttonPanel, BorderLayout.PAGE_END);
    }

    public void startProcess() {
        if (backgroundTask != null && !backgroundTask.isDone()) {
            return; // background task not yet done
        }
        textArea.setText("");
        startAction.setEnabled(false);
        stopAction.setEnabled(true);
        backgroundTask = new BackgroundTask();
        backgroundTask.addPropertyChangeListener(new BGTaskListener());
        backgroundTask.execute();
        bar.setIndeterminate(true);
    }

    public void cancelProcess() {
        if (backgroundTask != null && !backgroundTask.isDone()) {
            backgroundTask.cancel(true);
        }
    }

    public void processStopped() {
        statusLabel.setText((backgroundTask.getState()).toString() + " "
                + processStatus);
        stopAction.setEnabled(false);
        startAction.setEnabled(true);
        bar.setIndeterminate(false);

        // Window thisWindow = SwingUtilities.getWindowAncestor(textArea);
        // thisWindow.dispose();
    }

    public int getProcessStatus() {
        return processStatus;
    }

    private class BGTaskListener implements PropertyChangeListener {
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
                processStopped();
            }

            // percolate this listener up to the main JPanel's Prop Chng
            // Listener
            SWEx2WorkerPanel.this.firePropertyChange(evt.getPropertyName(), evt.getOldValue(),
                    evt.getNewValue());
        }
    }

    private class BackgroundTask extends SwingWorker<Integer, String> {

        @Override
        protected Integer doInBackground() throws Exception {
            List<String> list = new ArrayList<>();
            list.add("ping");
            String name = System.getProperty("os.name");
            if (name.startsWith("Win")) {
                list.add("-n");
            } else {
                list.add("-c");
            }
            list.add("5");
            list.add("google.com");
            try {
                ProcessBuilder pb = new ProcessBuilder(list);
                pb.redirectErrorStream(true);
                Process p = pb.start();
                String s;
                BufferedReader stdout = new BufferedReader(
                        new InputStreamReader(p.getInputStream()));
                while ((s = stdout.readLine()) != null && !isCancelled()) {
                    publish(s);
                }
                if (!isCancelled()) {
                    processStatus = p.waitFor();
                }
                p.getInputStream().close();
                p.getOutputStream().close();
                p.getErrorStream().close();
                p.destroy();
            } catch (IOException | InterruptedException ex) {
                ex.printStackTrace(System.err);
            }
            return processStatus;
        }

        @Override
        protected void process(java.util.List<String> messages) {
            statusLabel.setText((this.getState()).toString());
            for (String message : messages) {
                textArea.append(message + "\n");
            }
        }

    }

    private class StartAction extends AbstractAction {
        public StartAction(String name, int mnemonic) {
            super(name);
            putValue(MNEMONIC_KEY, mnemonic);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            startProcess();
        }
    }

    private class StopAction extends AbstractAction {
        public StopAction(String name, int mnemonic) {
            super(name);
            putValue(MNEMONIC_KEY, mnemonic);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            cancelProcess();
        }
    }

}

Edit 2

In your latest iteration of your code:

public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
        @Override
        public void run() {
            System.out.println("Here I start");

            // note that the use of invokeLater twice is not necessary
            SwingUtilities.invokeLater(() -> new SwingWorkerExample().displayGUI());

            // there is nothing in the code that pauses or delays this line of code
            System.out.println("I'll show up when the SwingWorkerExample has finnished");
        }
    });
}

Side issue: there's no need to queue code on the Swing thread from within code that is already queued to be called on the Swing thread.

Issue that is on point: Your println statement is made immediately after starting the GUI -- what's to stop it from being called? You need one of two mechanisms to effect this delay.

  1. Either use a call back of some sort, any sort, or
  2. Display the image processing GUI as a modal dialog, and this way it will delay any code from the calling program that is in the same thread and called after the dialog has been made visible. The delayed code will pause until the dialog is no longer present.

The first way is the more flexible of the two as it does not require use of a modal dialog.

An example below of both of the above described mechanisms. Note changes to your code have been denoted with \\ !! comments:

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.Dialog.ModalityType;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

import javax.swing.*;

import static javax.swing.Action.MNEMONIC_KEY;

public class SwingWorkerExample2 {
    private JDialog modalDialog; // !! a modal JDialog
    private ExecuteFrame backgroundTask;
    private Runnable myCallback; // !!

    private class properyListener implements PropertyChangeListener {
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
                int processState = backgroundTask.getProcessStatus();
                System.out.println("Process State: " + processState + "\n");

                // !! added
                myCallback.run(); // simply run it. No need to place into a new thread
            }
        }
    }

    // !! set myCallback field. This could also be done as a parameter to 
    // !! the displayGUI(Runnable myCallback) method  if desired
    public SwingWorkerExample2(Runnable myCallback) {
        this.myCallback = myCallback;
    }

    private void displayGUI() {
        // !! create it as a modal dialog
        modalDialog = new JDialog(null, "Swing Worker Example", ModalityType.APPLICATION_MODAL);
        modalDialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); // !!
        backgroundTask = new ExecuteFrame();
        modalDialog.add(backgroundTask);
        backgroundTask.addPropertyChangeListener(new properyListener());
        modalDialog.pack();
        modalDialog.setLocationByPlatform(true);
        modalDialog.setVisible(true);
        backgroundTask.startProcess();
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                System.out.println("Here I start");
                Runnable myCallback = new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("This is being displayed from a Runnable");
                    }
                };

                // pass callback Runnable into SwingWorkerExample2's constructor
                new SwingWorkerExample2(myCallback).displayGUI(); // !! not wrapped in a Runnable
                System.out.println("This is displayed after the dialog is no longer visible");
            }
        });
    }
}

@SuppressWarnings("serial")
class ExecuteFrame2 extends JPanel {
    private final JLabel statusLabel = new JLabel("Status: ", JLabel.CENTER);
    private final JTextArea textArea = new JTextArea(20, 30);
    private StartAction startAction = new StartAction("Start", KeyEvent.VK_S);
    private StopAction stopAction = new StopAction("Stop", KeyEvent.VK_T);
    private JProgressBar bar = new JProgressBar();
    private BackgroundTask backgroundTask;
    private int processStatus;

    public ExecuteFrame2() {
        setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        setLayout(new BorderLayout(5, 5));
        textArea.setFocusable(false);
        JScrollPane sp = new JScrollPane();
        sp.setBorder(BorderFactory.createTitledBorder("Output: "));
        sp.setViewportView(textArea);
        stopAction.setEnabled(false);
        JPanel buttonPanel = new JPanel();
        buttonPanel.add(new JButton(startAction));
        buttonPanel.add(new JButton(stopAction));
        buttonPanel.add(bar);
        add(statusLabel, BorderLayout.PAGE_START);
        add(sp, BorderLayout.CENTER);
        add(buttonPanel, BorderLayout.PAGE_END);
    }

    public void startProcess() {
        if (backgroundTask != null && !backgroundTask.isDone()) {
            return; // background task not yet done
        }
        textArea.setText("");
        startAction.setEnabled(false);
        stopAction.setEnabled(true);
        backgroundTask = new BackgroundTask();
        backgroundTask.addPropertyChangeListener(new BGTaskListener());
        backgroundTask.execute();
        bar.setIndeterminate(true);
    }

    public void cancelProcess() {
        if (backgroundTask != null && !backgroundTask.isDone()) {
            backgroundTask.cancel(true);
        }
    }

    public void processStopped() {
        statusLabel.setText((backgroundTask.getState()).toString() + " " + processStatus);
        stopAction.setEnabled(false);
        startAction.setEnabled(true);
        bar.setIndeterminate(false);
        // Window thisWindow = SwingUtilities.getWindowAncestor(textArea);
        // thisWindow.dispose();
    }

    public int getProcessStatus() {
        return processStatus;
    }

    public class BGTaskListener implements PropertyChangeListener {
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
                processStopped();
            }
            // percolate this listener up to the main JPanel's Prop Chng
            // Listener
            ExecuteFrame2.this.firePropertyChange(evt.getPropertyName(), evt.getOldValue(),
                    evt.getNewValue());
        }
    }

    private class BackgroundTask extends SwingWorker<Integer, String> {
        @Override
        protected Integer doInBackground() throws Exception {
            List<String> list = new ArrayList<>();
            list.add("ping");
            String name = System.getProperty("os.name");
            if (name.startsWith("Win")) {
                list.add("-n");
            } else {
                list.add("-c");
            }
            list.add("5");
            list.add("google.com");
            try {
                ProcessBuilder pb = new ProcessBuilder(list);
                pb.redirectErrorStream(true);
                Process p = pb.start();
                String s;
                BufferedReader stdout = new BufferedReader(
                        new InputStreamReader(p.getInputStream()));
                while ((s = stdout.readLine()) != null && !isCancelled()) {
                    publish(s);
                }
                if (!isCancelled()) {
                    processStatus = p.waitFor();
                }
                p.getInputStream().close();
                p.getOutputStream().close();
                p.getErrorStream().close();
                p.destroy();
            } catch (IOException | InterruptedException ex) {
                ex.printStackTrace(System.err);
            }
            return processStatus;
        }

        @Override
        protected void process(java.util.List<String> messages) {
            statusLabel.setText((this.getState()).toString());
            for (String message : messages) {
                textArea.append(message + "\n");
            }
        }
    }

    private class StartAction extends AbstractAction {
        public StartAction(String name, int mnemonic) {
            super(name);
            putValue(MNEMONIC_KEY, mnemonic);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            startProcess();
        }
    }

    private class StopAction extends AbstractAction {
        public StopAction(String name, int mnemonic) {
            super(name);
            putValue(MNEMONIC_KEY, mnemonic);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            cancelProcess();
        }
    }
}
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • Hello @Hovercraft, thanks for your answer. Displaying the output is already done by the code provided in the link. I don't understand how you tell the code "elsewhere" to wait. I mean, you call myworker.execute() but then in continues with the methods which follow that one. How do you do to wait until code under (SwingWorker.StateValue.DONE == evt.getNewValue()) is executed? – nck Nov 12 '16 at 18:52
  • 1
    @nck: you don't tell code "to wait", but rather this is event driven programming -- you respond to **events**, here the event being the SwingWorker being completed. If you're still unsure of what to do, please consider clarifying your question and your code by posting a valid and decent [mcve], a small compilable and runnable demonstration program that shows your problem for us. Perhaps in this MCVE, use Thread.sleep to emulate your background process. – Hovercraft Full Of Eels Nov 12 '16 at 18:56
  • I just added a copy of the code showing what I mean, I don't want the second string to be printed until the ping has finished. – nck Nov 12 '16 at 19:12
  • 1
    @nck: then add a PropertyChangeWorker to the SwingWorker as I described in my answer above. Question: in your working program is this window being displayed in response to an event in your main application window? In other words, is it a secondary "dialog" window and not the main application window? – Hovercraft Full Of Eels Nov 12 '16 at 20:46
  • I've tried to add your code to the example I just added, but it doesn't work or I'm doing it wrong because if i add the properyChangeListener to the backgroundtask that won't happen until I click the button start, and therefore the application will continue before finishing this process. And even if I put the code inside the start button on the displayGUI method It still shows the string before finishing. Maybe I'm doing it wrong. I copied that class and then added this code after creating the background task: "backgroundTask.addPropertyChangeListener(new TaskListener());". – nck Nov 12 '16 at 21:00
  • In my working program I import a picture to a project. Before showing this picture I do some things to it through the console which I want to show in a new frame because they may take some time, and I need that the showPicture(...) method is not called until the process is finished, because otherwise the file which is called from showPicture won't exist. – nck Nov 12 '16 at 21:04
  • I've seen your edit code like two Hours ago but I really don't get how It works. Could you simplify it in order to just have the frame of the background task triggered by the main process? Thank you. – nck Nov 13 '16 at 12:30
  • I'm sorry, I always vote once I've solved my question, but usually if I don't know whether the answer solves my problem or not I wait... I've tried to adapt your code to the code I put in the question but if I don't understood it wrong it isn't solving the problem. Thank you. – nck Nov 13 '16 at 13:35
  • 1
-1

You can use a Thread instead. Try this:

Thread t = new Thread(new Runnable(){  
    @Override
    public void run()
    {
    //The stuff you want to finish first
    }
}).start();
t.join();

which will create a new Thread and wait for it to finish.

Fırat Kıyak
  • 480
  • 1
  • 6
  • 18
  • Sorry, but this does not solve his problem. By joining the thread, you completely obliterate the thread's running in a background process, and so this code will freeze the Swing GUI. Much better to use a call-back mechanism, a listener, and fortunately this mechanism has been built into the SwingWorker. – Hovercraft Full Of Eels Nov 12 '16 at 18:30