KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > foxtrot > pumps > ConditionalEventPump


1 /**
2  * Copyright (c) 2002-2005, Simone Bordet
3  * All rights reserved.
4  *
5  * This software is distributable under the BSD license.
6  * See the terms of the BSD license in the documentation provided with this software.
7  */

8
9 package foxtrot.pumps;
10
11 import java.lang.reflect.Method JavaDoc;
12 import java.lang.reflect.InvocationHandler JavaDoc;
13 import java.lang.reflect.Proxy JavaDoc;
14 import java.lang.reflect.InvocationTargetException JavaDoc;
15 import java.security.AccessController JavaDoc;
16 import java.security.PrivilegedExceptionAction JavaDoc;
17 import java.security.PrivilegedAction JavaDoc;
18 import java.awt.AWTEvent JavaDoc;
19 import java.awt.EventQueue JavaDoc;
20 import java.awt.Toolkit JavaDoc;
21
22 import foxtrot.EventPump;
23 import foxtrot.Task;
24
25 /**
26  * This implementation of EventPump calls the package protected method
27  * <code>java.awt.EventDispatchThread.pumpEvents(Conditional)</code>
28  * to pump events while a Task is executed.
29  * @version $Revision: 1.1 $
30  */

31 public class ConditionalEventPump implements EventPump, EventFilterable
32 {
33    private static Class JavaDoc conditionalClass;
34    private static Method JavaDoc pumpEventsMethod;
35    static final boolean debug = false;
36
37    static
38    {
39       try
40       {
41          AccessController.doPrivileged(new PrivilegedExceptionAction JavaDoc()
42          {
43             public Object JavaDoc run() throws ClassNotFoundException JavaDoc, NoSuchMethodException JavaDoc
44             {
45                ClassLoader JavaDoc loader = ClassLoader.getSystemClassLoader();
46                conditionalClass = loader.loadClass("java.awt.Conditional");
47                Class JavaDoc dispatchThreadClass = loader.loadClass("java.awt.EventDispatchThread");
48                pumpEventsMethod = dispatchThreadClass.getDeclaredMethod("pumpEvents", new Class JavaDoc[]{conditionalClass});
49                pumpEventsMethod.setAccessible(true);
50
51                // See remarks for use of this property in java.awt.EventDispatchThread
52
String JavaDoc property = "sun.awt.exception.handler";
53                String JavaDoc handler = System.getProperty(property);
54                if (handler == null)
55                {
56                   handler = ThrowableHandler.class.getName();
57                   System.setProperty(property, handler);
58                   if (debug) System.out.println("[ConditionalEventPump] Installing AWT Throwable Handler " + handler);
59                }
60                else
61                {
62                   if (debug) System.out.println("[ConditionalEventPump] Using already installed AWT Throwable Handler " + handler);
63                }
64                return null;
65             }
66          });
67       }
68       catch (Throwable JavaDoc x)
69       {
70          if (debug) x.printStackTrace();
71          throw new Error JavaDoc(x.toString());
72       }
73    }
74
75    private EventFilter filter;
76
77    public void setEventFilter(EventFilter filter)
78    {
79       this.filter = filter;
80    }
81
82    public EventFilter getEventFilter()
83    {
84       return filter;
85    }
86
87    public void pumpEvents(Task task)
88    {
89       // A null task may be passed for initialization of this class.
90
if (task == null) return;
91
92       try
93       {
94          if (debug) System.out.println("[ConditionalEventPump] Start pumping events - Pump is " + this + " - Task is " + task);
95
96          // Invoke java.awt.EventDispatchThread.pumpEvents(new Conditional(task));
97
Object JavaDoc conditional = Proxy.newProxyInstance(conditionalClass.getClassLoader(), new Class JavaDoc[]{conditionalClass}, new Conditional(task));
98          pumpEventsMethod.invoke(Thread.currentThread(), new Object JavaDoc[]{conditional});
99       }
100       catch (InvocationTargetException JavaDoc x)
101       {
102          // No exceptions should escape from java.awt.EventDispatchThread.pumpEvents(Conditional)
103
// since we installed a throwable handler. But one provided by the user may fail.
104
Throwable JavaDoc t = x.getTargetException();
105          System.err.println("[ConditionalEventPump] Exception occurred during event dispatching:");
106          t.printStackTrace();
107
108          // Rethrow. This will exit from Worker.post with a runtime exception or an error, and
109
// the original event pump will take care of it.
110
if (t instanceof RuntimeException JavaDoc)
111             throw (RuntimeException JavaDoc)t;
112          else
113             throw (Error JavaDoc)t;
114       }
115       catch (Throwable JavaDoc x)
116       {
117          // Here we have an compiler bug
118
System.err.println("[ConditionalEventPump] PANIC: uncaught exception in Foxtrot code");
119          x.printStackTrace();
120       }
121       finally
122       {
123          // We're not done. Because of bug #4531693 (see Conditional) pumpEvents() may have returned
124
// immediately, but the Task is not completed. Same may happen in case of buggy exception handler.
125
// Here wait until the Task is completed. Side effect is freeze of the GUI.
126
waitForTask(task);
127
128          if (debug) System.out.println("[ConditionalEventPump] Stop pumping events - Pump is " + this + " - Task is " + task);
129       }
130    }
131
132    private void waitForTask(Task task)
133    {
134       try
135       {
136          synchronized (task)
137          {
138             while (!task.isCompleted())
139             {
140                if (debug) System.out.println("[ConditionalEventPump] Waiting for Task " + task + " to complete (GUI freeze)");
141                task.wait();
142             }
143             if (debug) System.out.println("[ConditionalEventPump] Task " + task + " is completed");
144          }
145       }
146       catch (InterruptedException JavaDoc x)
147       {
148          // Someone interrupted the Event Dispatch Thread, re-interrupt
149
Thread.currentThread().interrupt();
150       }
151    }
152
153    /**
154     * This method is called before an event is got from the EventQueue and dispatched,
155     * to see if pumping of events should continue or not.
156     * Returns true to indicate that pumping should continue, false to indicate that pumping
157     * should stop.
158     */

159    private Boolean JavaDoc pumpEvent(Task task)
160    {
161       Boolean JavaDoc completed = task.isCompleted() ? Boolean.TRUE : Boolean.FALSE;
162       // Task already completed, return false to indicate to stop pumping events
163
if (completed.booleanValue()) return Boolean.FALSE;
164
165       while (true)
166       {
167          // The task is still running, we should pump events
168
AWTEvent JavaDoc nextEvent = waitForEvent();
169          if (nextEvent == null) return Boolean.FALSE;
170          if (debug) System.out.println("[ConditionalEventPump] Next Event: " + nextEvent);
171
172          // If this event cannot be pumped, we interrupt immediately event pumping
173
// The GUI will freeze, but we still wait for the Task to finish (see pumpEvents())
174
if (!canPumpEvent(nextEvent)) return Boolean.FALSE;
175
176          // Plug the event filtering mechanism
177
if (filter == null || filter.accept(nextEvent)) return Boolean.TRUE;
178
179          // The event has been filtered out, pop it from the EventQueue
180
// then wait again for the next event
181
nextEvent = getNextEvent();
182          if (nextEvent == null) return Boolean.FALSE;
183          if (debug) System.out.println("[ConditionalEventPump] Filtered out AWT Event: " + nextEvent + " by filter " + filter);
184       }
185    }
186
187    /**
188     * Returns whether this event can be pumped from the EventQueue.
189     * JDK 1.4 introduced SequencedEvent, which is an event holding a list SequencedEvents
190     * that should be dispatched in order.
191     * Bug #4531693 was caused by the fact that the first SequencedEvent of a list, when
192     * dispatched, might end up calling an event listener that displayed a dialog (or called Foxtrot,
193     * that uses the same event pumping mechanism); the new event pump might try to dispatch the
194     * SequencedEvent second in the list (while the first wasn't completely dispatched yet),
195     * causing the application to hang.
196     * Bug #4531693 has been fixed in JDK 1.4.2, and backported to 1.4.1, so there is no longer
197     * need to check for this situation, unless using JDK 1.4.0 or non-fixed versions of JDK 1.4.1.
198     */

199    protected boolean canPumpEvent(AWTEvent JavaDoc event)
200    {
201       return true;
202    }
203
204    private EventQueue JavaDoc getEventQueue()
205    {
206       return (EventQueue JavaDoc)AccessController.doPrivileged(new PrivilegedAction JavaDoc()
207       {
208          public Object JavaDoc run()
209          {
210             return Toolkit.getDefaultToolkit().getSystemEventQueue();
211          }
212       });
213    }
214
215    private AWTEvent JavaDoc getNextEvent()
216    {
217       try
218       {
219          return getEventQueue().getNextEvent();
220       }
221       catch (InterruptedException JavaDoc x)
222       {
223          Thread.currentThread().interrupt();
224          return null;
225       }
226    }
227
228    /**
229     * Waits until an event is available on the EventQueue.
230     * This method uses the same synchronization mechanisms used by EventQueue to be notified when
231     * an event is posted on the EventQueue.
232     * Waiting for events is necessary in this case: a Task is posted and we would like to start
233     * pumping, but no events have been posted yet ({@link #peekEvent} returns null).
234     */

235    private AWTEvent JavaDoc waitForEvent()
236    {
237       EventQueue JavaDoc queue = getEventQueue();
238       AWTEvent JavaDoc nextEvent = null;
239       synchronized (queue)
240       {
241          while ((nextEvent = peekEvent(queue)) == null)
242          {
243             if (debug) System.out.println("[ConditionalEventPump] Waiting for events...");
244             try
245             {
246                queue.wait();
247             }
248             catch (InterruptedException JavaDoc x)
249             {
250                Thread.currentThread().interrupt();
251                return null;
252             }
253          }
254       }
255       return nextEvent;
256    }
257
258    /**
259     * Peeks the EventQueue for the next event, without removing it.
260     */

261    protected AWTEvent JavaDoc peekEvent(EventQueue JavaDoc queue)
262    {
263       return queue.peekEvent();
264    }
265
266    /**
267     * Implements the <code>java.awt.Conditional</code> interface,
268     * that is package private, with a JDK 1.3+ dynamic proxy.
269     */

270    private class Conditional implements InvocationHandler JavaDoc
271    {
272       private final Task task;
273
274       /**
275        * Creates a new invocation handler for the given task.
276        */

277       private Conditional(Task task)
278       {
279          this.task = task;
280       }
281
282       /**
283        * Implements method <code>java.awt.Conditional.evaluate()</code>
284        */

285       public Object JavaDoc invoke(Object JavaDoc proxy, Method JavaDoc method, Object JavaDoc[] args) throws Throwable JavaDoc
286       {
287          return pumpEvent(task);
288       }
289    }
290
291    /**
292     * Handler for RuntimeExceptions or Errors thrown during dispatching of AWT events. <br />
293     * The name of this class is used as a value of the property <code>sun.awt.exception.handler</code>,
294     * and the AWT event dispatch mechanism calls it when an unexpected runtime exception or error
295     * is thrown during event dispatching. If the user specifies a different exception handler,
296     * this one will not be used, and the user's one is used instead.
297     * Use of this class is necessary in JDK 1.4, since RuntimeExceptions and Errors are propagated to
298     * be handled by the ThreadGroup (but not for modal dialogs).
299     */

300    public static class ThrowableHandler
301    {
302       /**
303        * The callback method invoked by the AWT event dispatch mechanism when an unexpected
304        * exception or error is thrown during event dispatching. <br>
305        * It just logs the exception.
306        */

307       public void handle(Throwable JavaDoc t)
308       {
309          System.err.println("[ConditionalEventPump] Exception occurred during event dispatching:");
310          t.printStackTrace();
311       }
312    }
313 }
314
Popular Tags