Java: How to launch a UI dialog from another thread, e.g. for Authenticator
Asked Answered
D

3

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.

Diarrhea answered 23/5, 2011 at 17:58 Comment(1)
SwingWorker with PropertyChangeListener can invoke JDialog or JOptionPane, correctly and on EDTKenji
R
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
}
Rubenrubens answered 23/5, 2011 at 18:6 Comment(2)
Oooh, very nice. I kind of wish Java had the message-passing facility of Android, but this comes pretty close.Diarrhea
Actually, it looks like invokeLater() will also do what I want.Diarrhea
K
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

Kenji answered 23/5, 2011 at 19:19 Comment(3)
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?Diarrhea
If you don't use SwingWorker, you'll wind up re-inventing it. I'm guessing mKorbel meant "adapt", rather than "debug" the example. :-)Undeviating
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.Diarrhea
D
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;
            }
        }
    };
Diarrhea answered 23/5, 2011 at 22:49 Comment(5)
@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 TaskKenji
@Edward Falk if I ignored/close blind eyes "if (username == null)" for code +1Kenji
@Edward Falk: Thanks for posting this; it's a reasonable alternative as long you later synchronize access to the data retrieved using the Authenticator.Undeviating
@mKorbel: the authenticator runs on the background thread. The only thing that runs on UI thread is queryUsernamePassword, which just launches a modal dialog.Diarrhea
@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 existKenji

© 2022 - 2024 — McMap. All rights reserved.