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