5

I needed a way to get some data from a database and prevent the user from modifying existing data for that moment.

I created a SwingWorker to make the db update and a modal JDialog to show user what is going on (with a JProgressBar). The modal dialog has defaultCloseOperation set to DO_NOTHING, so it can only be closed with a proper call - I use setVisible(false).

MySwingWorkerTask myTask = new MySwingWorkerTask();
myTask.execute();
myModalDialog.setVisible(true);

The SwingWorker does some stuff within doInBackground() and lastly it calls:

myModalDialog.setVisible(false);

My only concern and my question: Is is possible that the SwingWorker executes the setVisible(false) before it is setVisible(true) in the line after worker spawn?

If so the setVisible(true) could block forever (the user can't close the modal window).

Do I have to implement something as:

while (!myModalDialog.isVisible()) {
    Thread.sleep(150);
}
myModalDialog.setVisible(false);

to make sure it will actually get closed?

Eng.Fouad
  • 115,165
  • 71
  • 313
  • 417
user1713059
  • 1,425
  • 2
  • 17
  • 34

1 Answers1

4

Generally, yes.

What I would do is in your doInBackground method is use SwingUtilities.invokeLater to show the dialog and in your done method hide the dialog.

This should mean that even if the dialog doesn't make it to the screen you gain a little more control over the flow...

The minor issue is you're now going to have to pass the dialog to the worker so it can gain control over it...

public class TestSwingWorkerDialog {

    public static void main(String[] args) {
        new TestSwingWorkerDialog();
    }
    private JDialog dialog;

    public TestSwingWorkerDialog() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException ex) {
                } catch (InstantiationException ex) {
                } catch (IllegalAccessException ex) {
                } catch (UnsupportedLookAndFeelException ex) {
                }

                MyWorker worker = new MyWorker();
                worker.execute();

            }
        });
    }

    public class MyWorker extends SwingWorker<Object, Object> {

        @Override
        protected Object doInBackground() throws Exception {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    getDialog().setVisible(true);
                }
            });
            Thread.sleep(2000);

            return null;
        }

        @Override
        protected void done() {
            System.out.println("now in done...");
            JDialog dialog = getDialog();
            // Don't care, dismiss the dialog
            dialog.setVisible(false);
        }

    }

    protected JDialog getDialog() {
        if (dialog == null) {

            dialog = new JDialog();
            dialog.setModal(true);
            dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
            dialog.setLayout(new BorderLayout());
            dialog.add(new JLabel("Please wait..."));
            dialog.setSize(200, 200);
            dialog.setLocationRelativeTo(null);

        }

        return dialog;
    }

}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • 1
    Whats the difference between `SwingUtilities.invokeLater` and `EventQueue.invokeLater` ? – Mordechai Oct 30 '12 at 00:55
  • 1
    Sorry if my question is silly, but in your example is it guaranteed, that invokeLater's run method is surely run before work is done()? Seems to me like the main problem of my post remains - theortically uncertain concurrency but I can be wrong. – user1713059 Oct 30 '12 at 00:56
  • 2
    @M.M. There are none. SwingUtilities.invokeLater forwards the call to EventQueue.invokeLater. – Guillaume Polet Oct 30 '12 at 08:15
  • @user1713059 what this does is guarantees that 1. The dialog will be opened only after the worker has started and 2. That it won't be closed (or attempted to be closed) before the work finishes. Because the done method is executed within the context of the EDT, using a similar method as I did to open it, the open event will be executed BEFORE the close, as it was added to the queue first. – MadProgrammer Oct 30 '12 at 08:23
  • @MadProgrammer I don't think invokeLater gives any guarantees as to the order in which things will be executed. – assylias Oct 30 '12 at 08:32
  • You could use invokeAndWait instead - that should be 100% robust. – assylias Oct 30 '12 at 08:33
  • @assyoias yes and no. Because we adding events into a queue there is some guarantee of the order they will be called, allowing a distinct difference in time, you are correct if you have two threads attempting to post at almost the same time, then it comes down the synchronisation mechanism. Don't use invokeAndWait to call into a blocking piece of code (unless that's your intention), the OP is trying to show a modal dialog. In this context, invokeAndWait will not return until AFTER the dialog is closed – MadProgrammer Oct 30 '12 at 08:37
  • Agreed with your point on invokeAndWait - but nothing in the docs says the queue is fifo. – assylias Oct 30 '12 at 09:04
  • @assylias Hava read of [EventQueue](http://docs.oracle.com/javase/7/docs/api/java/awt/EventQueue.html) *"In the same order as they are enqueued.*" – MadProgrammer Oct 30 '12 at 09:06
  • @MadProgrammer Good point - but it also says *"note that events being posted to the EventQueue can be coalesced"*... I'm confused... – assylias Oct 30 '12 at 10:32
  • 1
    @MadProgrammer I got a precision on that point here: http://stackoverflow.com/questions/6008279/swingutilities-invokelater/6008307#comment17867100_6008307 – assylias Oct 30 '12 at 12:28