29 import java.awt.Window;
30 import java.awt.SystemTray;
31 import java.awt.TrayIcon;
32 import java.awt.Toolkit;
33 import java.awt.GraphicsEnvironment;
34 import java.awt.event.InvocationEvent;
35 import java.security.AccessController;
36 import java.security.PrivilegedAction;
37 import java.util.Collections;
38 import java.util.HashMap;
39 import java.util.IdentityHashMap;
40 import java.util.Map;
41 import java.util.Set;
42 import java.util.HashSet;
43 import java.beans.PropertyChangeSupport;
44 import java.beans.PropertyChangeListener;
45 import sun.util.logging.PlatformLogger;
46 import java.util.concurrent.locks.Condition;
47 import java.util.concurrent.locks.Lock;
48 import java.util.concurrent.locks.ReentrantLock;
49
50 /**
51 * The AppContext is a table referenced by ThreadGroup which stores
52 * application service instances. (If you are not writing an application
53 * service, or don't know what one is, please do not use this class.)
54 * The AppContext allows applet access to what would otherwise be
55 * potentially dangerous services, such as the ability to peek at
56 * EventQueues or change the look-and-feel of a Swing application.<p>
57 *
58 * Most application services use a singleton object to provide their
59 * services, either as a default (such as getSystemEventQueue or
60 * getDefaultToolkit) or as static methods with class data (System).
61 * The AppContext works with the former method by extending the concept
62 * of "default" to be ThreadGroup-specific. Application services
63 * lookup their singleton in the AppContext.<p>
64 *
65 * For example, here we have a Foo service, with its pre-AppContext
66 * code:<p>
67 * <code><pre>
68 * public class Foo {
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,
234 * nor should it ever be used as such. The proper way to construct
235 * an AppContext is through the use of SunToolkit.createNewAppContext.
236 * A ThreadGroup is created for the new AppContext, a Thread is
237 * created within that ThreadGroup, and that Thread calls
238 * SunToolkit.createNewAppContext before calling anything else.
239 * That creates both the new AppContext and its EventQueue.
240 *
241 * @param threadGroup The ThreadGroup for the new AppContext
242 * @see sun.awt.SunToolkit
243 * @since 1.2
244 */
245 AppContext(ThreadGroup threadGroup) {
246 numAppContexts++;
247
248 this.threadGroup = threadGroup;
249 threadGroup2appContext.put(threadGroup, this);
250
251 this.contextClassLoader =
252 AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
253 public ClassLoader run() {
254 return Thread.currentThread().getContextClassLoader();
255 }
256 });
257
258 // Initialize push/pop lock and its condition to be used by all the
259 // EventQueues within this AppContext
260 Lock eventQueuePushPopLock = new ReentrantLock();
261 put(EVENT_QUEUE_LOCK_KEY, eventQueuePushPopLock);
262 Condition eventQueuePushPopCond = eventQueuePushPopLock.newCondition();
263 put(EVENT_QUEUE_COND_KEY, eventQueuePushPopCond);
264 }
265
266 private static final ThreadLocal<AppContext> threadAppContext =
267 new ThreadLocal<AppContext>();
268
269 /**
270 * Returns the appropriate AppContext for the caller,
271 * as determined by its ThreadGroup. If the main "system" AppContext
272 * would be returned and there's an AWTSecurityManager installed, it
273 * is called to get the proper AppContext based on the execution
274 * context.
275 *
276 * @return the AppContext for the caller.
277 * @see java.lang.ThreadGroup
278 * @since 1.2
279 */
280 public final static AppContext getAppContext() {
281 if (numAppContexts == 1) // If there's only one system-wide,
282 return mainAppContext; // return the main system AppContext.
283
284 AppContext appContext = threadAppContext.get();
285
286 if (null == appContext) {
287 appContext = AccessController.doPrivileged(new PrivilegedAction<AppContext>()
288 {
289 public AppContext run() {
290 // Get the current ThreadGroup, and look for it and its
291 // parents in the hash from ThreadGroup to AppContext --
292 // it should be found, because we use createNewContext()
293 // when new AppContext objects are created.
294 ThreadGroup currentThreadGroup = Thread.currentThread().getThreadGroup();
295 ThreadGroup threadGroup = currentThreadGroup;
296 AppContext context = threadGroup2appContext.get(threadGroup);
297 while (context == null) {
298 threadGroup = threadGroup.getParent();
299 if (threadGroup == null) {
300 // If we get here, we're running under a ThreadGroup that
301 // has no AppContext associated with it. This should never
486 numSubGroups = this.threadGroup.enumerate(subGroups);
487 for (int subGroup = 0; subGroup < numSubGroups; subGroup++) {
488 threadGroup2appContext.remove(subGroups[subGroup]);
489 }
490 }
491 threadGroup2appContext.remove(this.threadGroup);
492
493 threadAppContext.set(null);
494
495 // Finally, we destroy the ThreadGroup entirely.
496 try {
497 this.threadGroup.destroy();
498 } catch (IllegalThreadStateException e) {
499 // Fired if not all the Threads died, ignore it and proceed
500 }
501
502 synchronized (table) {
503 this.table.clear(); // Clear out the Hashtable to ease garbage collection
504 }
505
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 {
|
29 import java.awt.Window;
30 import java.awt.SystemTray;
31 import java.awt.TrayIcon;
32 import java.awt.Toolkit;
33 import java.awt.GraphicsEnvironment;
34 import java.awt.event.InvocationEvent;
35 import java.security.AccessController;
36 import java.security.PrivilegedAction;
37 import java.util.Collections;
38 import java.util.HashMap;
39 import java.util.IdentityHashMap;
40 import java.util.Map;
41 import java.util.Set;
42 import java.util.HashSet;
43 import java.beans.PropertyChangeSupport;
44 import java.beans.PropertyChangeListener;
45 import sun.util.logging.PlatformLogger;
46 import java.util.concurrent.locks.Condition;
47 import java.util.concurrent.locks.Lock;
48 import java.util.concurrent.locks.ReentrantLock;
49 import java.util.concurrent.atomic.AtomicInteger;
50
51 /**
52 * The AppContext is a table referenced by ThreadGroup which stores
53 * application service instances. (If you are not writing an application
54 * service, or don't know what one is, please do not use this class.)
55 * The AppContext allows applet access to what would otherwise be
56 * potentially dangerous services, such as the ability to peek at
57 * EventQueues or change the look-and-feel of a Swing application.<p>
58 *
59 * Most application services use a singleton object to provide their
60 * services, either as a default (such as getSystemEventQueue or
61 * getDefaultToolkit) or as static methods with class data (System).
62 * The AppContext works with the former method by extending the concept
63 * of "default" to be ThreadGroup-specific. Application services
64 * lookup their singleton in the AppContext.<p>
65 *
66 * For example, here we have a Foo service, with its pre-AppContext
67 * code:<p>
68 * <code><pre>
69 * public class Foo {
179 /**
180 * If any <code>PropertyChangeListeners</code> have been registered,
181 * the <code>changeSupport</code> field describes them.
182 *
183 * @see #addPropertyChangeListener
184 * @see #removePropertyChangeListener
185 * @see #firePropertyChange
186 */
187 private PropertyChangeSupport changeSupport = null;
188
189 public static final String DISPOSED_PROPERTY_NAME = "disposed";
190 public static final String GUI_DISPOSED = "guidisposed";
191
192 private volatile boolean isDisposed = false; // true if AppContext is disposed
193
194 public boolean isDisposed() {
195 return isDisposed;
196 }
197
198 static {
199 numAppContexts = new AtomicInteger(1);
200
201 // On the main Thread, we get the ThreadGroup, make a corresponding
202 // AppContext, and instantiate the Java EventQueue. This way, legacy
203 // code is unaffected by the move to multiple AppContext ability.
204 AccessController.doPrivileged(new PrivilegedAction() {
205 public Object run() {
206 ThreadGroup currentThreadGroup =
207 Thread.currentThread().getThreadGroup();
208 ThreadGroup parentThreadGroup = currentThreadGroup.getParent();
209 while (parentThreadGroup != null) {
210 // Find the root ThreadGroup to construct our main AppContext
211 currentThreadGroup = parentThreadGroup;
212 parentThreadGroup = currentThreadGroup.getParent();
213 }
214 mainAppContext = new AppContext(currentThreadGroup);
215 return mainAppContext;
216 }
217 });
218 }
219
220 /*
221 * The total number of AppContexts, system-wide. This number is
222 * incremented at the beginning of the constructor, and decremented
223 * at the end of dispose(). getAppContext() checks to see if this
224 * number is 1. If so, it returns the sole AppContext without
225 * checking Thread.currentThread().
226 */
227 private static final AtomicInteger numAppContexts;
228
229 /*
230 * The context ClassLoader that was used to create this AppContext.
231 */
232 private final ClassLoader contextClassLoader;
233
234 /**
235 * Constructor for AppContext. This method is <i>not</i> public,
236 * nor should it ever be used as such. The proper way to construct
237 * an AppContext is through the use of SunToolkit.createNewAppContext.
238 * A ThreadGroup is created for the new AppContext, a Thread is
239 * created within that ThreadGroup, and that Thread calls
240 * SunToolkit.createNewAppContext before calling anything else.
241 * That creates both the new AppContext and its EventQueue.
242 *
243 * @param threadGroup The ThreadGroup for the new AppContext
244 * @see sun.awt.SunToolkit
245 * @since 1.2
246 */
247 AppContext(ThreadGroup threadGroup) {
248 numAppContexts.incrementAndGet();
249
250 this.threadGroup = threadGroup;
251 threadGroup2appContext.put(threadGroup, this);
252
253 this.contextClassLoader =
254 AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
255 public ClassLoader run() {
256 return Thread.currentThread().getContextClassLoader();
257 }
258 });
259
260 // Initialize push/pop lock and its condition to be used by all the
261 // EventQueues within this AppContext
262 Lock eventQueuePushPopLock = new ReentrantLock();
263 put(EVENT_QUEUE_LOCK_KEY, eventQueuePushPopLock);
264 Condition eventQueuePushPopCond = eventQueuePushPopLock.newCondition();
265 put(EVENT_QUEUE_COND_KEY, eventQueuePushPopCond);
266 }
267
268 private static final ThreadLocal<AppContext> threadAppContext =
269 new ThreadLocal<AppContext>();
270
271 /**
272 * Returns the appropriate AppContext for the caller,
273 * as determined by its ThreadGroup. If the main "system" AppContext
274 * would be returned and there's an AWTSecurityManager installed, it
275 * is called to get the proper AppContext based on the execution
276 * context.
277 *
278 * @return the AppContext for the caller.
279 * @see java.lang.ThreadGroup
280 * @since 1.2
281 */
282 public final static AppContext getAppContext() {
283 if (numAppContexts.get() == 1) // If there's only one system-wide,
284 return mainAppContext; // return the main system AppContext.
285
286 AppContext appContext = threadAppContext.get();
287
288 if (null == appContext) {
289 appContext = AccessController.doPrivileged(new PrivilegedAction<AppContext>()
290 {
291 public AppContext run() {
292 // Get the current ThreadGroup, and look for it and its
293 // parents in the hash from ThreadGroup to AppContext --
294 // it should be found, because we use createNewContext()
295 // when new AppContext objects are created.
296 ThreadGroup currentThreadGroup = Thread.currentThread().getThreadGroup();
297 ThreadGroup threadGroup = currentThreadGroup;
298 AppContext context = threadGroup2appContext.get(threadGroup);
299 while (context == null) {
300 threadGroup = threadGroup.getParent();
301 if (threadGroup == null) {
302 // If we get here, we're running under a ThreadGroup that
303 // has no AppContext associated with it. This should never
488 numSubGroups = this.threadGroup.enumerate(subGroups);
489 for (int subGroup = 0; subGroup < numSubGroups; subGroup++) {
490 threadGroup2appContext.remove(subGroups[subGroup]);
491 }
492 }
493 threadGroup2appContext.remove(this.threadGroup);
494
495 threadAppContext.set(null);
496
497 // Finally, we destroy the ThreadGroup entirely.
498 try {
499 this.threadGroup.destroy();
500 } catch (IllegalThreadStateException e) {
501 // Fired if not all the Threads died, ignore it and proceed
502 }
503
504 synchronized (table) {
505 this.table.clear(); // Clear out the Hashtable to ease garbage collection
506 }
507
508 numAppContexts.decrementAndGet();
509
510 mostRecentKeyValue = null;
511 }
512
513 static final class PostShutdownEventRunnable implements Runnable {
514 private final AppContext appContext;
515
516 public PostShutdownEventRunnable(AppContext ac) {
517 appContext = ac;
518 }
519
520 public void run() {
521 final EventQueue eq = (EventQueue)appContext.get(EVENT_QUEUE_KEY);
522 if (eq != null) {
523 eq.postEvent(AWTAutoShutdown.getShutdownEvent());
524 }
525 }
526 }
527
528 static final class CreateThreadAction implements PrivilegedAction {
|