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