1 /* 2 * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.awt; 27 28 import java.awt.event.InputEvent; 29 import java.awt.event.MouseEvent; 30 import java.awt.event.ActionEvent; 31 import java.awt.event.WindowEvent; 32 import java.lang.reflect.Method; 33 import java.security.AccessController; 34 import sun.security.action.GetPropertyAction; 35 import sun.awt.AWTAutoShutdown; 36 import sun.awt.SunToolkit; 37 38 import java.util.Vector; 39 import java.util.logging.*; 40 41 import sun.awt.dnd.SunDragSourceContextPeer; 42 43 /** 44 * EventDispatchThread is a package-private AWT class which takes 45 * events off the EventQueue and dispatches them to the appropriate 46 * AWT components. 47 * 48 * The Thread starts a "permanent" event pump with a call to 49 * pumpEvents(Conditional) in its run() method. Event handlers can choose to 50 * block this event pump at any time, but should start a new pump (<b>not</b> 51 * a new EventDispatchThread) by again calling pumpEvents(Conditional). This 52 * secondary event pump will exit automatically as soon as the Condtional 53 * evaluate()s to false and an additional Event is pumped and dispatched. 54 * 55 * @author Tom Ball 56 * @author Amy Fowler 57 * @author Fred Ecks 58 * @author David Mendenhall 59 * 60 * @since 1.1 61 */ 62 class EventDispatchThread extends Thread { 63 private static final Logger eventLog = Logger.getLogger("java.awt.event.EventDispatchThread"); 64 65 private EventQueue theQueue; 66 private boolean doDispatch = true; 67 private static final int ANY_EVENT = -1; 68 69 private Vector<EventFilter> eventFilters = new Vector<EventFilter>(); 70 // used in handleException 71 private int modalFiltersCount = 0; 72 73 EventDispatchThread(ThreadGroup group, String name, EventQueue queue) { 74 super(group, name); 75 theQueue = queue; 76 } 77 78 void stopDispatchingImpl(boolean wait) { 79 // Note: We stop dispatching via a flag rather than using 80 // Thread.interrupt() because we can't guarantee that the wait() 81 // we interrupt will be EventQueue.getNextEvent()'s. -fredx 8-11-98 82 83 StopDispatchEvent stopEvent = new StopDispatchEvent(); 84 85 // wait for the dispatcher to complete 86 if (Thread.currentThread() != this) { 87 88 // fix 4122683, 4128923 89 // Post an empty event to ensure getNextEvent is unblocked 90 // 91 // We have to use postEventPrivate instead of postEvent because 92 // EventQueue.pop calls EventDispatchThread.stopDispatching. 93 // Calling SunToolkit.flushPendingEvents in this case could 94 // lead to deadlock. 95 theQueue.postEventPrivate(stopEvent); 96 97 if (wait) { 98 try { 99 join(); 100 } catch(InterruptedException e) { 101 } 102 } 103 } else { 104 stopEvent.dispatch(); 105 } 106 synchronized (theQueue) { 107 if (theQueue.getDispatchThread() == this) { 108 theQueue.detachDispatchThread(); 109 } 110 } 111 } 112 113 public void stopDispatching() { 114 stopDispatchingImpl(true); 115 } 116 117 public void stopDispatchingLater() { 118 stopDispatchingImpl(false); 119 } 120 121 class StopDispatchEvent extends AWTEvent implements ActiveEvent { 122 /* 123 * serialVersionUID 124 */ 125 static final long serialVersionUID = -3692158172100730735L; 126 127 public StopDispatchEvent() { 128 super(EventDispatchThread.this,0); 129 } 130 131 public void dispatch() { 132 doDispatch = false; 133 } 134 } 135 136 public void run() { 137 try { 138 pumpEvents(new Conditional() { 139 public boolean evaluate() { 140 return true; 141 } 142 }); 143 } finally { 144 /* 145 * This synchronized block is to secure that the event dispatch 146 * thread won't die in the middle of posting a new event to the 147 * associated event queue. It is important because we notify 148 * that the event dispatch thread is busy after posting a new event 149 * to its queue, so the EventQueue.dispatchThread reference must 150 * be valid at that point. 151 */ 152 synchronized (theQueue) { 153 if (theQueue.getDispatchThread() == this) { 154 theQueue.detachDispatchThread(); 155 } 156 /* 157 * Event dispatch thread dies in case of an uncaught exception. 158 * A new event dispatch thread for this queue will be started 159 * only if a new event is posted to it. In case if no more 160 * events are posted after this thread died all events that 161 * currently are in the queue will never be dispatched. 162 */ 163 /* 164 * Fix for 4648733. Check both the associated java event 165 * queue and the PostEventQueue. 166 */ 167 if (theQueue.peekEvent() != null || 168 !SunToolkit.isPostEventQueueEmpty()) { 169 theQueue.initDispatchThread(); 170 } 171 AWTAutoShutdown.getInstance().notifyThreadFree(this); 172 } 173 } 174 } 175 176 void pumpEvents(Conditional cond) { 177 pumpEvents(ANY_EVENT, cond); 178 } 179 180 void pumpEventsForHierarchy(Conditional cond, Component modalComponent) { 181 pumpEventsForHierarchy(ANY_EVENT, cond, modalComponent); 182 } 183 184 void pumpEvents(int id, Conditional cond) { 185 pumpEventsForHierarchy(id, cond, null); 186 } 187 188 void pumpEventsForHierarchy(int id, Conditional cond, Component modalComponent) 189 { 190 pumpEventsForFilter(id, cond, new HierarchyEventFilter(modalComponent)); 191 } 192 193 void pumpEventsForFilter(Conditional cond, EventFilter filter) { 194 pumpEventsForFilter(ANY_EVENT, cond, filter); 195 } 196 197 void pumpEventsForFilter(int id, Conditional cond, EventFilter filter) { 198 addEventFilter(filter); 199 while (doDispatch && cond.evaluate()) { 200 if (isInterrupted() || !pumpOneEventForFilters(id)) { 201 doDispatch = false; 202 } 203 } 204 removeEventFilter(filter); 205 } 206 207 void addEventFilter(EventFilter filter) { 208 synchronized (eventFilters) { 209 if (!eventFilters.contains(filter)) { 210 if (filter instanceof ModalEventFilter) { 211 ModalEventFilter newFilter = (ModalEventFilter)filter; 212 int k = 0; 213 for (k = 0; k < eventFilters.size(); k++) { 214 EventFilter f = eventFilters.get(k); 215 if (f instanceof ModalEventFilter) { 216 ModalEventFilter cf = (ModalEventFilter)f; 217 if (cf.compareTo(newFilter) > 0) { 218 break; 219 } 220 } 221 } 222 eventFilters.add(k, filter); 223 modalFiltersCount++; 224 } else { 225 eventFilters.add(filter); 226 } 227 } 228 } 229 } 230 231 void removeEventFilter(EventFilter filter) { 232 synchronized (eventFilters) { 233 if (eventFilters.contains(filter)) { 234 if (filter instanceof ModalEventFilter) { 235 modalFiltersCount--; 236 } 237 eventFilters.remove(filter); 238 } 239 } 240 } 241 242 boolean pumpOneEventForFilters(int id) { 243 try { 244 AWTEvent event; 245 boolean eventOK; 246 do { 247 event = (id == ANY_EVENT) 248 ? theQueue.getNextEvent() 249 : theQueue.getNextEvent(id); 250 251 eventOK = true; 252 synchronized (eventFilters) { 253 for (int i = eventFilters.size() - 1; i >= 0; i--) { 254 EventFilter f = eventFilters.get(i); 255 EventFilter.FilterAction accept = f.acceptEvent(event); 256 if (accept == EventFilter.FilterAction.REJECT) { 257 eventOK = false; 258 break; 259 } else if (accept == EventFilter.FilterAction.ACCEPT_IMMEDIATELY) { 260 break; 261 } 262 } 263 } 264 eventOK = eventOK && SunDragSourceContextPeer.checkEvent(event); 265 if (!eventOK) { 266 event.consume(); 267 } 268 } 269 while (eventOK == false); 270 271 if (eventLog.isLoggable(Level.FINEST)) { 272 eventLog.log(Level.FINEST, "Dispatching: " + event); 273 } 274 275 theQueue.dispatchEvent(event); 276 return true; 277 } 278 catch (ThreadDeath death) { 279 return false; 280 281 } 282 catch (InterruptedException interruptedException) { 283 return false; // AppContext.dispose() interrupts all 284 // Threads in the AppContext 285 286 } 287 catch (Throwable e) { 288 processException(e, modalFiltersCount > 0); 289 } 290 return true; 291 } 292 293 private void processException(Throwable e, boolean isModal) { 294 if (eventLog.isLoggable(Level.FINE)) { 295 eventLog.log(Level.FINE, "Processing exception: " + e + 296 ", isModal = " + isModal); 297 } 298 if (!handleException(e)) { 299 // See bug ID 4499199. 300 // If we are in a modal dialog, we cannot throw 301 // an exception for the ThreadGroup to handle (as added 302 // in RFE 4063022). If we did, the message pump of 303 // the modal dialog would be interrupted. 304 // We instead choose to handle the exception ourselves. 305 // It may be useful to add either a runtime flag or API 306 // later if someone would like to instead dispose the 307 // dialog and allow the thread group to handle it. 308 if (isModal) { 309 System.err.println( 310 "Exception occurred during event dispatching:"); 311 e.printStackTrace(); 312 } else if (e instanceof RuntimeException) { 313 throw (RuntimeException)e; 314 } else if (e instanceof Error) { 315 throw (Error)e; 316 } 317 } 318 } 319 320 private static final String handlerPropName = "sun.awt.exception.handler"; 321 private static String handlerClassName = null; 322 private static String NO_HANDLER = new String(); 323 324 /** 325 * Handles an exception thrown in the event-dispatch thread. 326 * 327 * <p> If the system property "sun.awt.exception.handler" is defined, then 328 * when this method is invoked it will attempt to do the following: 329 * 330 * <ol> 331 * <li> Load the class named by the value of that property, using the 332 * current thread's context class loader, 333 * <li> Instantiate that class using its zero-argument constructor, 334 * <li> Find the resulting handler object's <tt>public void handle</tt> 335 * method, which should take a single argument of type 336 * <tt>Throwable</tt>, and 337 * <li> Invoke the handler's <tt>handle</tt> method, passing it the 338 * <tt>thrown</tt> argument that was passed to this method. 339 * </ol> 340 * 341 * If any of the first three steps fail then this method will return 342 * <tt>false</tt> and all following invocations of this method will return 343 * <tt>false</tt> immediately. An exception thrown by the handler object's 344 * <tt>handle</tt> will be caught, and will cause this method to return 345 * <tt>false</tt>. If the handler's <tt>handle</tt> method is successfully 346 * invoked, then this method will return <tt>true</tt>. This method will 347 * never throw any sort of exception. 348 * 349 * <p> <i>Note:</i> This method is a temporary hack to work around the 350 * absence of a real API that provides the ability to replace the 351 * event-dispatch thread. The magic "sun.awt.exception.handler" property 352 * <i>will be removed</i> in a future release. 353 * 354 * @param thrown The Throwable that was thrown in the event-dispatch 355 * thread 356 * 357 * @return <tt>false</tt> if any of the above steps failed, otherwise 358 * <tt>true</tt> 359 */ 360 private boolean handleException(Throwable thrown) { 361 362 try { 363 364 if (handlerClassName == NO_HANDLER) { 365 return false; /* Already tried, and failed */ 366 } 367 368 /* Look up the class name */ 369 if (handlerClassName == null) { 370 handlerClassName = ((String) AccessController.doPrivileged( 371 new GetPropertyAction(handlerPropName))); 372 if (handlerClassName == null) { 373 handlerClassName = NO_HANDLER; /* Do not try this again */ 374 return false; 375 } 376 } 377 378 /* Load the class, instantiate it, and find its handle method */ 379 Method m; 380 Object h; 381 try { 382 ClassLoader cl = Thread.currentThread().getContextClassLoader(); 383 Class c = Class.forName(handlerClassName, true, cl); 384 m = c.getMethod("handle", new Class[] { Throwable.class }); 385 h = c.newInstance(); 386 } catch (Throwable x) { 387 handlerClassName = NO_HANDLER; /* Do not try this again */ 388 return false; 389 } 390 391 /* Finally, invoke the handler */ 392 m.invoke(h, new Object[] { thrown }); 393 394 } catch (Throwable x) { 395 return false; 396 } 397 398 return true; 399 } 400 401 boolean isDispatching(EventQueue eq) { 402 return theQueue.equals(eq); 403 } 404 405 EventQueue getEventQueue() { return theQueue; } 406 407 private static class HierarchyEventFilter implements EventFilter { 408 private Component modalComponent; 409 public HierarchyEventFilter(Component modalComponent) { 410 this.modalComponent = modalComponent; 411 } 412 public FilterAction acceptEvent(AWTEvent event) { 413 if (modalComponent != null) { 414 int eventID = event.getID(); 415 boolean mouseEvent = (eventID >= MouseEvent.MOUSE_FIRST) && 416 (eventID <= MouseEvent.MOUSE_LAST); 417 boolean actionEvent = (eventID >= ActionEvent.ACTION_FIRST) && 418 (eventID <= ActionEvent.ACTION_LAST); 419 boolean windowClosingEvent = (eventID == WindowEvent.WINDOW_CLOSING); 420 /* 421 * filter out MouseEvent and ActionEvent that's outside 422 * the modalComponent hierarchy. 423 * KeyEvent is handled by using enqueueKeyEvent 424 * in Dialog.show 425 */ 426 if (Component.isInstanceOf(modalComponent, "javax.swing.JInternalFrame")) { 427 /* 428 * Modal internal frames are handled separately. If event is 429 * for some component from another heavyweight than modalComp, 430 * it is accepted. If heavyweight is the same - we still accept 431 * event and perform further filtering in LightweightDispatcher 432 */ 433 return windowClosingEvent ? FilterAction.REJECT : FilterAction.ACCEPT; 434 } 435 if (mouseEvent || actionEvent || windowClosingEvent) { 436 Object o = event.getSource(); 437 if (o instanceof sun.awt.ModalExclude) { 438 // Exclude this object from modality and 439 // continue to pump it's events. 440 return FilterAction.ACCEPT; 441 } else if (o instanceof Component) { 442 Component c = (Component) o; 443 // 5.0u3 modal exclusion 444 boolean modalExcluded = false; 445 if (modalComponent instanceof Container) { 446 while (c != modalComponent && c != null) { 447 if ((c instanceof Window) && 448 (sun.awt.SunToolkit.isModalExcluded((Window)c))) { 449 // Exclude this window and all its children from 450 // modality and continue to pump it's events. 451 modalExcluded = true; 452 break; 453 } 454 c = c.getParent(); 455 } 456 } 457 if (!modalExcluded && (c != modalComponent)) { 458 return FilterAction.REJECT; 459 } 460 } 461 } 462 } 463 return FilterAction.ACCEPT; 464 } 465 } 466 }