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 }