Question

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.

Was it helpful?

Solution

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
}

OTHER TIPS

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

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;
            }
        }
    };
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top