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 }