0

My problem in a nutshell: my GUI app needs to execute a lengthy network download. The download is handled in a separate thread. It's possible that the remote site will require authentication, so I want to define an Authenticator that pops up an "enter your username and password" dialog. I realize that this dialog needs to be run from the UI thread.

I'm sure I'm not the first person to do this. What is the best practice here for having a background thread launch a dialog in the UI thread, and block until that dialog is dismissed?

p.s. the background thread is very large and does a lot more than just download a file from the net. In other words, it's probably not practical at this point to convert it to a SwingWorker, and anyway, I'm not sure how I would solve this from a SwingWorker either.

Edward Falk
  • 9,991
  • 11
  • 77
  • 112
  • SwingWorker with PropertyChangeListener can invoke JDialog or JOptionPane, correctly and on EDT – mKorbel May 23 '11 at 18:06

3 Answers3

2

you need SwingUtlities.invokeLater to present the dialog, and a synchronize/notify object to 'pause' and wait for the user to respond.

Basically in your worker(non-gui) thread:

final Object obj = new Object() ; // or something to receive your dialog's answer(s)
Runnable r = new Runnable() {

   void run() {
     Dialog d = new Dialog() ;

     Button b = new JButton("ok") ;
     b.addActionListener(new ActionListener() {
         void actionPerformed(ActionEvent e) {
             synchronize(obj) { // can lock when worker thread releases with wait
                obj.notify() ; // signals wait
             }
         }
     }) ;

   }
} ;

synchronize( obj ) {
   SwingUtilites.invokeLater(r) ; // executs r's run method on the swing thread
   obj.wait() ; // releases obj until worker thread notifies
}
Andrew
  • 2,530
  • 16
  • 9
2

Edward Falk wrote Actually, it looks like invokeLater() will also do what I want

no, that's wrong because you have to calculate that EDT exists, and SwingUtilites.invokeLater() works if is there running EDT, if not then SwingUtilites.invokeLater() nothing notified, any popup will be displaed, maybe just empty Rectangle

1/ create EDT by using java.swing.Action

2/ debug this idea by trashgod I think that that this logic is correct and best for that

Community
  • 1
  • 1
mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • I'm ok with that. My background thread can only be launched from the UI, so it's pretty much a given that the EDT exists. Or am I missing something? – Edward Falk May 23 '11 at 19:33
  • If you don't use `SwingWorker`, you'll wind up re-inventing it. I'm guessing mKorbel meant "adapt", rather than "debug" the [example](http://stackoverflow.com/questions/6100989/java-how-to-launch-a-ui-dialog-from-another-thread-e-g-for-authenticator/6101846#6101846). :-) – trashgod May 23 '11 at 22:12
  • If I was starting from scratch, I probably would go with SwingWorker, but I'm working with someone else's code, and it's a very large thread. Anyway, invokeAndWait() worked perfectly in my case. – Edward Falk May 23 '11 at 22:45
2

For the record, here was my final solution, based on Andrew's answer:

final Authenticator authenticator =
   new Authenticator() {
        @Override
        public PasswordAuthentication getPasswordAuthentication() {
            try {
                SwingUtilities.invokeAndWait(new Runnable() {
                    public void run() {
                        // Launch the GUI dialog
                        queryUsernamePassword(getRequestingHost(), getRequestingPrompt());
                    }
                });
                if (username == null)
                    return null;
                return new PasswordAuthentication(username,
                            password.toCharArray());
            } catch (Exception e) {
                return null;
            }
        }
    };
Edward Falk
  • 9,991
  • 11
  • 77
  • 112
  • @Edward Falk please don't take my wors as FlameWar (my poor English), 1) if possible then try to avoid to run Heavy Action on EDT (opening any type of connection) 2) you probably don't calculated with fact that host should be unavailable, then timeout depends by type of connection 30sec - 4mins, and your GUI will wait for that in unresponsible form, and who'll wait for that 3) set timeout or add posibility to cancel this long Task – mKorbel May 23 '11 at 23:49
  • @Edward Falk if I ignored/close blind eyes "if (username == null)" for code +1 – mKorbel May 23 '11 at 23:54
  • @Edward Falk: Thanks for posting this; it's a reasonable alternative as long you later synchronize access to the data retrieved using the `Authenticator`. – trashgod May 24 '11 at 01:57
  • @mKorbel: the authenticator runs on the background thread. The only thing that runs on UI thread is queryUsernamePassword, which just launches a modal dialog. – Edward Falk May 26 '11 at 15:49
  • @Edward Falk well, I think that (there) isn't any problem(s) if you run in one time just only BackGround thread, that's sometimes backBox, because deepest knowledge about concurency ensure us if this concrete method is treadSafe of not, but in all of cases AbstractAction works and created EDT if doesn't exist – mKorbel May 26 '11 at 19:23