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