1. /*
  2. * @(#)EventDispatchThread.java 1.36 01/04/21
  3. *
  4. * Copyright 1996-2001 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. package java.awt;
  11. import java.lang.reflect.Method;
  12. import java.security.AccessController;
  13. import sun.security.action.GetPropertyAction;
  14. import sun.awt.DebugHelper;
  15. import java.awt.event.InputEvent;
  16. /**
  17. * EventDispatchThread is a package-private AWT class which takes
  18. * events off the EventQueue and dispatches them to the appropriate
  19. * AWT components.
  20. *
  21. * The Thread starts a "permanent" event pump with a call to
  22. * pumpEvents(Conditional) in its run() method. Event handlers can choose to
  23. * block this event pump at any time, but should start a new pump (<b>not</b>
  24. * a new EventDispatchThread) by again calling pumpEvents(Conditional). This
  25. * secondary event pump will exit automatically as soon as the Condtional
  26. * evaluate()s to false and an additional Event is pumped and dispatched.
  27. *
  28. * @author Tom Ball
  29. * @author Amy Fowler
  30. * @author Fred Ecks
  31. * @author David Mendenhall
  32. *
  33. * @version 1.36, 04/21/01
  34. * @since 1.1
  35. */
  36. class EventDispatchThread extends Thread {
  37. private static final DebugHelper dbg = DebugHelper.create(EventDispatchThread.class);
  38. private EventQueue theQueue;
  39. private boolean doDispatch = true;
  40. EventDispatchThread(String name, EventQueue queue) {
  41. super(name);
  42. theQueue = queue;
  43. }
  44. public void stopDispatching() {
  45. // Note: We stop dispatching via a flag rather than using
  46. // Thread.interrupt() because we can't guarantee that the wait()
  47. // we interrupt will be EventQueue.getNextEvent()'s. -fredx 8-11-98
  48. doDispatch = false;
  49. // fix 4122683, 4128923
  50. // Post an empty event to ensure getNextEvent is unblocked
  51. //
  52. // We have to use postEventPrivate instead of postEvent because
  53. // EventQueue.pop calls EventDispatchThread.stopDispatching.
  54. // Calling SunToolkit.flushPendingEvents in this case could
  55. // lead to deadlock.
  56. theQueue.postEventPrivate(new EmptyEvent());
  57. // wait for the dispatcher to complete
  58. if (Thread.currentThread() != this) {
  59. try {
  60. join();
  61. } catch(InterruptedException e) {
  62. }
  63. }
  64. }
  65. class EmptyEvent extends AWTEvent implements ActiveEvent {
  66. public EmptyEvent() {
  67. super(EventDispatchThread.this,0);
  68. }
  69. public void dispatch() {}
  70. }
  71. public void run() {
  72. pumpEvents(new Conditional() {
  73. public boolean evaluate() {
  74. return true;
  75. }
  76. });
  77. }
  78. void pumpEvents(Conditional cond) {
  79. pumpEventsForHierarchy(cond, null);
  80. }
  81. void pumpEventsForHierarchy(Conditional cond, Component modalComponent) {
  82. while (doDispatch && cond.evaluate()) {
  83. if (isInterrupted() || !pumpOneEventForHierarchy(modalComponent)) {
  84. doDispatch = false;
  85. }
  86. }
  87. }
  88. boolean pumpOneEventForHierarchy(Component modalComponent) {
  89. try {
  90. AWTEvent event = theQueue.getNextEvent();
  91. if (modalComponent != null) {
  92. /*
  93. * filter out InputEvent that's not belong to
  94. * the specified modal component.
  95. * this can be caused by the following case:
  96. * a button, click once to open up a modal dialog
  97. * but use click on it twice really fast.
  98. * before the modal dialog comes up, the second
  99. * mouse click already comes in.
  100. * see also the comment in Dialog.show
  101. */
  102. while (event instanceof InputEvent) {
  103. Component c = (Component)event.getSource();
  104. // check if c's modalComponent's child
  105. if (modalComponent instanceof Container)
  106. while (c != modalComponent && c != null)
  107. c = c.getParent();
  108. if (c != modalComponent)
  109. event = theQueue.getNextEvent();
  110. else
  111. break;
  112. }
  113. }
  114. if ( dbg.on ) dbg.println("Dispatching: "+event);
  115. theQueue.dispatchEvent(event);
  116. return true;
  117. } catch (ThreadDeath death) {
  118. return false;
  119. } catch (InterruptedException interruptedException) {
  120. return false; // AppContext.dispose() interrupts all
  121. // Threads in the AppContext
  122. } catch (Throwable e) {
  123. if (!handleException(e)) {
  124. System.err.println(
  125. "Exception occurred during event dispatching:");
  126. e.printStackTrace();
  127. }
  128. return true;
  129. }
  130. }
  131. private static final String handlerPropName = "sun.awt.exception.handler";
  132. private static String handlerClassName = null;
  133. private static String NO_HANDLER = new String();
  134. /**
  135. * Handles an exception thrown in the event-dispatch thread.
  136. *
  137. * <p> If the system property "sun.awt.exception.handler" is defined, then
  138. * when this method is invoked it will attempt to do the following:
  139. *
  140. * <ol>
  141. * <li> Load the class named by the value of that property, using the
  142. * current thread's context class loader,
  143. * <li> Instantiate that class using its zero-argument constructor,
  144. * <li> Find the resulting handler object's <tt>public void handle</tt>
  145. * method, which should take a single argument of type
  146. * <tt>Throwable</tt>, and
  147. * <li> Invoke the handler's <tt>handle</tt> method, passing it the
  148. * <tt>thrown</tt> argument that was passed to this method.
  149. * </ol>
  150. *
  151. * If any of the first three steps fail then this method will return
  152. * <tt>false</tt> and all following invocations of this method will return
  153. * <tt>false</tt> immediately. An exception thrown by the handler object's
  154. * <tt>handle</tt> will be caught, and will cause this method to return
  155. * <tt>false</tt>. If the handler's <tt>handle</tt> method is successfully
  156. * invoked, then this method will return <tt>true</tt>. This method will
  157. * never throw any sort of exception.
  158. *
  159. * <p> <i>Note:</i> This method is a temporary hack to work around the
  160. * absence of a real API that provides the ability to replace the
  161. * event-dispatch thread. The magic "sun.awt.exception.handler" property
  162. * <i>will be removed</i> in a future release.
  163. *
  164. * @param thrown The Throwable that was thrown in the event-dispatch
  165. * thread
  166. *
  167. * @returns <tt>false</tt> if any of the above steps failed, otherwise
  168. * <tt>true</tt>.
  169. */
  170. private boolean handleException(Throwable thrown) {
  171. try {
  172. if (handlerClassName == NO_HANDLER) {
  173. return false; /* Already tried, and failed */
  174. }
  175. /* Look up the class name */
  176. if (handlerClassName == null) {
  177. handlerClassName = ((String) AccessController.doPrivileged(
  178. new GetPropertyAction(handlerPropName)));
  179. if (handlerClassName == null) {
  180. handlerClassName = NO_HANDLER; /* Do not try this again */
  181. return false;
  182. }
  183. }
  184. /* Load the class, instantiate it, and find its handle method */
  185. Method m;
  186. Object h;
  187. try {
  188. ClassLoader cl = Thread.currentThread().getContextClassLoader();
  189. Class c = Class.forName(handlerClassName, true, cl);
  190. m = c.getMethod("handle", new Class[] { Throwable.class });
  191. h = c.newInstance();
  192. } catch (Throwable x) {
  193. handlerClassName = NO_HANDLER; /* Do not try this again */
  194. return false;
  195. }
  196. /* Finally, invoke the handler */
  197. m.invoke(h, new Object[] { thrown });
  198. } catch (Throwable x) {
  199. return false;
  200. }
  201. return true;
  202. }
  203. boolean isDispatching(EventQueue eq) {
  204. return theQueue.equals(eq);
  205. }
  206. EventQueue getEventQueue() { return theQueue; }
  207. }