001 /*
002 * Copyright 1996-2007 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package java.awt;
027
028 import java.awt.event.InputEvent;
029 import java.awt.event.MouseEvent;
030 import java.awt.event.ActionEvent;
031 import java.awt.event.WindowEvent;
032 import java.lang.reflect.Method;
033 import java.security.AccessController;
034 import sun.security.action.GetPropertyAction;
035 import sun.awt.DebugHelper;
036 import sun.awt.AWTAutoShutdown;
037 import sun.awt.SunToolkit;
038
039 import java.util.Vector;
040
041 import sun.awt.dnd.SunDragSourceContextPeer;
042
043 /**
044 * EventDispatchThread is a package-private AWT class which takes
045 * events off the EventQueue and dispatches them to the appropriate
046 * AWT components.
047 *
048 * The Thread starts a "permanent" event pump with a call to
049 * pumpEvents(Conditional) in its run() method. Event handlers can choose to
050 * block this event pump at any time, but should start a new pump (<b>not</b>
051 * a new EventDispatchThread) by again calling pumpEvents(Conditional). This
052 * secondary event pump will exit automatically as soon as the Condtional
053 * evaluate()s to false and an additional Event is pumped and dispatched.
054 *
055 * @author Tom Ball
056 * @author Amy Fowler
057 * @author Fred Ecks
058 * @author David Mendenhall
059 *
060 * @version 1.66, 05/05/07
061 * @since 1.1
062 */
063 class EventDispatchThread extends Thread {
064 private static final DebugHelper dbg = DebugHelper
065 .create(EventDispatchThread.class);
066
067 private EventQueue theQueue;
068 private boolean doDispatch = true;
069 private static final int ANY_EVENT = -1;
070
071 private Vector<EventFilter> eventFilters = new Vector<EventFilter>();
072 // used in handleException
073 private int modalFiltersCount = 0;
074
075 EventDispatchThread(ThreadGroup group, String name, EventQueue queue) {
076 super (group, name);
077 theQueue = queue;
078 }
079
080 void stopDispatchingImpl(boolean wait) {
081 // Note: We stop dispatching via a flag rather than using
082 // Thread.interrupt() because we can't guarantee that the wait()
083 // we interrupt will be EventQueue.getNextEvent()'s. -fredx 8-11-98
084
085 StopDispatchEvent stopEvent = new StopDispatchEvent();
086
087 // wait for the dispatcher to complete
088 if (Thread.currentThread() != this ) {
089
090 // fix 4122683, 4128923
091 // Post an empty event to ensure getNextEvent is unblocked
092 //
093 // We have to use postEventPrivate instead of postEvent because
094 // EventQueue.pop calls EventDispatchThread.stopDispatching.
095 // Calling SunToolkit.flushPendingEvents in this case could
096 // lead to deadlock.
097 theQueue.postEventPrivate(stopEvent);
098
099 if (wait) {
100 try {
101 join();
102 } catch (InterruptedException e) {
103 }
104 }
105 } else {
106 stopEvent.dispatch();
107 }
108 synchronized (theQueue) {
109 if (theQueue.getDispatchThread() == this ) {
110 theQueue.detachDispatchThread();
111 }
112 }
113 }
114
115 public void stopDispatching() {
116 stopDispatchingImpl(true);
117 }
118
119 public void stopDispatchingLater() {
120 stopDispatchingImpl(false);
121 }
122
123 class StopDispatchEvent extends AWTEvent implements ActiveEvent {
124 /*
125 * serialVersionUID
126 */
127 static final long serialVersionUID = -3692158172100730735L;
128
129 public StopDispatchEvent() {
130 super (EventDispatchThread.this , 0);
131 }
132
133 public void dispatch() {
134 doDispatch = false;
135 }
136 }
137
138 public void run() {
139 try {
140 pumpEvents(new Conditional() {
141 public boolean evaluate() {
142 return true;
143 }
144 });
145 } finally {
146 /*
147 * This synchronized block is to secure that the event dispatch
148 * thread won't die in the middle of posting a new event to the
149 * associated event queue. It is important because we notify
150 * that the event dispatch thread is busy after posting a new event
151 * to its queue, so the EventQueue.dispatchThread reference must
152 * be valid at that point.
153 */
154 synchronized (theQueue) {
155 if (theQueue.getDispatchThread() == this ) {
156 theQueue.detachDispatchThread();
157 }
158 /*
159 * Event dispatch thread dies in case of an uncaught exception.
160 * A new event dispatch thread for this queue will be started
161 * only if a new event is posted to it. In case if no more
162 * events are posted after this thread died all events that
163 * currently are in the queue will never be dispatched.
164 */
165 /*
166 * Fix for 4648733. Check both the associated java event
167 * queue and the PostEventQueue.
168 */
169 if (theQueue.peekEvent() != null
170 || !SunToolkit.isPostEventQueueEmpty()) {
171 theQueue.initDispatchThread();
172 }
173 AWTAutoShutdown.getInstance().notifyThreadFree(this );
174 }
175 }
176 }
177
178 void pumpEvents(Conditional cond) {
179 pumpEvents(ANY_EVENT, cond);
180 }
181
182 void pumpEventsForHierarchy(Conditional cond,
183 Component modalComponent) {
184 pumpEventsForHierarchy(ANY_EVENT, cond, modalComponent);
185 }
186
187 void pumpEvents(int id, Conditional cond) {
188 pumpEventsForHierarchy(id, cond, null);
189 }
190
191 void pumpEventsForHierarchy(int id, Conditional cond,
192 Component modalComponent) {
193 pumpEventsForFilter(id, cond, new HierarchyEventFilter(
194 modalComponent));
195 }
196
197 void pumpEventsForFilter(Conditional cond, EventFilter filter) {
198 pumpEventsForFilter(ANY_EVENT, cond, filter);
199 }
200
201 void pumpEventsForFilter(int id, Conditional cond,
202 EventFilter filter) {
203 addEventFilter(filter);
204 while (doDispatch && cond.evaluate()) {
205 if (isInterrupted() || !pumpOneEventForFilters(id)) {
206 doDispatch = false;
207 }
208 }
209 removeEventFilter(filter);
210 }
211
212 void addEventFilter(EventFilter filter) {
213 synchronized (eventFilters) {
214 if (!eventFilters.contains(filter)) {
215 if (filter instanceof ModalEventFilter) {
216 ModalEventFilter newFilter = (ModalEventFilter) filter;
217 int k = 0;
218 for (k = 0; k < eventFilters.size(); k++) {
219 EventFilter f = eventFilters.get(k);
220 if (f instanceof ModalEventFilter) {
221 ModalEventFilter cf = (ModalEventFilter) f;
222 if (cf.compareTo(newFilter) > 0) {
223 break;
224 }
225 }
226 }
227 eventFilters.add(k, filter);
228 modalFiltersCount++;
229 } else {
230 eventFilters.add(filter);
231 }
232 }
233 }
234 }
235
236 void removeEventFilter(EventFilter filter) {
237 synchronized (eventFilters) {
238 if (eventFilters.contains(filter)) {
239 if (filter instanceof ModalEventFilter) {
240 modalFiltersCount--;
241 }
242 eventFilters.remove(filter);
243 }
244 }
245 }
246
247 boolean pumpOneEventForFilters(int id) {
248 try {
249 AWTEvent event;
250 boolean eventOK;
251 do {
252 event = (id == ANY_EVENT) ? theQueue.getNextEvent()
253 : theQueue.getNextEvent(id);
254
255 eventOK = true;
256 synchronized (eventFilters) {
257 for (int i = eventFilters.size() - 1; i >= 0; i--) {
258 EventFilter f = eventFilters.get(i);
259 EventFilter.FilterAction accept = f
260 .acceptEvent(event);
261 if (accept == EventFilter.FilterAction.REJECT) {
262 eventOK = false;
263 break;
264 } else if (accept == EventFilter.FilterAction.ACCEPT_IMMEDIATELY) {
265 break;
266 }
267 }
268 }
269 eventOK = eventOK
270 && SunDragSourceContextPeer.checkEvent(event);
271 if (!eventOK) {
272 event.consume();
273 }
274 } while (eventOK == false);
275
276 if (dbg.on) {
277 dbg.println("Dispatching: " + event);
278 }
279
280 theQueue.dispatchEvent(event);
281 return true;
282 } catch (ThreadDeath death) {
283 return false;
284
285 } catch (InterruptedException interruptedException) {
286 return false; // AppContext.dispose() interrupts all
287 // Threads in the AppContext
288
289 }
290 // Can get and throw only unchecked exceptions
291 catch (RuntimeException e) {
292 processException(e, modalFiltersCount > 0);
293 } catch (Error e) {
294 processException(e, modalFiltersCount > 0);
295 }
296 return true;
297 }
298
299 private void processException(Throwable e, boolean isModal) {
300 if (!handleException(e)) {
301 // See bug ID 4499199.
302 // If we are in a modal dialog, we cannot throw
303 // an exception for the ThreadGroup to handle (as added
304 // in RFE 4063022). If we did, the message pump of
305 // the modal dialog would be interrupted.
306 // We instead choose to handle the exception ourselves.
307 // It may be useful to add either a runtime flag or API
308 // later if someone would like to instead dispose the
309 // dialog and allow the thread group to handle it.
310 if (isModal) {
311 System.err
312 .println("Exception occurred during event dispatching:");
313 e.printStackTrace();
314 } else if (e instanceof RuntimeException) {
315 throw (RuntimeException) e;
316 } else if (e instanceof Error) {
317 throw (Error) e;
318 }
319 }
320 }
321
322 private static final String handlerPropName = "sun.awt.exception.handler";
323 private static String handlerClassName = null;
324 private static String NO_HANDLER = new String();
325
326 /**
327 * Handles an exception thrown in the event-dispatch thread.
328 *
329 * <p> If the system property "sun.awt.exception.handler" is defined, then
330 * when this method is invoked it will attempt to do the following:
331 *
332 * <ol>
333 * <li> Load the class named by the value of that property, using the
334 * current thread's context class loader,
335 * <li> Instantiate that class using its zero-argument constructor,
336 * <li> Find the resulting handler object's <tt>public void handle</tt>
337 * method, which should take a single argument of type
338 * <tt>Throwable</tt>, and
339 * <li> Invoke the handler's <tt>handle</tt> method, passing it the
340 * <tt>thrown</tt> argument that was passed to this method.
341 * </ol>
342 *
343 * If any of the first three steps fail then this method will return
344 * <tt>false</tt> and all following invocations of this method will return
345 * <tt>false</tt> immediately. An exception thrown by the handler object's
346 * <tt>handle</tt> will be caught, and will cause this method to return
347 * <tt>false</tt>. If the handler's <tt>handle</tt> method is successfully
348 * invoked, then this method will return <tt>true</tt>. This method will
349 * never throw any sort of exception.
350 *
351 * <p> <i>Note:</i> This method is a temporary hack to work around the
352 * absence of a real API that provides the ability to replace the
353 * event-dispatch thread. The magic "sun.awt.exception.handler" property
354 * <i>will be removed</i> in a future release.
355 *
356 * @param thrown The Throwable that was thrown in the event-dispatch
357 * thread
358 *
359 * @return <tt>false</tt> if any of the above steps failed, otherwise
360 * <tt>true</tt>
361 */
362 private boolean handleException(Throwable thrown) {
363
364 try {
365
366 if (handlerClassName == NO_HANDLER) {
367 return false; /* Already tried, and failed */
368 }
369
370 /* Look up the class name */
371 if (handlerClassName == null) {
372 handlerClassName = ((String) AccessController
373 .doPrivileged(new GetPropertyAction(
374 handlerPropName)));
375 if (handlerClassName == null) {
376 handlerClassName = NO_HANDLER; /* Do not try this again */
377 return false;
378 }
379 }
380
381 /* Load the class, instantiate it, and find its handle method */
382 Method m;
383 Object h;
384 try {
385 ClassLoader cl = Thread.currentThread()
386 .getContextClassLoader();
387 Class c = Class.forName(handlerClassName, true, cl);
388 m = c.getMethod("handle",
389 new Class[] { Throwable.class });
390 h = c.newInstance();
391 } catch (Throwable x) {
392 handlerClassName = NO_HANDLER; /* Do not try this again */
393 return false;
394 }
395
396 /* Finally, invoke the handler */
397 m.invoke(h, new Object[] { thrown });
398
399 } catch (Throwable x) {
400 return false;
401 }
402
403 return true;
404 }
405
406 boolean isDispatching(EventQueue eq) {
407 return theQueue.equals(eq);
408 }
409
410 EventQueue getEventQueue() {
411 return theQueue;
412 }
413
414 private static class HierarchyEventFilter implements EventFilter {
415 private Component modalComponent;
416
417 public HierarchyEventFilter(Component modalComponent) {
418 this .modalComponent = modalComponent;
419 }
420
421 public FilterAction acceptEvent(AWTEvent event) {
422 if (modalComponent != null) {
423 int eventID = event.getID();
424 boolean mouseEvent = (eventID >= MouseEvent.MOUSE_FIRST)
425 && (eventID <= MouseEvent.MOUSE_LAST);
426 boolean actionEvent = (eventID >= ActionEvent.ACTION_FIRST)
427 && (eventID <= ActionEvent.ACTION_LAST);
428 boolean windowClosingEvent = (eventID == WindowEvent.WINDOW_CLOSING);
429 /*
430 * filter out MouseEvent and ActionEvent that's outside
431 * the modalComponent hierarchy.
432 * KeyEvent is handled by using enqueueKeyEvent
433 * in Dialog.show
434 */
435 if (Component.isInstanceOf(modalComponent,
436 "javax.swing.JInternalFrame")) {
437 /*
438 * Modal internal frames are handled separately. If event is
439 * for some component from another heavyweight than modalComp,
440 * it is accepted. If heavyweight is the same - we still accept
441 * event and perform further filtering in LightweightDispatcher
442 */
443 return windowClosingEvent ? FilterAction.REJECT
444 : FilterAction.ACCEPT;
445 }
446 if (mouseEvent || actionEvent || windowClosingEvent) {
447 Object o = event.getSource();
448 if (o instanceof sun.awt.ModalExclude) {
449 // Exclude this object from modality and
450 // continue to pump it's events.
451 return FilterAction.ACCEPT;
452 } else if (o instanceof Component) {
453 Component c = (Component) o;
454 // 5.0u3 modal exclusion
455 boolean modalExcluded = false;
456 if (modalComponent instanceof Container) {
457 while (c != modalComponent && c != null) {
458 if ((c instanceof Window)
459 && (sun.awt.SunToolkit
460 .isModalExcluded((Window) c))) {
461 // Exclude this window and all its children from
462 // modality and continue to pump it's events.
463 modalExcluded = true;
464 break;
465 }
466 c = c.getParent();
467 }
468 }
469 if (!modalExcluded && (c != modalComponent)) {
470 return FilterAction.REJECT;
471 }
472 }
473 }
474 }
475 return FilterAction.ACCEPT;
476 }
477 }
478 }
|