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 }