1

This is in the context of unit testing (as it happens).

At the end of a test, whatever the result, I want the code to check for the presence (visibility) of a JFileChooser dialog... and if visible, dismiss it.

There are different ways of dismissing a dialog, of course, but to mimic human action (and to give an example of the sort of question I'm concerned with here) I choose to use java.awt.Robot. The latter's methods should be run in a non-EDT thread.

In fact I have extended Robot to include a convenience method called type_input, so

robot.type_input( KeyEvent.VK_F4, KeyEvent.VK_ALT )

presses first Alt, then F4, then releases F4 and then Alt: like a human dismissing a window/dialog.

I submit the Runnable using invokeAndWait because I don't want the code to run on to the next test until this dialog has been dismissed. I have to test for visibility and focus in the EDT. But the Robot method must, as I said, be run in a non-EDT.

Are there any potential problems with going get() like this in the EDT? Would it potentially lead to unresponsiveness of the GUI? The thing is, I have heard that the framework is capable of "starting a new EDT pump" under certain conditions. I have to confess this is one of the aspects of EDT-related matters that I feel I know least about...

import java.awt.EventQueue;
import java.awt.Robot;
import java.awt.event.KeyEvent;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;

class MainFrame extends JFrame {
    JFileChooser update_target_file_chooser;
    JDialog file_chooser_dlg;

    // ... rest of class

}

public class ThreadWithinThread {

    public static void main(String[] args) throws InvocationTargetException, InterruptedException {

        final ExecutorService thread_pool_exec_serv = Executors.newFixedThreadPool( 5 );

        class DismissDlg implements Runnable {
            MainFrame main_frame;
            Robot robot;

            @Override
            public void run() {
                boolean focus_on_dlg = main_frame.file_chooser_dlg.hasFocus();
                if( main_frame.file_chooser_dlg.isVisible() ){
                    if( ! focus_on_dlg ){
                        main_frame.file_chooser_dlg.requestFocus();
                    }
                    class AltF4 implements Callable<Void>{
                        public Void call(){
                            return robot.type_input( KeyEvent.VK_F4, KeyEvent.VK_ALT );
                        }
                    }
                    Future<Void> future_result = thread_pool_exec_serv.submit( new AltF4() );
                    try {
                        // this is the line I'm worried about
                        future_result.get();
                    } catch (InterruptedException | ExecutionException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        EventQueue.invokeAndWait( new DismissDlg() );
    }

}

later

As I have said in my responses, this is not about a particular practical solution to the case in hand: I'm really trying to understand whether and when another EDT "event pump" starts up with Future.get() blocking the EDT. As I say I find this whole aspect difficult to understand. My use of JOptionPane's static methods (which must be run in the EDT, see posts on SO ad nauseam) demonstrates to me that these methods (which do block the EDT!) don't actually seem to prevent the GUI from functioning (NB not to be confused with whether these JOptionPanes, or rather their JDialogs, are modal or modeless).

My understanding is that this is due to the fact that "the framework" then starts another "event pump". So could it do so here?

mike rodent
  • 14,126
  • 11
  • 103
  • 157
  • How hard would it be to get a reference to the JFileChooser and call isVisible, and then call setVisible(false) ? – BillRobertson42 Jan 21 '16 at 22:29
  • @Bill Thanks - in fact I'm mainly concerned with whether using Future.get() results in another "event pump" starting on occurrence of another EDT event. It's not that I'm having practical difficulties finding "some solution". – mike rodent Jan 22 '16 at 08:16
  • Was just a thought. Might be that you can't access the dialog, but might be that you can. Sometimes we get so caught up in more complicated answers that we forget about the simple stuff. – BillRobertson42 Jan 22 '16 at 12:56

2 Answers2

3

So, from the JavaDocs

Waits if necessary for the computation to complete, and then retrieves its result.

Which suggests that the method is a blocking method, if there's one thing you shouldn't do from within the context of the Event Dispatching Thread, it's call a blocking method within the context of the Event Dispatching Thread

Ah, but what to do? You could use a SwingWorker instead, which internally uses it's own ExecutorService, or submit the worker to your own ExecutorService (SwingWorker implements Runnable), you should then be able to use a PropertyChangeListener to monitor the state of the SwingWorker and when it's DONE, you can retrieve the value from it's get method without blocking

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Thanks... yes, I know all about SwingWorker and use it all the time. The main point of my question is to understand whether use of Future.get() here results in another "event pump" starting if another EDT event is generated when get() is blocking the EDT... – mike rodent Jan 22 '16 at 08:10
  • 1
    Not sure I understand what you mean, `Future#get` will block until the `Callable` returns a result or `Exception`, meaning that if called from within the context of the EDT, you will prevent it from processing any new events until it's unblocked and the method returns – MadProgrammer Jan 22 '16 at 12:40
  • *"As I say I find this whole aspect difficult to understand. My use of JOptionPane's static methods ... demonstrates to me that these methods (which do block the EDT!) don't actually seem to prevent the GUI from functioning"* - Modal dialogs work by taking over the event dispatching process, allowing events to be processed and dispatched, but allowing them to "block" the current thread's execution ... yeah, my head hurts to – MadProgrammer Jan 22 '16 at 12:43
  • Thanks! Yes, you may well put "block" in inverted commas, because we are indeed talking about the EDT... I didn't know that's how modal dialogs work... so are they more or less a unique feature in the whole Swing/AWT architecture? Do you know anywhere I can read up on this? – mike rodent Jan 22 '16 at 20:03
  • 1
    I dug around the API source code, but you might want to look at [Foxtrot](http://foxtrot.sourceforge.net/) which is an API which does the same kind of thing, but allows you to call other pieces of code – MadProgrammer Jan 22 '16 at 20:16
  • Looks interesting. Thanks again. – mike rodent Jan 22 '16 at 20:21
0

Perform necessary steps on the EDT, and signal the test thread whether an additional step is needed:

class PrepDlgDismiss implements Runnable {
  boolean file_chooser_visible;
  @Override
  public void run() {
    boolean focus_on_dlg = main_frame.file_chooser_dlg.hasFocus();
    if( main_frame.file_chooser_dlg.isVisible() ){
      file_chooser_visible = true;
      if( ! focus_on_dlg ){
        main_frame.file_chooser_dlg.requestFocus();
      }
    }
  }
}
PrepDlgDismiss task = new PrepDlgDismiss();
EventQueue.invokeAndWait( task );
if( task.file_chooser_visible ){
  robot.type_input( KeyEvent.VK_F4, KeyEvent.VK_ALT );
}
erickson
  • 265,237
  • 58
  • 395
  • 493
  • Thanks... This is a practical answer which "refactors" the question, although I would say from a concurrency PoV it's not really very desirable to "store" the visibility status like this. But the main point of my question is to understand whether use of Future.get() here results in another "event pump" starting if another EDT event is generated when get() is blocking the EDT... – mike rodent Jan 22 '16 at 08:13
  • @mikerodent What is your concern regarding visibility status from a concurrency POV? While it's not documented authoritatively, there is a *happens-before* relationship between actions in the task executed by `invokeAndWait()` and the return of `invokeAndWait()`. Even with this *de facto* assurance, do you still feel uncomfortable? If that's the case, you should definitely not depend on behavior that is even less assured, such as a new event pump starting when EDT is blocked. – erickson Jan 22 '16 at 19:50
  • In fact, above all I'm trying to understand *whether* an event pump comes into play in the circs I've described: I'm not depending on it... I want to understand! My point about the "visible" status here is just the really idiotic one that, in between the end of the `invokeAndWait`, and the running of the `robot.type_input` command some other bit of code might have dismissed the dialog... and this Alt-F4 robot typing might (for example) then cause something else to close... It's pure concurrency finickittiness on my part. – mike rodent Jan 22 '16 at 19:58
  • @mikerodent Closing the wrong window can happen whenever `Robot` is used to inject input into the underlying system's queue; your initial version is subject to the same problem. Even if `Robot` ran on the EDT, activity outside the application could change the focus asynchronously. – erickson Jan 22 '16 at 20:04
  • As to your "real" question, that could depend on the JVM you are running, as it's unspecified behavior. If you identified a specific JVM, perhaps looking at the source code could dig up an answer. – erickson Jan 22 '16 at 20:06
  • Except that in my version all that `Runnable` monopolises the EDT for its entire duration, right? So calling `Future.get()` is a way to make sure nothing else does something in the EDT such as close the file chooser `JDialog`... (?) – mike rodent Jan 22 '16 at 20:07
  • Ah, really, so it's actually JVM-dependent... wow, that had not occurred to me. Unfortunately we're being told to curtail this discussion, which is a shame because I'm learning things. – mike rodent Jan 22 '16 at 20:08
  • @mikerodent Yes, if no other event pumps are started, your Java application won't be able to alter the focus, but the OS can still change focus (Alt-Tab or whatever), right? So you still have a race condition where you don't know which window has focus when you press Alt-F4. This actually happens sometimes when I run something, focus on a second application, then the first application pops up a dialog just as I'm pressing Enter in the second application. Luckily it's never been a "format drive?" confirmation, knock on wood. – erickson Jan 22 '16 at 20:11
  • OK, fair point... Haha, yes, or "confirm launch thermonuclear strike" – mike rodent Jan 22 '16 at 20:13