1. /*
  2. * @(#)EventDispatchThread.java 1.46 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.awt;
  8. import java.awt.event.InputEvent;
  9. import java.awt.event.MouseEvent;
  10. import java.awt.event.ActionEvent;
  11. import java.lang.reflect.Method;
  12. import java.security.AccessController;
  13. import sun.security.action.GetPropertyAction;
  14. import sun.awt.DebugHelper;
  15. import sun.awt.AWTAutoShutdown;
  16. import sun.awt.SunToolkit;
  17. import sun.awt.dnd.SunDragSourceContextPeer;
  18. /**
  19. * EventDispatchThread is a package-private AWT class which takes
  20. * events off the EventQueue and dispatches them to the appropriate
  21. * AWT components.
  22. *
  23. * The Thread starts a "permanent" event pump with a call to
  24. * pumpEvents(Conditional) in its run() method. Event handlers can choose to
  25. * block this event pump at any time, but should start a new pump (<b>not</b>
  26. * a new EventDispatchThread) by again calling pumpEvents(Conditional). This
  27. * secondary event pump will exit automatically as soon as the Condtional
  28. * evaluate()s to false and an additional Event is pumped and dispatched.
  29. *
  30. * @author Tom Ball
  31. * @author Amy Fowler
  32. * @author Fred Ecks
  33. * @author David Mendenhall
  34. *
  35. * @version 1.46, 01/23/03
  36. * @since 1.1
  37. */
  38. class EventDispatchThread extends Thread {
  39. private static final DebugHelper dbg = DebugHelper.create(EventDispatchThread.class);
  40. private EventQueue theQueue;
  41. private boolean doDispatch = true;
  42. private static final int ANY_EVENT = -1;
  43. EventDispatchThread(ThreadGroup group, String name, EventQueue queue) {
  44. super(group, name);
  45. theQueue = queue;
  46. }
  47. void stopDispatchingImpl(boolean wait) {
  48. // Note: We stop dispatching via a flag rather than using
  49. // Thread.interrupt() because we can't guarantee that the wait()
  50. // we interrupt will be EventQueue.getNextEvent()'s. -fredx 8-11-98
  51. doDispatch = false;
  52. // wait for the dispatcher to complete
  53. if (Thread.currentThread() != this) {
  54. // fix 4122683, 4128923
  55. // Post an empty event to ensure getNextEvent is unblocked
  56. //
  57. // We have to use postEventPrivate instead of postEvent because
  58. // EventQueue.pop calls EventDispatchThread.stopDispatching.
  59. // Calling SunToolkit.flushPendingEvents in this case could
  60. // lead to deadlock.
  61. theQueue.postEventPrivate(new EmptyEvent());
  62. if (wait) {
  63. try {
  64. join();
  65. } catch(InterruptedException e) {
  66. }
  67. }
  68. }
  69. }
  70. public void stopDispatching() {
  71. stopDispatchingImpl(true);
  72. }
  73. public void stopDispatchingLater() {
  74. stopDispatchingImpl(false);
  75. }
  76. class EmptyEvent extends AWTEvent implements ActiveEvent {
  77. public EmptyEvent() {
  78. super(EventDispatchThread.this,0);
  79. }
  80. public void dispatch() {}
  81. }
  82. public void run() {
  83. try {
  84. pumpEvents(new Conditional() {
  85. public boolean evaluate() {
  86. return true;
  87. }
  88. });
  89. } finally {
  90. /*
  91. * This synchronized block is to secure that the event dispatch
  92. * thread won't die in the middle of posting a new event to the
  93. * associated event queue. It is important because we notify
  94. * that the event dispatch thread is busy after posting a new event
  95. * to its queue, so the EventQueue.dispatchThread reference must
  96. * be valid at that point.
  97. */
  98. synchronized (theQueue) {
  99. theQueue.detachDispatchThread();
  100. /*
  101. * Event dispatch thread dies in case of an uncaught exception.
  102. * A new event dispatch thread for this queue will be started
  103. * only if a new event is posted to it. In case if no more
  104. * events are posted after this thread died all events that
  105. * currently are in the queue will never be dispatched.
  106. */
  107. /*
  108. * Fix for 4648733. Check both the associated java event
  109. * queue and the PostEventQueue.
  110. */
  111. if (theQueue.peekEvent() != null ||
  112. !SunToolkit.isPostEventQueueEmpty()) {
  113. theQueue.initDispatchThread();
  114. }
  115. AWTAutoShutdown.getInstance().notifyThreadFree(this);
  116. }
  117. }
  118. }
  119. void pumpEvents(Conditional cond) {
  120. pumpEvents(ANY_EVENT, cond);
  121. }
  122. void pumpEventsForHierarchy(Conditional cond, Component modalComponent) {
  123. pumpEventsForHierarchy(ANY_EVENT, cond, modalComponent);
  124. }
  125. void pumpEvents(int id, Conditional cond) {
  126. pumpEventsForHierarchy(id, cond, null);
  127. }
  128. void pumpEventsForHierarchy(int id, Conditional cond, Component modalComponent)
  129. {
  130. while (doDispatch && cond.evaluate()) {
  131. if (isInterrupted() || !pumpOneEventForHierarchy(id, modalComponent)) {
  132. doDispatch = false;
  133. }
  134. }
  135. }
  136. boolean pumpOneEventForHierarchy(int id, Component modalComponent) {
  137. try {
  138. AWTEvent event;
  139. boolean eventOK;
  140. do {
  141. event = (id == ANY_EVENT)
  142. ? theQueue.getNextEvent()
  143. : theQueue.getNextEvent(id);
  144. eventOK = true;
  145. if (modalComponent != null) {
  146. /*
  147. * filter out MouseEvent and ActionEvent that's outside
  148. * the modalComponent hierarchy.
  149. * KeyEvent is handled by using enqueueKeyEvent
  150. * in Dialog.show
  151. */
  152. int eventID = event.getID();
  153. if ((eventID >= MouseEvent.MOUSE_FIRST &&
  154. eventID <= MouseEvent.MOUSE_LAST) ||
  155. (eventID >= ActionEvent.ACTION_FIRST &&
  156. eventID <= ActionEvent.ACTION_LAST)) {
  157. Object o = event.getSource();
  158. if (o instanceof Component) {
  159. Component c = (Component) o;
  160. if (modalComponent instanceof Container) {
  161. while (c != modalComponent && c != null) {
  162. c = c.getParent();
  163. }
  164. }
  165. if (c != modalComponent) {
  166. eventOK = false;
  167. }
  168. }
  169. }
  170. }
  171. eventOK = eventOK && SunDragSourceContextPeer.checkEvent(event);
  172. if (!eventOK) {
  173. event.consume();
  174. }
  175. } while (eventOK == false);
  176. if ( dbg.on ) dbg.println("Dispatching: "+event);
  177. theQueue.dispatchEvent(event);
  178. return true;
  179. } catch (ThreadDeath death) {
  180. return false;
  181. } catch (InterruptedException interruptedException) {
  182. return false; // AppContext.dispose() interrupts all
  183. // Threads in the AppContext
  184. // Can get and throw only unchecked exceptions
  185. } catch (RuntimeException e) {
  186. processException(e, modalComponent != null);
  187. } catch (Error e) {
  188. processException(e, modalComponent != null);
  189. }
  190. return true;
  191. }
  192. private void processException(Throwable e, boolean isModal) {
  193. if (!handleException(e)) {
  194. // See bug ID 4499199.
  195. // If we are in a modal dialog, we cannot throw
  196. // an exception for the ThreadGroup to handle (as added
  197. // in RFE 4063022). If we did, the message pump of
  198. // the modal dialog would be interrupted.
  199. // We instead choose to handle the exception ourselves.
  200. // It may be useful to add either a runtime flag or API
  201. // later if someone would like to instead dispose the
  202. // dialog and allow the thread group to handle it.
  203. if (isModal) {
  204. System.err.println(
  205. "Exception occurred during event dispatching:");
  206. e.printStackTrace();
  207. } else if (e instanceof RuntimeException) {
  208. throw (RuntimeException)e;
  209. } else if (e instanceof Error) {
  210. throw (Error)e;
  211. }
  212. }
  213. }
  214. private static final String handlerPropName = "sun.awt.exception.handler";
  215. private static String handlerClassName = null;
  216. private static String NO_HANDLER = new String();
  217. /**
  218. * Handles an exception thrown in the event-dispatch thread.
  219. *
  220. * <p> If the system property "sun.awt.exception.handler" is defined, then
  221. * when this method is invoked it will attempt to do the following:
  222. *
  223. * <ol>
  224. * <li> Load the class named by the value of that property, using the
  225. * current thread's context class loader,
  226. * <li> Instantiate that class using its zero-argument constructor,
  227. * <li> Find the resulting handler object's <tt>public void handle</tt>
  228. * method, which should take a single argument of type
  229. * <tt>Throwable</tt>, and
  230. * <li> Invoke the handler's <tt>handle</tt> method, passing it the
  231. * <tt>thrown</tt> argument that was passed to this method.
  232. * </ol>
  233. *
  234. * If any of the first three steps fail then this method will return
  235. * <tt>false</tt> and all following invocations of this method will return
  236. * <tt>false</tt> immediately. An exception thrown by the handler object's
  237. * <tt>handle</tt> will be caught, and will cause this method to return
  238. * <tt>false</tt>. If the handler's <tt>handle</tt> method is successfully
  239. * invoked, then this method will return <tt>true</tt>. This method will
  240. * never throw any sort of exception.
  241. *
  242. * <p> <i>Note:</i> This method is a temporary hack to work around the
  243. * absence of a real API that provides the ability to replace the
  244. * event-dispatch thread. The magic "sun.awt.exception.handler" property
  245. * <i>will be removed</i> in a future release.
  246. *
  247. * @param thrown The Throwable that was thrown in the event-dispatch
  248. * thread
  249. *
  250. * @return <tt>false</tt> if any of the above steps failed, otherwise
  251. * <tt>true</tt>
  252. */
  253. private boolean handleException(Throwable thrown) {
  254. try {
  255. if (handlerClassName == NO_HANDLER) {
  256. return false; /* Already tried, and failed */
  257. }
  258. /* Look up the class name */
  259. if (handlerClassName == null) {
  260. handlerClassName = ((String) AccessController.doPrivileged(
  261. new GetPropertyAction(handlerPropName)));
  262. if (handlerClassName == null) {
  263. handlerClassName = NO_HANDLER; /* Do not try this again */
  264. return false;
  265. }
  266. }
  267. /* Load the class, instantiate it, and find its handle method */
  268. Method m;
  269. Object h;
  270. try {
  271. ClassLoader cl = Thread.currentThread().getContextClassLoader();
  272. Class c = Class.forName(handlerClassName, true, cl);
  273. m = c.getMethod("handle", new Class[] { Throwable.class });
  274. h = c.newInstance();
  275. } catch (Throwable x) {
  276. handlerClassName = NO_HANDLER; /* Do not try this again */
  277. return false;
  278. }
  279. /* Finally, invoke the handler */
  280. m.invoke(h, new Object[] { thrown });
  281. } catch (Throwable x) {
  282. return false;
  283. }
  284. return true;
  285. }
  286. boolean isDispatching(EventQueue eq) {
  287. return theQueue.equals(eq);
  288. }
  289. EventQueue getEventQueue() { return theQueue; }
  290. }