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