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()} method on a {@code Runnable
  35  * } when dispatched by the AWT event dispatcher thread. This class can
  36  * be used as a reference implementation of {@code ActiveEvent} rather
  37  * than declaring a new class and defining {@code dispatch()}.<p>
  38  *
  39  * Instances of this class are placed on the {@code EventQueue} by calls
  40  * to {@code invokeLater} and {@code invokeAndWait}. Client code
  41  * can use this fact to write replacement functions for {@code invokeLater
  42  * } and {@code invokeAndWait} without writing special-case code
  43  * in any {@code AWTEventListener} 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()} method of the {@code runnable}
 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} with the specified
 153      * source which will execute the runnable's {@code run}
 154      * method when dispatched.
 155      * <p>This is a convenience constructor.  An invocation of the form
 156      * {@code InvocationEvent(source, runnable)}
 157      * behaves in exactly the same way as the invocation of
 158      * {@link #InvocationEvent(Object, Runnable, Object, boolean)
 159      * InvocationEvent(source, runnable, null, false)}.
 160      * <p> This method throws an {@code IllegalArgumentException}
 161      * if {@code source} is {@code null}.
 162      *
 163      * @param source    The {@code Object} that originated the event
 164      * @param runnable  The {@code Runnable} whose {@code run}
 165      *                  method will be executed
 166      * @throws IllegalArgumentException if {@code source} is null
 167      *
 168      * @see #getSource()
 169      * @see #InvocationEvent(Object, Runnable, Object, boolean)
 170      */
 171     public InvocationEvent(Object source, Runnable runnable) {
 172         this(source, INVOCATION_DEFAULT, runnable, null, null, false);
 173     }
 174 
 175     /**
 176      * Constructs an {@code InvocationEvent} with the specified
 177      * source which will execute the runnable's {@code run}
 178      * method when dispatched.  If notifier is non-{@code null},
 179      * {@code notifyAll()} will be called on it
 180      * immediately after {@code run} has returned or thrown an exception.
 181      * <p>An invocation of the form
 182      * {@code InvocationEvent(source, runnable, notifier, catchThrowables)}
 183      * behaves in exactly the same way as the invocation of
 184      * {@link #InvocationEvent(Object, int, Runnable, Object, boolean)
 185      * InvocationEvent(source, InvocationEvent.INVOCATION_DEFAULT, runnable, notifier, catchThrowables)}.
 186      * <p>This method throws an {@code IllegalArgumentException}
 187      * if {@code source} is {@code null}.
 188      *
 189      * @param source            The {@code Object} that originated
 190      *                          the event
 191      * @param runnable          The {@code Runnable} whose
 192      *                          {@code run} method will be
 193      *                          executed
 194      * @param notifier          The {@code Object} whose {@code notifyAll}
 195      *                          method will be called after
 196      *                          {@code Runnable.run} has returned or
 197      *                          thrown an exception or after the event was
 198      *                          disposed
 199      * @param catchThrowables   Specifies whether {@code dispatch}
 200      *                          should catch Throwable when executing
 201      *                          the {@code Runnable}'s {@code run}
 202      *                          method, or should instead propagate those
 203      *                          Throwables to the EventDispatchThread's
 204      *                          dispatch loop
 205      * @throws IllegalArgumentException if {@code source} is null
 206      *
 207      * @see #getSource()
 208      * @see     #InvocationEvent(Object, int, Runnable, Object, boolean)
 209      */
 210     public InvocationEvent(Object source, Runnable runnable, Object notifier,
 211                            boolean catchThrowables) {
 212         this(source, INVOCATION_DEFAULT, runnable, notifier, null, catchThrowables);
 213     }
 214 
 215     /**
 216      * Constructs an {@code InvocationEvent} with the specified
 217      * source which will execute the runnable's {@code run}
 218      * method when dispatched.  If listener is non-{@code null},
 219      * {@code listener.run()} will be called immediately after
 220      * {@code run} has returned, thrown an exception or the event
 221      * was disposed.
 222      * <p>This method throws an {@code IllegalArgumentException}
 223      * if {@code source} is {@code null}.
 224      *
 225      * @param source            The {@code Object} that originated
 226      *                          the event
 227      * @param runnable          The {@code Runnable} whose
 228      *                          {@code run} method will be
 229      *                          executed
 230      * @param listener          The {@code Runnable}Runnable whose
 231      *                          {@code run()} method will be called
 232      *                          after the {@code InvocationEvent}
 233      *                          was dispatched or disposed
 234      * @param catchThrowables   Specifies whether {@code dispatch}
 235      *                          should catch Throwable when executing
 236      *                          the {@code Runnable}'s {@code run}
 237      *                          method, or should instead propagate those
 238      *                          Throwables to the EventDispatchThread's
 239      *                          dispatch loop
 240      * @throws IllegalArgumentException if {@code source} is null
 241      */
 242     public InvocationEvent(Object source, Runnable runnable, Runnable listener,
 243                            boolean catchThrowables)  {
 244         this(source, INVOCATION_DEFAULT, runnable, null, listener, catchThrowables);
 245     }
 246 
 247     /**
 248      * Constructs an {@code InvocationEvent} with the specified
 249      * source and ID which will execute the runnable's {@code run}
 250      * method when dispatched.  If notifier is non-{@code null},
 251      * {@code notifyAll} will be called on it immediately after
 252      * {@code run} has returned or thrown an exception.
 253      * <p>This method throws an
 254      * {@code IllegalArgumentException} if {@code source}
 255      * is {@code null}.
 256      *
 257      * @param source            The {@code Object} that originated
 258      *                          the event
 259      * @param id     An integer indicating the type of event.
 260      *                     For information on allowable values, see
 261      *                     the class description for {@link InvocationEvent}
 262      * @param runnable          The {@code Runnable} whose
 263      *                          {@code run} method will be executed
 264      * @param notifier          The {@code Object} whose {@code notifyAll}
 265      *                          method will be called after
 266      *                          {@code Runnable.run} has returned or
 267      *                          thrown an exception or after the event was
 268      *                          disposed
 269      * @param catchThrowables   Specifies whether {@code dispatch}
 270      *                          should catch Throwable when executing the
 271      *                          {@code Runnable}'s {@code run}
 272      *                          method, or should instead propagate those
 273      *                          Throwables to the EventDispatchThread's
 274      *                          dispatch loop
 275      * @throws IllegalArgumentException if {@code source} is null
 276      * @see #getSource()
 277      * @see #getID()
 278      */
 279     protected InvocationEvent(Object source, int id, Runnable runnable,
 280                               Object notifier, boolean catchThrowables) {
 281         this(source, id, runnable, notifier, null, catchThrowables);
 282     }
 283 
 284     private InvocationEvent(Object source, int id, Runnable runnable,
 285                             Object notifier, Runnable listener, boolean catchThrowables) {
 286         super(source, id);
 287         this.runnable = runnable;
 288         this.notifier = notifier;
 289         this.listener = listener;
 290         this.catchExceptions = catchThrowables;
 291         this.when = System.currentTimeMillis();
 292     }
 293     /**
 294      * Executes the Runnable's {@code run()} method and notifies the
 295      * notifier (if any) when {@code run()} has returned or thrown an exception.
 296      *
 297      * @see #isDispatched
 298      */
 299     public void dispatch() {
 300         try {
 301             if (catchExceptions) {
 302                 try {
 303                     runnable.run();
 304                 }
 305                 catch (Throwable t) {
 306                     if (t instanceof Exception) {
 307                         exception = (Exception) t;
 308                     }
 309                     throwable = t;
 310                 }
 311             }
 312             else {
 313                 runnable.run();
 314             }
 315         } finally {
 316             finishedDispatching(true);
 317         }
 318     }
 319 
 320     /**
 321      * Returns any Exception caught while executing
 322      * the Runnable's {@code run()} method.
 323      *
 324      * @return  A reference to the Exception if one was thrown; null if no
 325      *          Exception was thrown or if this InvocationEvent does not
 326      *          catch exceptions
 327      */
 328     public Exception getException() {
 329         return (catchExceptions) ? exception : null;
 330     }
 331 
 332     /**
 333      * Returns any Throwable caught while executing
 334      * the Runnable's {@code run()} method.
 335      *
 336      * @return  A reference to the Throwable if one was thrown; null if no
 337      *          Throwable was thrown or if this InvocationEvent does not
 338      *          catch Throwables
 339      * @since 1.5
 340      */
 341     public Throwable getThrowable() {
 342         return (catchExceptions) ? throwable : null;
 343     }
 344 
 345     /**
 346      * Returns the timestamp of when this event occurred.
 347      *
 348      * @return this event's timestamp
 349      * @since 1.4
 350      */
 351     public long getWhen() {
 352         return when;
 353     }
 354 
 355     /**
 356      * Returns {@code true} if the event is dispatched or any exception is
 357      * thrown while dispatching, {@code false} otherwise. The method should
 358      * be called by a waiting thread that calls the {@code notifier.wait()} method.
 359      * Since spurious wakeups are possible (as explained in {@link Object#wait()}),
 360      * this method should be used in a waiting loop to ensure that the event
 361      * got dispatched:
 362      * <pre>
 363      *     while (!event.isDispatched()) {
 364      *         notifier.wait();
 365      *     }
 366      * </pre>
 367      * If the waiting thread wakes up without dispatching the event,
 368      * the {@code isDispatched()} method returns {@code false}, and
 369      * the {@code while} loop executes once more, thus, causing
 370      * the awakened thread to revert to the waiting mode.
 371      * <p>
 372      * If the {@code notifier.notifyAll()} happens before the waiting thread
 373      * enters the {@code notifier.wait()} method, the {@code while} loop ensures
 374      * that the waiting thread will not enter the {@code notifier.wait()} method.
 375      * Otherwise, there is no guarantee that the waiting thread will ever be woken
 376      * from the wait.
 377      *
 378      * @return {@code true} if the event has been dispatched, or any exception
 379      * has been thrown while dispatching, {@code false} otherwise
 380      * @see #dispatch
 381      * @see #notifier
 382      * @see #catchExceptions
 383      * @since 1.7
 384      */
 385     public boolean isDispatched() {
 386         return dispatched;
 387     }
 388 
 389     /**
 390      * Called when the event was dispatched or disposed
 391      * @param dispatched true if the event was dispatched
 392      *                   false if the event was disposed
 393      */
 394     private void finishedDispatching(boolean dispatched) {
 395         this.dispatched = dispatched;
 396 
 397         if (notifier != null) {
 398             synchronized (notifier) {
 399                 notifier.notifyAll();
 400             }
 401         }
 402 
 403         if (listener != null) {
 404             listener.run();
 405         }
 406     }
 407 
 408     /**
 409      * Returns a parameter string identifying this event.
 410      * This method is useful for event-logging and for debugging.
 411      *
 412      * @return  A string identifying the event and its attributes
 413      */
 414     public String paramString() {
 415         String typeStr;
 416         switch(id) {
 417             case INVOCATION_DEFAULT:
 418                 typeStr = "INVOCATION_DEFAULT";
 419                 break;
 420             default:
 421                 typeStr = "unknown type";
 422         }
 423         return typeStr + ",runnable=" + runnable + ",notifier=" + notifier +
 424             ",catchExceptions=" + catchExceptions + ",when=" + when;
 425     }
 426 }