package abbot.util; import java.awt.AWTEvent; import java.awt.event.AWTEventListener; import java.util.ArrayList; import javax.swing.SwingUtilities; import abbot.Log; import abbot.tester.Robot; /** Provide an AWTEventListener which ensures all events are handled on the event dispatch thread. This allows the recorders and other listeners to safely manipulate GUI objects without concern for event dispatch thread-safety. <p> Window.show generates WINDOW_OPENED (and possibly hierarchy and other events) to any listeners from whatever thread the method was invoked on. <p> NOTE: Applet runners may run several simultaneous event dispatch threads when displaying multiple applets simultaneously. If this listener is installed in the parent context of those dispatch threads, it will be invoked on each of those threads, possibly simultaneously. */ public abstract class SingleThreadedEventListener implements AWTEventListener { private ArrayList deferredEvents = new ArrayList(); private Runnable action = new Runnable() { public void run() { processDeferredEvents(); } }; /** Event reception callback. */ public void eventDispatched(AWTEvent event) { if (!SwingUtilities.isEventDispatchThread()) { // Often the application under test will invoke Window.show, which // spawns hierarchy events. We want to ensure we respond to those // events on the dispatch thread to avoid deadlock. Log.debug("deferring event handling of " + Robot.toString(event)); synchronized(deferredEvents) { deferredEvents.add(event); } // Ensure that in the absence of any subsequent event thread // events deferred events still get processed. // If regular events are received before this action is run, the // deferred events will be processed prior to those events and the // action will do nothing. SwingUtilities.invokeLater(action); } else { // Ensure any deferred events are processed prior to subsequently // posted events. processDeferredEvents(); processEvent(event); } } /** Process any events that were generated off the event queue but not immediately handled. */ protected void processDeferredEvents() { // Make a copy of the deferred events and empty the queue ArrayList queue = new ArrayList(); synchronized(deferredEvents) { // In the rare case where there are multiple simultaneous dispatch // threads, it's possible for deferred events to get posted while // another event is being processed. At most this will mean a few // events get processed out of order, but they will likely be from // different event dispatch contexts, so it shouldn't matter. queue.addAll(deferredEvents); deferredEvents.clear(); } while (queue.size() > 0) { AWTEvent prev = null; Log.debug("processing deferred event"); // Process any events that were generated prev = (AWTEvent)queue.get(0); queue.remove(0); processEvent(prev); } } /** This method is not protected by any synchronization locks (nor should it be); in the presence of multiple simultaneous event dispatch threads, the listener must be threadsafe. */ protected abstract void processEvent(AWTEvent event); }