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: }
|