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 }