EventDispatchThread.java revision 0
0N/A/*
0N/A * Copyright 1996-2007 Sun Microsystems, Inc. All Rights Reserved.
2362N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
0N/A * published by the Free Software Foundation. Sun designates this
2362N/A * particular file as subject to the "Classpath" exception as provided
0N/A * by Sun in the LICENSE file that accompanied this code.
2362N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
0N/A * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
2362N/A * CA 95054 USA or visit www.sun.com if you need additional information or
2362N/A * have any questions.
2362N/A */
0N/A
0N/Apackage java.awt;
0N/A
0N/Aimport java.awt.event.InputEvent;
0N/Aimport java.awt.event.MouseEvent;
0N/Aimport java.awt.event.ActionEvent;
0N/Aimport java.awt.event.WindowEvent;
0N/Aimport java.lang.reflect.Method;
0N/Aimport java.security.AccessController;
0N/Aimport sun.security.action.GetPropertyAction;
0N/Aimport sun.awt.AWTAutoShutdown;
0N/Aimport sun.awt.SunToolkit;
0N/A
0N/Aimport java.util.Vector;
0N/Aimport java.util.logging.*;
0N/A
0N/Aimport sun.awt.dnd.SunDragSourceContextPeer;
0N/A
0N/A/**
0N/A * EventDispatchThread is a package-private AWT class which takes
0N/A * events off the EventQueue and dispatches them to the appropriate
0N/A * AWT components.
0N/A *
0N/A * The Thread starts a "permanent" event pump with a call to
0N/A * pumpEvents(Conditional) in its run() method. Event handlers can choose to
0N/A * block this event pump at any time, but should start a new pump (<b>not</b>
0N/A * a new EventDispatchThread) by again calling pumpEvents(Conditional). This
0N/A * secondary event pump will exit automatically as soon as the Condtional
0N/A * evaluate()s to false and an additional Event is pumped and dispatched.
0N/A *
0N/A * @author Tom Ball
0N/A * @author Amy Fowler
0N/A * @author Fred Ecks
0N/A * @author David Mendenhall
0N/A *
0N/A * @since 1.1
0N/A */
0N/Aclass EventDispatchThread extends Thread {
0N/A private static final Logger eventLog = Logger.getLogger("java.awt.event.EventDispatchThread");
0N/A
0N/A private EventQueue theQueue;
0N/A private boolean doDispatch = true;
0N/A private static final int ANY_EVENT = -1;
0N/A
0N/A private Vector<EventFilter> eventFilters = new Vector<EventFilter>();
0N/A // used in handleException
0N/A private int modalFiltersCount = 0;
0N/A
0N/A EventDispatchThread(ThreadGroup group, String name, EventQueue queue) {
0N/A super(group, name);
0N/A theQueue = queue;
0N/A }
0N/A
0N/A void stopDispatchingImpl(boolean wait) {
0N/A // Note: We stop dispatching via a flag rather than using
0N/A // Thread.interrupt() because we can't guarantee that the wait()
0N/A // we interrupt will be EventQueue.getNextEvent()'s. -fredx 8-11-98
0N/A
0N/A StopDispatchEvent stopEvent = new StopDispatchEvent();
0N/A
0N/A // wait for the dispatcher to complete
0N/A if (Thread.currentThread() != this) {
0N/A
0N/A // fix 4122683, 4128923
0N/A // Post an empty event to ensure getNextEvent is unblocked
0N/A //
0N/A // We have to use postEventPrivate instead of postEvent because
0N/A // EventQueue.pop calls EventDispatchThread.stopDispatching.
0N/A // Calling SunToolkit.flushPendingEvents in this case could
0N/A // lead to deadlock.
0N/A theQueue.postEventPrivate(stopEvent);
0N/A
0N/A if (wait) {
0N/A try {
0N/A join();
0N/A } catch(InterruptedException e) {
0N/A }
0N/A }
0N/A } else {
0N/A stopEvent.dispatch();
0N/A }
0N/A synchronized (theQueue) {
0N/A if (theQueue.getDispatchThread() == this) {
0N/A theQueue.detachDispatchThread();
0N/A }
0N/A }
0N/A }
0N/A
0N/A public void stopDispatching() {
0N/A stopDispatchingImpl(true);
0N/A }
0N/A
0N/A public void stopDispatchingLater() {
0N/A stopDispatchingImpl(false);
0N/A }
0N/A
0N/A class StopDispatchEvent extends AWTEvent implements ActiveEvent {
0N/A /*
0N/A * serialVersionUID
0N/A */
0N/A static final long serialVersionUID = -3692158172100730735L;
0N/A
0N/A public StopDispatchEvent() {
0N/A super(EventDispatchThread.this,0);
0N/A }
0N/A
0N/A public void dispatch() {
0N/A doDispatch = false;
0N/A }
0N/A }
0N/A
0N/A public void run() {
0N/A try {
0N/A pumpEvents(new Conditional() {
0N/A public boolean evaluate() {
0N/A return true;
0N/A }
0N/A });
0N/A } finally {
0N/A /*
0N/A * This synchronized block is to secure that the event dispatch
0N/A * thread won't die in the middle of posting a new event to the
0N/A * associated event queue. It is important because we notify
0N/A * that the event dispatch thread is busy after posting a new event
0N/A * to its queue, so the EventQueue.dispatchThread reference must
0N/A * be valid at that point.
0N/A */
0N/A synchronized (theQueue) {
0N/A if (theQueue.getDispatchThread() == this) {
0N/A theQueue.detachDispatchThread();
0N/A }
0N/A /*
0N/A * Event dispatch thread dies in case of an uncaught exception.
0N/A * A new event dispatch thread for this queue will be started
0N/A * only if a new event is posted to it. In case if no more
0N/A * events are posted after this thread died all events that
0N/A * currently are in the queue will never be dispatched.
0N/A */
0N/A /*
0N/A * Fix for 4648733. Check both the associated java event
0N/A * queue and the PostEventQueue.
0N/A */
0N/A if (theQueue.peekEvent() != null ||
0N/A !SunToolkit.isPostEventQueueEmpty()) {
0N/A theQueue.initDispatchThread();
0N/A }
0N/A AWTAutoShutdown.getInstance().notifyThreadFree(this);
0N/A }
0N/A }
0N/A }
0N/A
0N/A void pumpEvents(Conditional cond) {
0N/A pumpEvents(ANY_EVENT, cond);
0N/A }
0N/A
0N/A void pumpEventsForHierarchy(Conditional cond, Component modalComponent) {
0N/A pumpEventsForHierarchy(ANY_EVENT, cond, modalComponent);
0N/A }
0N/A
0N/A void pumpEvents(int id, Conditional cond) {
0N/A pumpEventsForHierarchy(id, cond, null);
0N/A }
0N/A
0N/A void pumpEventsForHierarchy(int id, Conditional cond, Component modalComponent)
0N/A {
pumpEventsForFilter(id, cond, new HierarchyEventFilter(modalComponent));
}
void pumpEventsForFilter(Conditional cond, EventFilter filter) {
pumpEventsForFilter(ANY_EVENT, cond, filter);
}
void pumpEventsForFilter(int id, Conditional cond, EventFilter filter) {
addEventFilter(filter);
while (doDispatch && cond.evaluate()) {
if (isInterrupted() || !pumpOneEventForFilters(id)) {
doDispatch = false;
}
}
removeEventFilter(filter);
}
void addEventFilter(EventFilter filter) {
synchronized (eventFilters) {
if (!eventFilters.contains(filter)) {
if (filter instanceof ModalEventFilter) {
ModalEventFilter newFilter = (ModalEventFilter)filter;
int k = 0;
for (k = 0; k < eventFilters.size(); k++) {
EventFilter f = eventFilters.get(k);
if (f instanceof ModalEventFilter) {
ModalEventFilter cf = (ModalEventFilter)f;
if (cf.compareTo(newFilter) > 0) {
break;
}
}
}
eventFilters.add(k, filter);
modalFiltersCount++;
} else {
eventFilters.add(filter);
}
}
}
}
void removeEventFilter(EventFilter filter) {
synchronized (eventFilters) {
if (eventFilters.contains(filter)) {
if (filter instanceof ModalEventFilter) {
modalFiltersCount--;
}
eventFilters.remove(filter);
}
}
}
boolean pumpOneEventForFilters(int id) {
try {
AWTEvent event;
boolean eventOK;
do {
event = (id == ANY_EVENT)
? theQueue.getNextEvent()
: theQueue.getNextEvent(id);
eventOK = true;
synchronized (eventFilters) {
for (int i = eventFilters.size() - 1; i >= 0; i--) {
EventFilter f = eventFilters.get(i);
EventFilter.FilterAction accept = f.acceptEvent(event);
if (accept == EventFilter.FilterAction.REJECT) {
eventOK = false;
break;
} else if (accept == EventFilter.FilterAction.ACCEPT_IMMEDIATELY) {
break;
}
}
}
eventOK = eventOK && SunDragSourceContextPeer.checkEvent(event);
if (!eventOK) {
event.consume();
}
}
while (eventOK == false);
if (eventLog.isLoggable(Level.FINEST)) {
eventLog.log(Level.FINEST, "Dispatching: " + event);
}
theQueue.dispatchEvent(event);
return true;
}
catch (ThreadDeath death) {
return false;
}
catch (InterruptedException interruptedException) {
return false; // AppContext.dispose() interrupts all
// Threads in the AppContext
}
// Can get and throw only unchecked exceptions
catch (RuntimeException e) {
processException(e, modalFiltersCount > 0);
} catch (Error e) {
processException(e, modalFiltersCount > 0);
}
return true;
}
private void processException(Throwable e, boolean isModal) {
if (eventLog.isLoggable(Level.FINE)) {
eventLog.log(Level.FINE, "Processing exception: " + e +
", isModal = " + isModal);
}
if (!handleException(e)) {
// See bug ID 4499199.
// If we are in a modal dialog, we cannot throw
// an exception for the ThreadGroup to handle (as added
// in RFE 4063022). If we did, the message pump of
// the modal dialog would be interrupted.
// We instead choose to handle the exception ourselves.
// It may be useful to add either a runtime flag or API
// later if someone would like to instead dispose the
// dialog and allow the thread group to handle it.
if (isModal) {
System.err.println(
"Exception occurred during event dispatching:");
e.printStackTrace();
} else if (e instanceof RuntimeException) {
throw (RuntimeException)e;
} else if (e instanceof Error) {
throw (Error)e;
}
}
}
private static final String handlerPropName = "sun.awt.exception.handler";
private static String handlerClassName = null;
private static String NO_HANDLER = new String();
/**
* Handles an exception thrown in the event-dispatch thread.
*
* <p> If the system property "sun.awt.exception.handler" is defined, then
* when this method is invoked it will attempt to do the following:
*
* <ol>
* <li> Load the class named by the value of that property, using the
* current thread's context class loader,
* <li> Instantiate that class using its zero-argument constructor,
* <li> Find the resulting handler object's <tt>public void handle</tt>
* method, which should take a single argument of type
* <tt>Throwable</tt>, and
* <li> Invoke the handler's <tt>handle</tt> method, passing it the
* <tt>thrown</tt> argument that was passed to this method.
* </ol>
*
* If any of the first three steps fail then this method will return
* <tt>false</tt> and all following invocations of this method will return
* <tt>false</tt> immediately. An exception thrown by the handler object's
* <tt>handle</tt> will be caught, and will cause this method to return
* <tt>false</tt>. If the handler's <tt>handle</tt> method is successfully
* invoked, then this method will return <tt>true</tt>. This method will
* never throw any sort of exception.
*
* <p> <i>Note:</i> This method is a temporary hack to work around the
* absence of a real API that provides the ability to replace the
* event-dispatch thread. The magic "sun.awt.exception.handler" property
* <i>will be removed</i> in a future release.
*
* @param thrown The Throwable that was thrown in the event-dispatch
* thread
*
* @return <tt>false</tt> if any of the above steps failed, otherwise
* <tt>true</tt>
*/
private boolean handleException(Throwable thrown) {
try {
if (handlerClassName == NO_HANDLER) {
return false; /* Already tried, and failed */
}
/* Look up the class name */
if (handlerClassName == null) {
handlerClassName = ((String) AccessController.doPrivileged(
new GetPropertyAction(handlerPropName)));
if (handlerClassName == null) {
handlerClassName = NO_HANDLER; /* Do not try this again */
return false;
}
}
/* Load the class, instantiate it, and find its handle method */
Method m;
Object h;
try {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Class c = Class.forName(handlerClassName, true, cl);
m = c.getMethod("handle", new Class[] { Throwable.class });
h = c.newInstance();
} catch (Throwable x) {
handlerClassName = NO_HANDLER; /* Do not try this again */
return false;
}
/* Finally, invoke the handler */
m.invoke(h, new Object[] { thrown });
} catch (Throwable x) {
return false;
}
return true;
}
boolean isDispatching(EventQueue eq) {
return theQueue.equals(eq);
}
EventQueue getEventQueue() { return theQueue; }
private static class HierarchyEventFilter implements EventFilter {
private Component modalComponent;
public HierarchyEventFilter(Component modalComponent) {
this.modalComponent = modalComponent;
}
public FilterAction acceptEvent(AWTEvent event) {
if (modalComponent != null) {
int eventID = event.getID();
boolean mouseEvent = (eventID >= MouseEvent.MOUSE_FIRST) &&
(eventID <= MouseEvent.MOUSE_LAST);
boolean actionEvent = (eventID >= ActionEvent.ACTION_FIRST) &&
(eventID <= ActionEvent.ACTION_LAST);
boolean windowClosingEvent = (eventID == WindowEvent.WINDOW_CLOSING);
/*
* filter out MouseEvent and ActionEvent that's outside
* the modalComponent hierarchy.
* KeyEvent is handled by using enqueueKeyEvent
* in Dialog.show
*/
if (Component.isInstanceOf(modalComponent, "javax.swing.JInternalFrame")) {
/*
* Modal internal frames are handled separately. If event is
* for some component from another heavyweight than modalComp,
* it is accepted. If heavyweight is the same - we still accept
* event and perform further filtering in LightweightDispatcher
*/
return windowClosingEvent ? FilterAction.REJECT : FilterAction.ACCEPT;
}
if (mouseEvent || actionEvent || windowClosingEvent) {
Object o = event.getSource();
if (o instanceof sun.awt.ModalExclude) {
// Exclude this object from modality and
// continue to pump it's events.
return FilterAction.ACCEPT;
} else if (o instanceof Component) {
Component c = (Component) o;
// 5.0u3 modal exclusion
boolean modalExcluded = false;
if (modalComponent instanceof Container) {
while (c != modalComponent && c != null) {
if ((c instanceof Window) &&
(sun.awt.SunToolkit.isModalExcluded((Window)c))) {
// Exclude this window and all its children from
// modality and continue to pump it's events.
modalExcluded = true;
break;
}
c = c.getParent();
}
}
if (!modalExcluded && (c != modalComponent)) {
return FilterAction.REJECT;
}
}
}
}
return FilterAction.ACCEPT;
}
}
}