001 /*
002 * Created on Mar 27, 2008
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
005 * the License. You may obtain a copy of the License at
006 *
007 * http://www.apache.org/licenses/LICENSE-2.0
008 *
009 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
010 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
011 * specific language governing permissions and limitations under the License.
012 *
013 * Copyright @2008-2010 the original author or authors.
014 */
015 package org.fest.swing.listener;
016
017 import static javax.swing.SwingUtilities.invokeLater;
018 import static javax.swing.SwingUtilities.isEventDispatchThread;
019
020 import java.awt.AWTEvent;
021 import java.awt.event.AWTEventListener;
022 import java.util.ArrayList;
023 import java.util.List;
024
025 import net.jcip.annotations.GuardedBy;
026 import net.jcip.annotations.ThreadSafe;
027
028 /**
029 * Understands a <code>{@link AWTEventListener}</code> that ensures all events are handled on the event dispatch
030 * thread.
031 * <p>
032 * NOTE from Abbot: Applet runners may run several simultaneous event dispatch threads when displaying multiple applets
033 * simultaneously. If this listener is installed in the parent context of those dispatch threads, it will be invoked on
034 * each of those threads, possibly simultaneously.
035 * </p>
036 *
037 * @author Yvonne Wang
038 * @author Alex Ruiz
039 */
040 @ThreadSafe
041 public abstract class EventDispatchThreadedEventListener implements AWTEventListener {
042
043 @GuardedBy("lock") private final List<AWTEvent> deferredEvents = new ArrayList<AWTEvent>();
044 private final Object lock = new Object();
045
046 private final Runnable processDeferredEventsTask = new Runnable() {
047 public void run() {
048 processDeferredEvents();
049 }
050 };
051
052 /**
053 * If this method is called in the event dispatch thread, it processes the given event and the queued ones. Otherwise
054 * it will add the given event to the queue and process all the events in the queue in the event dispatch thread.
055 * @param event the event to process.
056 */
057 public void eventDispatched(AWTEvent event) {
058 if (!isEventDispatchThread()) {
059 // Often the application under test will invoke Window.show, which spawns hierarchy events. We want to ensure we
060 // respond to those events on the dispatch thread to avoid deadlock.
061 synchronized (lock) {
062 deferredEvents.add(event);
063 }
064 // Ensure that in the absence of any subsequent event thread events deferred events still get processed.
065 // If regular events are received before this action is run, the deferred events will be processed prior to those
066 // events and the action will do nothing.
067 invokeLater(processDeferredEventsTask);
068 return;
069 }
070 // Ensure any deferred events are processed prior to subsequently posted events.
071 processDeferredEvents();
072 processEvent(event);
073 }
074
075 /** Processes any events that were generated off the event queue but not immediately handled. */
076 protected void processDeferredEvents() {
077 // Make a copy of the deferred events and empty the queue
078 List<AWTEvent> queue = new ArrayList<AWTEvent>();
079 synchronized (lock) {
080 // In the rare case where there are multiple simultaneous dispatch threads, it's possible for deferred events to
081 // get posted while another event is being processed. At most this will mean a few events get processed out of
082 // order, but they will likely be from different event dispatch contexts, so it shouldn't matter.
083 queue.addAll(deferredEvents);
084 deferredEvents.clear();
085 }
086 while (queue.size() > 0) {
087 AWTEvent event = queue.get(0);
088 queue.remove(0);
089 processEvent(event);
090 }
091 }
092
093 /**
094 * This method is not protected by any synchronization locks (nor should it be); in the presence of multiple
095 * simultaneous event dispatch threads, the listener must be thread-safe.
096 * @param event the event to process.
097 */
098 protected abstract void processEvent(AWTEvent event);
099 }