View Javadoc
1   /*
2    * Copyright (c) 1998, 2013, 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.event;
27  
28  import sun.awt.AWTAccessor;
29  
30  import java.awt.ActiveEvent;
31  import java.awt.AWTEvent;
32  
33  /**
34   * An event which executes the <code>run()</code> method on a <code>Runnable
35   * </code> when dispatched by the AWT event dispatcher thread. This class can
36   * be used as a reference implementation of <code>ActiveEvent</code> rather
37   * than declaring a new class and defining <code>dispatch()</code>.<p>
38   *
39   * Instances of this class are placed on the <code>EventQueue</code> by calls
40   * to <code>invokeLater</code> and <code>invokeAndWait</code>. Client code
41   * can use this fact to write replacement functions for <code>invokeLater
42   * </code> and <code>invokeAndWait</code> without writing special-case code
43   * in any <code>AWTEventListener</code> objects.
44   * <p>
45   * An unspecified behavior will be caused if the {@code id} parameter
46   * of any particular {@code InvocationEvent} instance is not
47   * in the range from {@code INVOCATION_FIRST} to {@code INVOCATION_LAST}.
48   *
49   * @author      Fred Ecks
50   * @author      David Mendenhall
51   *
52   * @see         java.awt.ActiveEvent
53   * @see         java.awt.EventQueue#invokeLater
54   * @see         java.awt.EventQueue#invokeAndWait
55   * @see         AWTEventListener
56   *
57   * @since       1.2
58   */
59  public class InvocationEvent extends AWTEvent implements ActiveEvent {
60  
61      static {
62          AWTAccessor.setInvocationEventAccessor(new AWTAccessor.InvocationEventAccessor() {
63              @Override
64              public void dispose(InvocationEvent invocationEvent) {
65                  invocationEvent.finishedDispatching(false);
66              }
67          });
68      }
69  
70      /**
71       * Marks the first integer id for the range of invocation event ids.
72       */
73      public static final int INVOCATION_FIRST = 1200;
74  
75      /**
76       * The default id for all InvocationEvents.
77       */
78      public static final int INVOCATION_DEFAULT = INVOCATION_FIRST;
79  
80      /**
81       * Marks the last integer id for the range of invocation event ids.
82       */
83      public static final int INVOCATION_LAST = INVOCATION_DEFAULT;
84  
85      /**
86       * The Runnable whose run() method will be called.
87       */
88      protected Runnable runnable;
89  
90      /**
91       * The (potentially null) Object whose notifyAll() method will be called
92       * immediately after the Runnable.run() method has returned or thrown an exception
93       * or after the event was disposed.
94       *
95       * @see #isDispatched
96       */
97      protected volatile Object notifier;
98  
99      /**
100      * The (potentially null) Runnable whose run() method will be called
101      * immediately after the event was dispatched or disposed.
102      *
103      * @see #isDispatched
104      * @since 1.8
105      */
106     private final Runnable listener;
107 
108     /**
109      * Indicates whether the <code>run()</code> method of the <code>runnable</code>
110      * was executed or not.
111      *
112      * @see #isDispatched
113      * @since 1.7
114      */
115     private volatile boolean dispatched = false;
116 
117     /**
118      * Set to true if dispatch() catches Throwable and stores it in the
119      * exception instance variable. If false, Throwables are propagated up
120      * to the EventDispatchThread's dispatch loop.
121      */
122     protected boolean catchExceptions;
123 
124     /**
125      * The (potentially null) Exception thrown during execution of the
126      * Runnable.run() method. This variable will also be null if a particular
127      * instance does not catch exceptions.
128      */
129     private Exception exception = null;
130 
131     /**
132      * The (potentially null) Throwable thrown during execution of the
133      * Runnable.run() method. This variable will also be null if a particular
134      * instance does not catch exceptions.
135      */
136     private Throwable throwable = null;
137 
138     /**
139      * The timestamp of when this event occurred.
140      *
141      * @serial
142      * @see #getWhen
143      */
144     private long when;
145 
146     /*
147      * JDK 1.1 serialVersionUID.
148      */
149     private static final long serialVersionUID = 436056344909459450L;
150 
151     /**
152      * Constructs an <code>InvocationEvent</code> with the specified
153      * source which will execute the runnable's <code>run</code>
154      * method when dispatched.
155      * <p>This is a convenience constructor.  An invocation of the form
156      * <tt>InvocationEvent(source, runnable)</tt>
157      * behaves in exactly the same way as the invocation of
158      * <tt>{@link #InvocationEvent(Object, Runnable, Object, boolean) InvocationEvent}(source, runnable, null, false)</tt>.
159      * <p> This method throws an <code>IllegalArgumentException</code>
160      * if <code>source</code> is <code>null</code>.
161      *
162      * @param source    The <code>Object</code> that originated the event
163      * @param runnable  The <code>Runnable</code> whose <code>run</code>
164      *                  method will be executed
165      * @throws IllegalArgumentException if <code>source</code> is null
166      *
167      * @see #getSource()
168      * @see #InvocationEvent(Object, Runnable, Object, boolean)
169      */
170     public InvocationEvent(Object source, Runnable runnable) {
171         this(source, INVOCATION_DEFAULT, runnable, null, null, false);
172     }
173 
174     /**
175      * Constructs an <code>InvocationEvent</code> with the specified
176      * source which will execute the runnable's <code>run</code>
177      * method when dispatched.  If notifier is non-<code>null</code>,
178      * <code>notifyAll()</code> will be called on it
179      * immediately after <code>run</code> has returned or thrown an exception.
180      * <p>An invocation of the form <tt>InvocationEvent(source,
181      * runnable, notifier, catchThrowables)</tt>
182      * behaves in exactly the same way as the invocation of
183      * <tt>{@link #InvocationEvent(Object, int, Runnable, Object, boolean) InvocationEvent}(source, InvocationEvent.INVOCATION_DEFAULT, runnable, notifier, catchThrowables)</tt>.
184      * <p>This method throws an <code>IllegalArgumentException</code>
185      * if <code>source</code> is <code>null</code>.
186      *
187      * @param source            The <code>Object</code> that originated
188      *                          the event
189      * @param runnable          The <code>Runnable</code> whose
190      *                          <code>run</code> method will be
191      *                          executed
192      * @param notifier          The {@code Object} whose <code>notifyAll</code>
193      *                          method will be called after
194      *                          <code>Runnable.run</code> has returned or
195      *                          thrown an exception or after the event was
196      *                          disposed
197      * @param catchThrowables   Specifies whether <code>dispatch</code>
198      *                          should catch Throwable when executing
199      *                          the <code>Runnable</code>'s <code>run</code>
200      *                          method, or should instead propagate those
201      *                          Throwables to the EventDispatchThread's
202      *                          dispatch loop
203      * @throws IllegalArgumentException if <code>source</code> is null
204      *
205      * @see #getSource()
206      * @see     #InvocationEvent(Object, int, Runnable, Object, boolean)
207      */
208     public InvocationEvent(Object source, Runnable runnable, Object notifier,
209                            boolean catchThrowables) {
210         this(source, INVOCATION_DEFAULT, runnable, notifier, null, catchThrowables);
211     }
212 
213     /**
214      * Constructs an <code>InvocationEvent</code> with the specified
215      * source which will execute the runnable's <code>run</code>
216      * method when dispatched.  If listener is non-<code>null</code>,
217      * <code>listener.run()</code> will be called immediately after
218      * <code>run</code> has returned, thrown an exception or the event
219      * was disposed.
220      * <p>This method throws an <code>IllegalArgumentException</code>
221      * if <code>source</code> is <code>null</code>.
222      *
223      * @param source            The <code>Object</code> that originated
224      *                          the event
225      * @param runnable          The <code>Runnable</code> whose
226      *                          <code>run</code> method will be
227      *                          executed
228      * @param listener          The <code>Runnable</code>Runnable whose
229      *                          <code>run()</code> method will be called
230      *                          after the {@code InvocationEvent}
231      *                          was dispatched or disposed
232      * @param catchThrowables   Specifies whether <code>dispatch</code>
233      *                          should catch Throwable when executing
234      *                          the <code>Runnable</code>'s <code>run</code>
235      *                          method, or should instead propagate those
236      *                          Throwables to the EventDispatchThread's
237      *                          dispatch loop
238      * @throws IllegalArgumentException if <code>source</code> is null
239      */
240     public InvocationEvent(Object source, Runnable runnable, Runnable listener,
241                            boolean catchThrowables)  {
242         this(source, INVOCATION_DEFAULT, runnable, null, listener, catchThrowables);
243     }
244 
245     /**
246      * Constructs an <code>InvocationEvent</code> with the specified
247      * source and ID which will execute the runnable's <code>run</code>
248      * method when dispatched.  If notifier is non-<code>null</code>,
249      * <code>notifyAll</code> will be called on it immediately after
250      * <code>run</code> has returned or thrown an exception.
251      * <p>This method throws an
252      * <code>IllegalArgumentException</code> if <code>source</code>
253      * is <code>null</code>.
254      *
255      * @param source            The <code>Object</code> that originated
256      *                          the event
257      * @param id     An integer indicating the type of event.
258      *                     For information on allowable values, see
259      *                     the class description for {@link InvocationEvent}
260      * @param runnable          The <code>Runnable</code> whose
261      *                          <code>run</code> method will be executed
262      * @param notifier          The <code>Object</code> whose <code>notifyAll</code>
263      *                          method will be called after
264      *                          <code>Runnable.run</code> has returned or
265      *                          thrown an exception or after the event was
266      *                          disposed
267      * @param catchThrowables   Specifies whether <code>dispatch</code>
268      *                          should catch Throwable when executing the
269      *                          <code>Runnable</code>'s <code>run</code>
270      *                          method, or should instead propagate those
271      *                          Throwables to the EventDispatchThread's
272      *                          dispatch loop
273      * @throws IllegalArgumentException if <code>source</code> is null
274      * @see #getSource()
275      * @see #getID()
276      */
277     protected InvocationEvent(Object source, int id, Runnable runnable,
278                               Object notifier, boolean catchThrowables) {
279         this(source, id, runnable, notifier, null, catchThrowables);
280     }
281 
282     private InvocationEvent(Object source, int id, Runnable runnable,
283                             Object notifier, Runnable listener, boolean catchThrowables) {
284         super(source, id);
285         this.runnable = runnable;
286         this.notifier = notifier;
287         this.listener = listener;
288         this.catchExceptions = catchThrowables;
289         this.when = System.currentTimeMillis();
290     }
291     /**
292      * Executes the Runnable's <code>run()</code> method and notifies the
293      * notifier (if any) when <code>run()</code> has returned or thrown an exception.
294      *
295      * @see #isDispatched
296      */
297     public void dispatch() {
298         try {
299             if (catchExceptions) {
300                 try {
301                     runnable.run();
302                 }
303                 catch (Throwable t) {
304                     if (t instanceof Exception) {
305                         exception = (Exception) t;
306                     }
307                     throwable = t;
308                 }
309             }
310             else {
311                 runnable.run();
312             }
313         } finally {
314             finishedDispatching(true);
315         }
316     }
317 
318     /**
319      * Returns any Exception caught while executing the Runnable's <code>run()
320      * </code> method.
321      *
322      * @return  A reference to the Exception if one was thrown; null if no
323      *          Exception was thrown or if this InvocationEvent does not
324      *          catch exceptions
325      */
326     public Exception getException() {
327         return (catchExceptions) ? exception : null;
328     }
329 
330     /**
331      * Returns any Throwable caught while executing the Runnable's <code>run()
332      * </code> method.
333      *
334      * @return  A reference to the Throwable if one was thrown; null if no
335      *          Throwable was thrown or if this InvocationEvent does not
336      *          catch Throwables
337      * @since 1.5
338      */
339     public Throwable getThrowable() {
340         return (catchExceptions) ? throwable : null;
341     }
342 
343     /**
344      * Returns the timestamp of when this event occurred.
345      *
346      * @return this event's timestamp
347      * @since 1.4
348      */
349     public long getWhen() {
350         return when;
351     }
352 
353     /**
354      * Returns {@code true} if the event is dispatched or any exception is
355      * thrown while dispatching, {@code false} otherwise. The method should
356      * be called by a waiting thread that calls the {@code notifier.wait()} method.
357      * Since spurious wakeups are possible (as explained in {@link Object#wait()}),
358      * this method should be used in a waiting loop to ensure that the event
359      * got dispatched:
360      * <pre>
361      *     while (!event.isDispatched()) {
362      *         notifier.wait();
363      *     }
364      * </pre>
365      * If the waiting thread wakes up without dispatching the event,
366      * the {@code isDispatched()} method returns {@code false}, and
367      * the {@code while} loop executes once more, thus, causing
368      * the awakened thread to revert to the waiting mode.
369      * <p>
370      * If the {@code notifier.notifyAll()} happens before the waiting thread
371      * enters the {@code notifier.wait()} method, the {@code while} loop ensures
372      * that the waiting thread will not enter the {@code notifier.wait()} method.
373      * Otherwise, there is no guarantee that the waiting thread will ever be woken
374      * from the wait.
375      *
376      * @return {@code true} if the event has been dispatched, or any exception
377      * has been thrown while dispatching, {@code false} otherwise
378      * @see #dispatch
379      * @see #notifier
380      * @see #catchExceptions
381      * @since 1.7
382      */
383     public boolean isDispatched() {
384         return dispatched;
385     }
386 
387     /**
388      * Called when the event was dispatched or disposed
389      * @param dispatched true if the event was dispatched
390      *                   false if the event was disposed
391      */
392     private void finishedDispatching(boolean dispatched) {
393         this.dispatched = dispatched;
394 
395         if (notifier != null) {
396             synchronized (notifier) {
397                 notifier.notifyAll();
398             }
399         }
400 
401         if (listener != null) {
402             listener.run();
403         }
404     }
405 
406     /**
407      * Returns a parameter string identifying this event.
408      * This method is useful for event-logging and for debugging.
409      *
410      * @return  A string identifying the event and its attributes
411      */
412     public String paramString() {
413         String typeStr;
414         switch(id) {
415             case INVOCATION_DEFAULT:
416                 typeStr = "INVOCATION_DEFAULT";
417                 break;
418             default:
419                 typeStr = "unknown type";
420         }
421         return typeStr + ",runnable=" + runnable + ",notifier=" + notifier +
422             ",catchExceptions=" + catchExceptions + ",when=" + when;
423     }
424 }