154 /** 155 * Returns a set containing all <code>AppContext</code>s. 156 */ 157 public static Set<AppContext> getAppContexts() { 158 synchronized (threadGroup2appContext) { 159 return new HashSet<AppContext>(threadGroup2appContext.values()); 160 } 161 } 162 163 /* The main "system" AppContext, used by everything not otherwise 164 contained in another AppContext. 165 */ 166 private static volatile AppContext mainAppContext = null; 167 168 /* 169 * The hash map associated with this AppContext. A private delegate 170 * is used instead of subclassing HashMap so as to avoid all of 171 * HashMap's potentially risky methods, such as clear(), elements(), 172 * putAll(), etc. 173 */ 174 private final HashMap table = new HashMap(); 175 176 private final ThreadGroup threadGroup; 177 178 /** 179 * If any <code>PropertyChangeListeners</code> have been registered, 180 * the <code>changeSupport</code> field describes them. 181 * 182 * @see #addPropertyChangeListener 183 * @see #removePropertyChangeListener 184 * @see #firePropertyChange 185 */ 186 private PropertyChangeSupport changeSupport = null; 187 188 public static final String DISPOSED_PROPERTY_NAME = "disposed"; 189 public static final String GUI_DISPOSED = "guidisposed"; 190 191 private volatile boolean isDisposed = false; // true if AppContext is disposed 192 193 public boolean isDisposed() { 194 return isDisposed; 195 } 196 197 static { 198 // On the main Thread, we get the ThreadGroup, make a corresponding 199 // AppContext, and instantiate the Java EventQueue. This way, legacy 200 // code is unaffected by the move to multiple AppContext ability. 201 AccessController.doPrivileged(new PrivilegedAction() { 202 public Object run() { 203 ThreadGroup currentThreadGroup = 204 Thread.currentThread().getThreadGroup(); 205 ThreadGroup parentThreadGroup = currentThreadGroup.getParent(); 206 while (parentThreadGroup != null) { 207 // Find the root ThreadGroup to construct our main AppContext 208 currentThreadGroup = parentThreadGroup; 209 parentThreadGroup = currentThreadGroup.getParent(); 210 } 211 mainAppContext = new AppContext(currentThreadGroup); 212 numAppContexts = 1; 213 return mainAppContext; 214 } 215 }); 216 } 217 218 /* 219 * The total number of AppContexts, system-wide. This number is 220 * incremented at the beginning of the constructor, and decremented 221 * at the end of dispose(). getAppContext() checks to see if this 222 * number is 1. If so, it returns the sole AppContext without 223 * checking Thread.currentThread(). 224 */ 225 private static volatile int numAppContexts; 226 227 /* 228 * The context ClassLoader that was used to create this AppContext. 229 */ 230 private final ClassLoader contextClassLoader; 231 232 /** 233 * Constructor for AppContext. This method is <i>not</i> public, 382 final PropertyChangeSupport changeSupport = this.changeSupport; 383 if (changeSupport != null) { 384 changeSupport.firePropertyChange(DISPOSED_PROPERTY_NAME, false, true); 385 } 386 387 // First, we post an InvocationEvent to be run on the 388 // EventDispatchThread which disposes of all top-level Frames and TrayIcons 389 390 final Object notificationLock = new Object(); 391 392 Runnable runnable = new Runnable() { 393 public void run() { 394 Window[] windowsToDispose = Window.getOwnerlessWindows(); 395 for (Window w : windowsToDispose) { 396 try { 397 w.dispose(); 398 } catch (Throwable t) { 399 log.finer("exception occured while disposing app context", t); 400 } 401 } 402 AccessController.doPrivileged(new PrivilegedAction() { 403 public Object run() { 404 if (!GraphicsEnvironment.isHeadless() && SystemTray.isSupported()) 405 { 406 SystemTray systemTray = SystemTray.getSystemTray(); 407 TrayIcon[] trayIconsToDispose = systemTray.getTrayIcons(); 408 for (TrayIcon ti : trayIconsToDispose) { 409 systemTray.remove(ti); 410 } 411 } 412 return null; 413 } 414 }); 415 // Alert PropertyChangeListeners that the GUI has been disposed. 416 if (changeSupport != null) { 417 changeSupport.firePropertyChange(GUI_DISPOSED, false, true); 418 } 419 synchronized(notificationLock) { 420 notificationLock.notifyAll(); // Notify caller that we're done 421 } 422 } 423 }; 506 numAppContexts--; 507 508 mostRecentKeyValue = null; 509 } 510 511 static final class PostShutdownEventRunnable implements Runnable { 512 private final AppContext appContext; 513 514 public PostShutdownEventRunnable(AppContext ac) { 515 appContext = ac; 516 } 517 518 public void run() { 519 final EventQueue eq = (EventQueue)appContext.get(EVENT_QUEUE_KEY); 520 if (eq != null) { 521 eq.postEvent(AWTAutoShutdown.getShutdownEvent()); 522 } 523 } 524 } 525 526 static final class CreateThreadAction implements PrivilegedAction { 527 private final AppContext appContext; 528 private final Runnable runnable; 529 530 public CreateThreadAction(AppContext ac, Runnable r) { 531 appContext = ac; 532 runnable = r; 533 } 534 535 public Object run() { 536 Thread t = new Thread(appContext.getThreadGroup(), runnable); 537 t.setContextClassLoader(appContext.getContextClassLoader()); 538 t.setPriority(Thread.NORM_PRIORITY + 1); 539 t.setDaemon(true); 540 return t; 541 } 542 } 543 544 static void stopEventDispatchThreads() { 545 for (AppContext appContext: getAppContexts()) { 546 if (appContext.isDisposed()) { 547 continue; 548 } 549 Runnable r = new PostShutdownEventRunnable(appContext); 550 // For security reasons EventQueue.postEvent should only be called 551 // on a thread that belongs to the corresponding thread group. 552 if (appContext != AppContext.getAppContext()) { 553 // Create a thread that belongs to the thread group associated 554 // with the AppContext and invokes EventQueue.postEvent. 555 PrivilegedAction action = new CreateThreadAction(appContext, r); 556 Thread thread = (Thread)AccessController.doPrivileged(action); 557 thread.start(); 558 } else { 559 r.run(); 560 } 561 } 562 } 563 564 private MostRecentKeyValue mostRecentKeyValue = null; 565 private MostRecentKeyValue shadowMostRecentKeyValue = null; 566 567 /** 568 * Returns the value to which the specified key is mapped in this context. 569 * 570 * @param key a key in the AppContext. 571 * @return the value to which the key is mapped in this AppContext; 572 * <code>null</code> if the key is not mapped to any value. 573 * @see #put(Object, Object) 574 * @since 1.2 575 */ 576 public Object get(Object key) { | 154 /** 155 * Returns a set containing all <code>AppContext</code>s. 156 */ 157 public static Set<AppContext> getAppContexts() { 158 synchronized (threadGroup2appContext) { 159 return new HashSet<AppContext>(threadGroup2appContext.values()); 160 } 161 } 162 163 /* The main "system" AppContext, used by everything not otherwise 164 contained in another AppContext. 165 */ 166 private static volatile AppContext mainAppContext = null; 167 168 /* 169 * The hash map associated with this AppContext. A private delegate 170 * is used instead of subclassing HashMap so as to avoid all of 171 * HashMap's potentially risky methods, such as clear(), elements(), 172 * putAll(), etc. 173 */ 174 private final Map<Object, Object> table = new HashMap<>(); 175 176 private final ThreadGroup threadGroup; 177 178 /** 179 * If any <code>PropertyChangeListeners</code> have been registered, 180 * the <code>changeSupport</code> field describes them. 181 * 182 * @see #addPropertyChangeListener 183 * @see #removePropertyChangeListener 184 * @see #firePropertyChange 185 */ 186 private PropertyChangeSupport changeSupport = null; 187 188 public static final String DISPOSED_PROPERTY_NAME = "disposed"; 189 public static final String GUI_DISPOSED = "guidisposed"; 190 191 private volatile boolean isDisposed = false; // true if AppContext is disposed 192 193 public boolean isDisposed() { 194 return isDisposed; 195 } 196 197 static { 198 // On the main Thread, we get the ThreadGroup, make a corresponding 199 // AppContext, and instantiate the Java EventQueue. This way, legacy 200 // code is unaffected by the move to multiple AppContext ability. 201 AccessController.doPrivileged(new PrivilegedAction<Void>() { 202 public Void run() { 203 ThreadGroup currentThreadGroup = 204 Thread.currentThread().getThreadGroup(); 205 ThreadGroup parentThreadGroup = currentThreadGroup.getParent(); 206 while (parentThreadGroup != null) { 207 // Find the root ThreadGroup to construct our main AppContext 208 currentThreadGroup = parentThreadGroup; 209 parentThreadGroup = currentThreadGroup.getParent(); 210 } 211 mainAppContext = new AppContext(currentThreadGroup); 212 numAppContexts = 1; 213 return null; 214 } 215 }); 216 } 217 218 /* 219 * The total number of AppContexts, system-wide. This number is 220 * incremented at the beginning of the constructor, and decremented 221 * at the end of dispose(). getAppContext() checks to see if this 222 * number is 1. If so, it returns the sole AppContext without 223 * checking Thread.currentThread(). 224 */ 225 private static volatile int numAppContexts; 226 227 /* 228 * The context ClassLoader that was used to create this AppContext. 229 */ 230 private final ClassLoader contextClassLoader; 231 232 /** 233 * Constructor for AppContext. This method is <i>not</i> public, 382 final PropertyChangeSupport changeSupport = this.changeSupport; 383 if (changeSupport != null) { 384 changeSupport.firePropertyChange(DISPOSED_PROPERTY_NAME, false, true); 385 } 386 387 // First, we post an InvocationEvent to be run on the 388 // EventDispatchThread which disposes of all top-level Frames and TrayIcons 389 390 final Object notificationLock = new Object(); 391 392 Runnable runnable = new Runnable() { 393 public void run() { 394 Window[] windowsToDispose = Window.getOwnerlessWindows(); 395 for (Window w : windowsToDispose) { 396 try { 397 w.dispose(); 398 } catch (Throwable t) { 399 log.finer("exception occured while disposing app context", t); 400 } 401 } 402 AccessController.doPrivileged(new PrivilegedAction<Void>() { 403 public Void run() { 404 if (!GraphicsEnvironment.isHeadless() && SystemTray.isSupported()) 405 { 406 SystemTray systemTray = SystemTray.getSystemTray(); 407 TrayIcon[] trayIconsToDispose = systemTray.getTrayIcons(); 408 for (TrayIcon ti : trayIconsToDispose) { 409 systemTray.remove(ti); 410 } 411 } 412 return null; 413 } 414 }); 415 // Alert PropertyChangeListeners that the GUI has been disposed. 416 if (changeSupport != null) { 417 changeSupport.firePropertyChange(GUI_DISPOSED, false, true); 418 } 419 synchronized(notificationLock) { 420 notificationLock.notifyAll(); // Notify caller that we're done 421 } 422 } 423 }; 506 numAppContexts--; 507 508 mostRecentKeyValue = null; 509 } 510 511 static final class PostShutdownEventRunnable implements Runnable { 512 private final AppContext appContext; 513 514 public PostShutdownEventRunnable(AppContext ac) { 515 appContext = ac; 516 } 517 518 public void run() { 519 final EventQueue eq = (EventQueue)appContext.get(EVENT_QUEUE_KEY); 520 if (eq != null) { 521 eq.postEvent(AWTAutoShutdown.getShutdownEvent()); 522 } 523 } 524 } 525 526 static final class CreateThreadAction implements PrivilegedAction<Thread> { 527 private final AppContext appContext; 528 private final Runnable runnable; 529 530 public CreateThreadAction(AppContext ac, Runnable r) { 531 appContext = ac; 532 runnable = r; 533 } 534 535 public Thread run() { 536 Thread t = new Thread(appContext.getThreadGroup(), runnable); 537 t.setContextClassLoader(appContext.getContextClassLoader()); 538 t.setPriority(Thread.NORM_PRIORITY + 1); 539 t.setDaemon(true); 540 return t; 541 } 542 } 543 544 static void stopEventDispatchThreads() { 545 for (AppContext appContext: getAppContexts()) { 546 if (appContext.isDisposed()) { 547 continue; 548 } 549 Runnable r = new PostShutdownEventRunnable(appContext); 550 // For security reasons EventQueue.postEvent should only be called 551 // on a thread that belongs to the corresponding thread group. 552 if (appContext != AppContext.getAppContext()) { 553 // Create a thread that belongs to the thread group associated 554 // with the AppContext and invokes EventQueue.postEvent. 555 PrivilegedAction<Thread> action = new CreateThreadAction(appContext, r); 556 Thread thread = AccessController.doPrivileged(action); 557 thread.start(); 558 } else { 559 r.run(); 560 } 561 } 562 } 563 564 private MostRecentKeyValue mostRecentKeyValue = null; 565 private MostRecentKeyValue shadowMostRecentKeyValue = null; 566 567 /** 568 * Returns the value to which the specified key is mapped in this context. 569 * 570 * @param key a key in the AppContext. 571 * @return the value to which the key is mapped in this AppContext; 572 * <code>null</code> if the key is not mapped to any value. 573 * @see #put(Object, Object) 574 * @since 1.2 575 */ 576 public Object get(Object key) { |