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