01: package abbot.util;
02:
03: import java.awt.AWTEvent;
04: import java.awt.event.AWTEventListener;
05: import java.util.ArrayList;
06:
07: import javax.swing.SwingUtilities;
08:
09: import abbot.Log;
10: import abbot.tester.Robot;
11:
12: /** Provide an AWTEventListener which ensures all events are handled on the
13: event dispatch thread. This allows the recorders and other
14: listeners to safely manipulate GUI objects without concern for event
15: dispatch thread-safety.
16: <p>
17: Window.show generates WINDOW_OPENED (and possibly hierarchy and other
18: events) to any listeners from whatever thread the method was invoked on.
19: <p>
20: NOTE: Applet runners may run several simultaneous event dispatch threads
21: when displaying multiple applets simultaneously. If this listener is
22: installed in the parent context of those dispatch threads, it will be
23: invoked on each of those threads, possibly simultaneously.
24: */
25: public abstract class SingleThreadedEventListener implements
26: AWTEventListener {
27: private ArrayList deferredEvents = new ArrayList();
28:
29: private Runnable action = new Runnable() {
30: public void run() {
31: processDeferredEvents();
32: }
33: };
34:
35: /** Event reception callback. */
36: public void eventDispatched(AWTEvent event) {
37: if (!SwingUtilities.isEventDispatchThread()) {
38: // Often the application under test will invoke Window.show, which
39: // spawns hierarchy events. We want to ensure we respond to those
40: // events on the dispatch thread to avoid deadlock.
41: Log.debug("deferring event handling of "
42: + Robot.toString(event));
43: synchronized (deferredEvents) {
44: deferredEvents.add(event);
45: }
46: // Ensure that in the absence of any subsequent event thread
47: // events deferred events still get processed.
48: // If regular events are received before this action is run, the
49: // deferred events will be processed prior to those events and the
50: // action will do nothing.
51: SwingUtilities.invokeLater(action);
52: } else {
53: // Ensure any deferred events are processed prior to subsequently
54: // posted events.
55: processDeferredEvents();
56: processEvent(event);
57: }
58: }
59:
60: /** Process any events that were generated off the event queue but not
61: immediately handled.
62: */
63: protected void processDeferredEvents() {
64: // Make a copy of the deferred events and empty the queue
65: ArrayList queue = new ArrayList();
66: synchronized (deferredEvents) {
67: // In the rare case where there are multiple simultaneous dispatch
68: // threads, it's possible for deferred events to get posted while
69: // another event is being processed. At most this will mean a few
70: // events get processed out of order, but they will likely be from
71: // different event dispatch contexts, so it shouldn't matter.
72: queue.addAll(deferredEvents);
73: deferredEvents.clear();
74: }
75: while (queue.size() > 0) {
76: AWTEvent prev = null;
77: Log.debug("processing deferred event");
78: // Process any events that were generated
79: prev = (AWTEvent) queue.get(0);
80: queue.remove(0);
81: processEvent(prev);
82: }
83: }
84:
85: /** This method is not protected by any synchronization locks (nor should
86: it be); in the presence of multiple simultaneous event dispatch
87: threads, the listener must be threadsafe.
88: */
89: protected abstract void processEvent(AWTEvent event);
90: }
|