/* * @(#)EventDispatchThread.java 1.31 06/10/10 * * Copyright 1990-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 only, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License version 2 for more details (a copy is * included at /legal/license.txt). * * You should have received a copy of the GNU General Public License * version 2 along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa * Clara, CA 95054 or visit www.sun.com if you need additional * information or have any questions. * */ package java.awt; import java.lang.reflect.Method; import java.security.AccessController; import sun.security.action.GetPropertyAction; import java.lang.ref.WeakReference; import java.security.PrivilegedAction; /** * EventDispatchThread is a package-private AWT class which takes * events off the EventQueue and dispatches them to the appropriate * AWT components. * * The Thread starts a "permanent" event pump with a call to * pumpEvents(Conditional) in its run() method. Event handlers can choose to * block this event pump at any time, but should start a new pump (<b>not</b> * a new EventDispatchThread) by again calling pumpEvents(Conditional). This * secondary event pump will exit automatically as soon as the Condtional * evaluate()s to false and an additional Event is pumped and dispatched. * * @author Tom Ball * @author Amy Fowler * @author Fred Ecks * @author David Mendenhall * * @version 1.34, 02/02/00 * @since 1.1 */ class EventDispatchThread extends Thread { EventQueueProxy theQueue; // 6261461 boolean doDispatch = true; static final int ANY_EVENT = -1; EventDispatchThread() { // 6261461 } static EventDispatchThread create(String name, EventQueueProxy queue) { EventDispatchThread edt = (EventDispatchThread) AccessController.doPrivileged(new PrivilegedAction() { public Object run() { try { Class dispatchThreadClass = Class.forName( System.getProperty( "java.awt.EventDispatchThread.classname", "java.awt.EventDispatchThread")); return dispatchThreadClass.newInstance(); } catch (Exception e) { return new EventDispatchThread(); } } }); edt.setName(name); edt.theQueue = queue; return edt; } public void stopDispatching() { // Note: We stop dispatching via a flag rather than using // Thread.interrupt() because we can't guarantee that the wait() // we interrupt will be EventQueue.getNextEvent()'s. -fredx 8-11-98 doDispatch = false; // fix 4122683, 4128923 // Post an empty event to ensure getNextEvent is unblocked // // We have to use postEventPrivate instead of postEvent because // EventQueue.pop calls EventDispatchThread.stopDispatching. // Calling SunToolkit.flushPendingEvents in this case could // lead to deadlock. theQueue.postEventPrivate(new EmptyEvent()); // wait for the dispatcher to complete if (Thread.currentThread() != this) { try { join(); } catch (InterruptedException e) {} } } class EmptyEvent extends AWTEvent implements ActiveEvent { public EmptyEvent() { super(EventDispatchThread.this, 0); } public void dispatch() {} } public void run() { pumpEvents(new Conditional() { public boolean evaluate() { return true; } } ); } void pumpEvents(Conditional cond) { pumpEvents(ANY_EVENT, cond); } void pumpEvents(int id, Conditional cond) { while (doDispatch && cond.evaluate()) { if (isInterrupted() || !pumpOneEvent(id)) { doDispatch = false; } } } boolean pumpOneEvent(int id) { try { AWTEvent event = (id == ANY_EVENT) ? theQueue.getNextEvent() : theQueue.getNextEvent(id); theQueue.dispatchEvent(event); return true; } catch (ThreadDeath death) { return false; } catch (InterruptedException interruptedException) { return false; // AppContext.dispose() interrupts all // Threads in the AppContext } catch (Throwable e) { if (!handleException(e)) { System.err.println( "Exception occurred during event dispatching:"); e.printStackTrace(); } return true; } } 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 fix 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 * * @returns <tt>false</tt> if any of the above steps failed, otherwise * <tt>true</tt>. */ 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) { // note : getQueue() can return null, hence we changed the order return eq.equals(theQueue.getQueue()); // 6261461 } EventQueue getEventQueue() { return theQueue.getQueue(); // 6261461 this can be null } } // 6261461 // Ideally EventQueueProxy should be defined in EventQueue class, since it // accesses package protected variables. This is defined here since we // share this file between basis and personal and the implementation of the // methods are identical. This helps us in ease of maintaining the proxy class. /** */ class EventQueueProxy { WeakReference eventQueueRef ; int waitForID; EventQueueProxy(EventQueue eq) { this.eventQueueRef = new WeakReference(eq); } void postEventPrivate(AWTEvent theEvent) { EventQueue eq = getQueue(); if ( eq != null ) eq.postEventPrivate(theEvent); else { // the event queue must have been collected, so call notify // so that any thread blocked on getNextEvent() can unblock synchronized(this) { this.notifyAll(); } } } public synchronized AWTEvent getNextEvent() throws InterruptedException { do { EventQueue eq = getQueue(); if ( eq == null ) throw new InterruptedException(); for (int i = EventQueue.NUM_PRIORITIES - 1; i >= 0; i--) { if (eq.queues[i].head != null) { EventQueueItem eqi = eq.queues[i].head; eq.queues[i].head = eqi.next; if (eqi.next == null) { eq.queues[i].tail = null; } return eqi.event; } } // dont reference event queue since we are going to wait. // this will allow gc on the event queue. eq = null; wait(); } while (true); } AWTEvent getNextEvent(int id) throws InterruptedException { do { EventQueue eq = getQueue(); if ( eq == null ) throw new InterruptedException(); synchronized(this) { for (int i = 0; i < EventQueue.NUM_PRIORITIES; i++) { for (EventQueueItem entry = eq.queues[i].head, prev = null; entry != null; prev = entry, entry = entry.next) { if (entry.id == id) { if (prev == null) { eq.queues[i].head = entry.next; } else { prev.next = entry.next; } if (eq.queues[i].tail == entry) { eq.queues[i].tail = prev; } return entry.event; } } } // dont reference event queue since we are going to wait. // this will allow gc on the event queue. eq = null; this.waitForID = id; wait(); this.waitForID = 0; } } while(true); } void dispatchEvent(AWTEvent event) { EventQueue eq = getQueue(); if ( eq != null ) eq.dispatchEvent(event); } EventQueue getQueue() { return (EventQueue)(EventQueue)eventQueueRef.get(); } } // 6261461