Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/awt/AppContext.java
38827 views
/*1* Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.awt;2627import java.awt.EventQueue;28import java.awt.Window;29import java.awt.SystemTray;30import java.awt.TrayIcon;31import java.awt.Toolkit;32import java.awt.GraphicsEnvironment;33import java.awt.event.InvocationEvent;34import java.security.AccessController;35import java.security.PrivilegedAction;36import java.util.Collections;37import java.util.HashMap;38import java.util.IdentityHashMap;39import java.util.Map;40import java.util.Set;41import java.util.HashSet;42import java.beans.PropertyChangeSupport;43import java.beans.PropertyChangeListener;44import java.lang.ref.SoftReference;45import sun.util.logging.PlatformLogger;46import java.util.concurrent.locks.Condition;47import java.util.concurrent.locks.Lock;48import java.util.concurrent.locks.ReentrantLock;49import java.util.concurrent.atomic.AtomicInteger;50import java.util.function.Supplier;5152/**53* The AppContext is a table referenced by ThreadGroup which stores54* application service instances. (If you are not writing an application55* service, or don't know what one is, please do not use this class.)56* The AppContext allows applet access to what would otherwise be57* potentially dangerous services, such as the ability to peek at58* EventQueues or change the look-and-feel of a Swing application.<p>59*60* Most application services use a singleton object to provide their61* services, either as a default (such as getSystemEventQueue or62* getDefaultToolkit) or as static methods with class data (System).63* The AppContext works with the former method by extending the concept64* of "default" to be ThreadGroup-specific. Application services65* lookup their singleton in the AppContext.<p>66*67* For example, here we have a Foo service, with its pre-AppContext68* code:<p>69* <code><pre>70* public class Foo {71* private static Foo defaultFoo = new Foo();72*73* public static Foo getDefaultFoo() {74* return defaultFoo;75* }76*77* ... Foo service methods78* }</pre></code><p>79*80* The problem with the above is that the Foo service is global in scope,81* so that applets and other untrusted code can execute methods on the82* single, shared Foo instance. The Foo service therefore either needs83* to block its use by untrusted code using a SecurityManager test, or84* restrict its capabilities so that it doesn't matter if untrusted code85* executes it.<p>86*87* Here's the Foo class written to use the AppContext:<p>88* <code><pre>89* public class Foo {90* public static Foo getDefaultFoo() {91* Foo foo = (Foo)AppContext.getAppContext().get(Foo.class);92* if (foo == null) {93* foo = new Foo();94* getAppContext().put(Foo.class, foo);95* }96* return foo;97* }98*99* ... Foo service methods100* }</pre></code><p>101*102* Since a separate AppContext can exist for each ThreadGroup, trusted103* and untrusted code have access to different Foo instances. This allows104* untrusted code access to "system-wide" services -- the service remains105* within the AppContext "sandbox". For example, say a malicious applet106* wants to peek all of the key events on the EventQueue to listen for107* passwords; if separate EventQueues are used for each ThreadGroup108* using AppContexts, the only key events that applet will be able to109* listen to are its own. A more reasonable applet request would be to110* change the Swing default look-and-feel; with that default stored in111* an AppContext, the applet's look-and-feel will change without112* disrupting other applets or potentially the browser itself.<p>113*114* Because the AppContext is a facility for safely extending application115* service support to applets, none of its methods may be blocked by a116* a SecurityManager check in a valid Java implementation. Applets may117* therefore safely invoke any of its methods without worry of being118* blocked.119*120* Note: If a SecurityManager is installed which derives from121* sun.awt.AWTSecurityManager, it may override the122* AWTSecurityManager.getAppContext() method to return the proper123* AppContext based on the execution context, in the case where124* the default ThreadGroup-based AppContext indexing would return125* the main "system" AppContext. For example, in an applet situation,126* if a system thread calls into an applet, rather than returning the127* main "system" AppContext (the one corresponding to the system thread),128* an installed AWTSecurityManager may return the applet's AppContext129* based on the execution context.130*131* @author Thomas Ball132* @author Fred Ecks133*/134public final class AppContext {135private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.AppContext");136137/* Since the contents of an AppContext are unique to each Java138* session, this class should never be serialized. */139140/*141* The key to put()/get() the Java EventQueue into/from the AppContext.142*/143public static final Object EVENT_QUEUE_KEY = new StringBuffer("EventQueue");144145/*146* The keys to store EventQueue push/pop lock and condition.147*/148public final static Object EVENT_QUEUE_LOCK_KEY = new StringBuilder("EventQueue.Lock");149public final static Object EVENT_QUEUE_COND_KEY = new StringBuilder("EventQueue.Condition");150151/* A map of AppContexts, referenced by ThreadGroup.152*/153private static final Map<ThreadGroup, AppContext> threadGroup2appContext =154Collections.synchronizedMap(new IdentityHashMap<ThreadGroup, AppContext>());155156/**157* Returns a set containing all <code>AppContext</code>s.158*/159public static Set<AppContext> getAppContexts() {160synchronized (threadGroup2appContext) {161return new HashSet<AppContext>(threadGroup2appContext.values());162}163}164165/* The main "system" AppContext, used by everything not otherwise166contained in another AppContext. It is implicitly created for167standalone apps only (i.e. not applets)168*/169private static volatile AppContext mainAppContext = null;170171private static class GetAppContextLock {};172private final static Object getAppContextLock = new GetAppContextLock();173174/*175* The hash map associated with this AppContext. A private delegate176* is used instead of subclassing HashMap so as to avoid all of177* HashMap's potentially risky methods, such as clear(), elements(),178* putAll(), etc.179*/180private final Map<Object, Object> table = new HashMap<>();181182private final ThreadGroup threadGroup;183184/**185* If any <code>PropertyChangeListeners</code> have been registered,186* the <code>changeSupport</code> field describes them.187*188* @see #addPropertyChangeListener189* @see #removePropertyChangeListener190* @see #firePropertyChange191*/192private PropertyChangeSupport changeSupport = null;193194public static final String DISPOSED_PROPERTY_NAME = "disposed";195public static final String GUI_DISPOSED = "guidisposed";196197private enum State {198VALID,199BEING_DISPOSED,200DISPOSED201};202203private volatile State state = State.VALID;204205public boolean isDisposed() {206return state == State.DISPOSED;207}208209/*210* The total number of AppContexts, system-wide. This number is211* incremented at the beginning of the constructor, and decremented212* at the end of dispose(). getAppContext() checks to see if this213* number is 1. If so, it returns the sole AppContext without214* checking Thread.currentThread().215*/216private static final AtomicInteger numAppContexts = new AtomicInteger(0);217218219/*220* The context ClassLoader that was used to create this AppContext.221*/222private final ClassLoader contextClassLoader;223224/**225* Constructor for AppContext. This method is <i>not</i> public,226* nor should it ever be used as such. The proper way to construct227* an AppContext is through the use of SunToolkit.createNewAppContext.228* A ThreadGroup is created for the new AppContext, a Thread is229* created within that ThreadGroup, and that Thread calls230* SunToolkit.createNewAppContext before calling anything else.231* That creates both the new AppContext and its EventQueue.232*233* @param threadGroup The ThreadGroup for the new AppContext234* @see sun.awt.SunToolkit235* @since 1.2236*/237AppContext(ThreadGroup threadGroup) {238numAppContexts.incrementAndGet();239240this.threadGroup = threadGroup;241threadGroup2appContext.put(threadGroup, this);242243this.contextClassLoader =244AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {245public ClassLoader run() {246return Thread.currentThread().getContextClassLoader();247}248});249250// Initialize push/pop lock and its condition to be used by all the251// EventQueues within this AppContext252Lock eventQueuePushPopLock = new ReentrantLock();253put(EVENT_QUEUE_LOCK_KEY, eventQueuePushPopLock);254Condition eventQueuePushPopCond = eventQueuePushPopLock.newCondition();255put(EVENT_QUEUE_COND_KEY, eventQueuePushPopCond);256}257258private static final ThreadLocal<AppContext> threadAppContext =259new ThreadLocal<AppContext>();260261private final static void initMainAppContext() {262// On the main Thread, we get the ThreadGroup, make a corresponding263// AppContext, and instantiate the Java EventQueue. This way, legacy264// code is unaffected by the move to multiple AppContext ability.265AccessController.doPrivileged(new PrivilegedAction<Void>() {266public Void run() {267ThreadGroup currentThreadGroup =268Thread.currentThread().getThreadGroup();269ThreadGroup parentThreadGroup = currentThreadGroup.getParent();270while (parentThreadGroup != null) {271// Find the root ThreadGroup to construct our main AppContext272currentThreadGroup = parentThreadGroup;273parentThreadGroup = currentThreadGroup.getParent();274}275276mainAppContext = SunToolkit.createNewAppContext(currentThreadGroup);277return null;278}279});280}281282/**283* Returns the appropriate AppContext for the caller,284* as determined by its ThreadGroup. If the main "system" AppContext285* would be returned and there's an AWTSecurityManager installed, it286* is called to get the proper AppContext based on the execution287* context.288*289* @return the AppContext for the caller.290* @see java.lang.ThreadGroup291* @since 1.2292*/293public final static AppContext getAppContext() {294// we are standalone app, return the main app context295if (numAppContexts.get() == 1 && mainAppContext != null) {296return mainAppContext;297}298299AppContext appContext = threadAppContext.get();300301if (null == appContext) {302appContext = AccessController.doPrivileged(new PrivilegedAction<AppContext>()303{304public AppContext run() {305// Get the current ThreadGroup, and look for it and its306// parents in the hash from ThreadGroup to AppContext --307// it should be found, because we use createNewContext()308// when new AppContext objects are created.309ThreadGroup currentThreadGroup = Thread.currentThread().getThreadGroup();310ThreadGroup threadGroup = currentThreadGroup;311312// Special case: we implicitly create the main app context313// if no contexts have been created yet. This covers standalone apps314// and excludes applets because by the time applet starts315// a number of contexts have already been created by the plugin.316synchronized (getAppContextLock) {317if (numAppContexts.get() == 0) {318if (System.getProperty("javaplugin.version") == null &&319System.getProperty("javawebstart.version") == null) {320initMainAppContext();321} else if (System.getProperty("javafx.version") != null &&322threadGroup.getParent() != null) {323// Swing inside JavaFX case324SunToolkit.createNewAppContext();325}326}327}328329AppContext context = threadGroup2appContext.get(threadGroup);330while (context == null) {331threadGroup = threadGroup.getParent();332if (threadGroup == null) {333// We've got up to the root thread group and did not find an AppContext334// Try to get it from the security manager335SecurityManager securityManager = System.getSecurityManager();336if (securityManager != null) {337ThreadGroup smThreadGroup = securityManager.getThreadGroup();338if (smThreadGroup != null) {339/*340* If we get this far then it's likely that341* the ThreadGroup does not actually belong342* to the applet, so do not cache it.343*/344return threadGroup2appContext.get(smThreadGroup);345}346}347return null;348}349context = threadGroup2appContext.get(threadGroup);350}351352// In case we did anything in the above while loop, we add353// all the intermediate ThreadGroups to threadGroup2appContext354// so we won't spin again.355for (ThreadGroup tg = currentThreadGroup; tg != threadGroup; tg = tg.getParent()) {356threadGroup2appContext.put(tg, context);357}358359// Now we're done, so we cache the latest key/value pair.360threadAppContext.set(context);361362return context;363}364});365}366367return appContext;368}369370/**371* Returns true if the specified AppContext is the main AppContext.372*373* @param ctx the context to compare with the main context374* @return true if the specified AppContext is the main AppContext.375* @since 1.8376*/377public final static boolean isMainContext(AppContext ctx) {378return (ctx != null && ctx == mainAppContext);379}380381private final static AppContext getExecutionAppContext() {382SecurityManager securityManager = System.getSecurityManager();383if ((securityManager != null) &&384(securityManager instanceof AWTSecurityManager))385{386AWTSecurityManager awtSecMgr = (AWTSecurityManager) securityManager;387AppContext secAppContext = awtSecMgr.getAppContext();388return secAppContext; // Return what we're told389}390return null;391}392393private long DISPOSAL_TIMEOUT = 5000; // Default to 5-second timeout394// for disposal of all Frames395// (we wait for this time twice,396// once for dispose(), and once397// to clear the EventQueue).398399private long THREAD_INTERRUPT_TIMEOUT = 1000;400// Default to 1-second timeout for all401// interrupted Threads to exit, and another402// 1 second for all stopped Threads to die.403404/**405* Disposes of this AppContext, all of its top-level Frames, and406* all Threads and ThreadGroups contained within it.407*408* This method must be called from a Thread which is not contained409* within this AppContext.410*411* @exception IllegalThreadStateException if the current thread is412* contained within this AppContext413* @since 1.2414*/415public void dispose() throws IllegalThreadStateException {416// Check to be sure that the current Thread isn't in this AppContext417if (this.threadGroup.parentOf(Thread.currentThread().getThreadGroup())) {418throw new IllegalThreadStateException(419"Current Thread is contained within AppContext to be disposed."420);421}422423synchronized(this) {424if (this.state != State.VALID) {425return; // If already disposed or being disposed, bail.426}427428this.state = State.BEING_DISPOSED;429}430431final PropertyChangeSupport changeSupport = this.changeSupport;432if (changeSupport != null) {433changeSupport.firePropertyChange(DISPOSED_PROPERTY_NAME, false, true);434}435436// First, we post an InvocationEvent to be run on the437// EventDispatchThread which disposes of all top-level Frames and TrayIcons438439final Object notificationLock = new Object();440441Runnable runnable = new Runnable() {442public void run() {443Window[] windowsToDispose = Window.getOwnerlessWindows();444for (Window w : windowsToDispose) {445try {446w.dispose();447} catch (Throwable t) {448log.finer("exception occurred while disposing app context", t);449}450}451AccessController.doPrivileged(new PrivilegedAction<Void>() {452public Void run() {453if (!GraphicsEnvironment.isHeadless() && SystemTray.isSupported())454{455SystemTray systemTray = SystemTray.getSystemTray();456TrayIcon[] trayIconsToDispose = systemTray.getTrayIcons();457for (TrayIcon ti : trayIconsToDispose) {458systemTray.remove(ti);459}460}461return null;462}463});464// Alert PropertyChangeListeners that the GUI has been disposed.465if (changeSupport != null) {466changeSupport.firePropertyChange(GUI_DISPOSED, false, true);467}468synchronized(notificationLock) {469notificationLock.notifyAll(); // Notify caller that we're done470}471}472};473synchronized(notificationLock) {474SunToolkit.postEvent(this,475new InvocationEvent(Toolkit.getDefaultToolkit(), runnable));476try {477notificationLock.wait(DISPOSAL_TIMEOUT);478} catch (InterruptedException e) { }479}480481// Next, we post another InvocationEvent to the end of the482// EventQueue. When it's executed, we know we've executed all483// events in the queue.484485runnable = new Runnable() { public void run() {486synchronized(notificationLock) {487notificationLock.notifyAll(); // Notify caller that we're done488}489} };490synchronized(notificationLock) {491SunToolkit.postEvent(this,492new InvocationEvent(Toolkit.getDefaultToolkit(), runnable));493try {494notificationLock.wait(DISPOSAL_TIMEOUT);495} catch (InterruptedException e) { }496}497498// We are done with posting events, so change the state to disposed499synchronized(this) {500this.state = State.DISPOSED;501}502503// Next, we interrupt all Threads in the ThreadGroup504this.threadGroup.interrupt();505// Note, the EventDispatchThread we've interrupted may dump an506// InterruptedException to the console here. This needs to be507// fixed in the EventDispatchThread, not here.508509// Next, we sleep 10ms at a time, waiting for all of the active510// Threads in the ThreadGroup to exit.511512long startTime = System.currentTimeMillis();513long endTime = startTime + THREAD_INTERRUPT_TIMEOUT;514while ((this.threadGroup.activeCount() > 0) &&515(System.currentTimeMillis() < endTime)) {516try {517Thread.sleep(10);518} catch (InterruptedException e) { }519}520521// Then, we stop any remaining Threads522this.threadGroup.stop();523524// Next, we sleep 10ms at a time, waiting for all of the active525// Threads in the ThreadGroup to die.526527startTime = System.currentTimeMillis();528endTime = startTime + THREAD_INTERRUPT_TIMEOUT;529while ((this.threadGroup.activeCount() > 0) &&530(System.currentTimeMillis() < endTime)) {531try {532Thread.sleep(10);533} catch (InterruptedException e) { }534}535536// Next, we remove this and all subThreadGroups from threadGroup2appContext537int numSubGroups = this.threadGroup.activeGroupCount();538if (numSubGroups > 0) {539ThreadGroup [] subGroups = new ThreadGroup[numSubGroups];540numSubGroups = this.threadGroup.enumerate(subGroups);541for (int subGroup = 0; subGroup < numSubGroups; subGroup++) {542threadGroup2appContext.remove(subGroups[subGroup]);543}544}545threadGroup2appContext.remove(this.threadGroup);546547threadAppContext.set(null);548549// Finally, we destroy the ThreadGroup entirely.550try {551this.threadGroup.destroy();552} catch (IllegalThreadStateException e) {553// Fired if not all the Threads died, ignore it and proceed554}555556synchronized (table) {557this.table.clear(); // Clear out the Hashtable to ease garbage collection558}559560numAppContexts.decrementAndGet();561562mostRecentKeyValue = null;563}564565static final class PostShutdownEventRunnable implements Runnable {566private final AppContext appContext;567568public PostShutdownEventRunnable(AppContext ac) {569appContext = ac;570}571572public void run() {573final EventQueue eq = (EventQueue)appContext.get(EVENT_QUEUE_KEY);574if (eq != null) {575eq.postEvent(AWTAutoShutdown.getShutdownEvent());576}577}578}579580static final class CreateThreadAction implements PrivilegedAction<Thread> {581private final AppContext appContext;582private final Runnable runnable;583584public CreateThreadAction(AppContext ac, Runnable r) {585appContext = ac;586runnable = r;587}588589public Thread run() {590Thread t = new Thread(appContext.getThreadGroup(), runnable);591t.setContextClassLoader(appContext.getContextClassLoader());592t.setPriority(Thread.NORM_PRIORITY + 1);593t.setDaemon(true);594return t;595}596}597598static void stopEventDispatchThreads() {599for (AppContext appContext: getAppContexts()) {600if (appContext.isDisposed()) {601continue;602}603Runnable r = new PostShutdownEventRunnable(appContext);604// For security reasons EventQueue.postEvent should only be called605// on a thread that belongs to the corresponding thread group.606if (appContext != AppContext.getAppContext()) {607// Create a thread that belongs to the thread group associated608// with the AppContext and invokes EventQueue.postEvent.609PrivilegedAction<Thread> action = new CreateThreadAction(appContext, r);610Thread thread = AccessController.doPrivileged(action);611thread.start();612} else {613r.run();614}615}616}617618private MostRecentKeyValue mostRecentKeyValue = null;619private MostRecentKeyValue shadowMostRecentKeyValue = null;620621/**622* Returns the value to which the specified key is mapped in this context.623*624* @param key a key in the AppContext.625* @return the value to which the key is mapped in this AppContext;626* <code>null</code> if the key is not mapped to any value.627* @see #put(Object, Object)628* @since 1.2629*/630public Object get(Object key) {631/*632* The most recent reference should be updated inside a synchronized633* block to avoid a race when put() and get() are executed in634* parallel on different threads.635*/636synchronized (table) {637// Note: this most recent key/value caching is thread-hot.638// A simple test using SwingSet found that 72% of lookups639// were matched using the most recent key/value. By instantiating640// a simple MostRecentKeyValue object on cache misses, the641// cache hits can be processed without synchronization.642643MostRecentKeyValue recent = mostRecentKeyValue;644if ((recent != null) && (recent.key == key)) {645return recent.value;646}647648Object value = table.get(key);649if(mostRecentKeyValue == null) {650mostRecentKeyValue = new MostRecentKeyValue(key, value);651shadowMostRecentKeyValue = new MostRecentKeyValue(key, value);652} else {653MostRecentKeyValue auxKeyValue = mostRecentKeyValue;654shadowMostRecentKeyValue.setPair(key, value);655mostRecentKeyValue = shadowMostRecentKeyValue;656shadowMostRecentKeyValue = auxKeyValue;657}658return value;659}660}661662/**663* Maps the specified <code>key</code> to the specified664* <code>value</code> in this AppContext. Neither the key nor the665* value can be <code>null</code>.666* <p>667* The value can be retrieved by calling the <code>get</code> method668* with a key that is equal to the original key.669*670* @param key the AppContext key.671* @param value the value.672* @return the previous value of the specified key in this673* AppContext, or <code>null</code> if it did not have one.674* @exception NullPointerException if the key or value is675* <code>null</code>.676* @see #get(Object)677* @since 1.2678*/679public Object put(Object key, Object value) {680synchronized (table) {681MostRecentKeyValue recent = mostRecentKeyValue;682if ((recent != null) && (recent.key == key))683recent.value = value;684return table.put(key, value);685}686}687688/**689* Removes the key (and its corresponding value) from this690* AppContext. This method does nothing if the key is not in the691* AppContext.692*693* @param key the key that needs to be removed.694* @return the value to which the key had been mapped in this AppContext,695* or <code>null</code> if the key did not have a mapping.696* @since 1.2697*/698public Object remove(Object key) {699synchronized (table) {700MostRecentKeyValue recent = mostRecentKeyValue;701if ((recent != null) && (recent.key == key))702recent.value = null;703return table.remove(key);704}705}706707/**708* Returns the root ThreadGroup for all Threads contained within709* this AppContext.710* @since 1.2711*/712public ThreadGroup getThreadGroup() {713return threadGroup;714}715716/**717* Returns the context ClassLoader that was used to create this718* AppContext.719*720* @see java.lang.Thread#getContextClassLoader721*/722public ClassLoader getContextClassLoader() {723return contextClassLoader;724}725726/**727* Returns a string representation of this AppContext.728* @since 1.2729*/730@Override731public String toString() {732return getClass().getName() + "[threadGroup=" + threadGroup.getName() + "]";733}734735/**736* Returns an array of all the property change listeners737* registered on this component.738*739* @return all of this component's <code>PropertyChangeListener</code>s740* or an empty array if no property change741* listeners are currently registered742*743* @see #addPropertyChangeListener744* @see #removePropertyChangeListener745* @see #getPropertyChangeListeners(java.lang.String)746* @see java.beans.PropertyChangeSupport#getPropertyChangeListeners747* @since 1.4748*/749public synchronized PropertyChangeListener[] getPropertyChangeListeners() {750if (changeSupport == null) {751return new PropertyChangeListener[0];752}753return changeSupport.getPropertyChangeListeners();754}755756/**757* Adds a PropertyChangeListener to the listener list for a specific758* property. The specified property may be one of the following:759* <ul>760* <li>if this AppContext is disposed ("disposed")</li>761* </ul>762* <ul>763* <li>if this AppContext's unowned Windows have been disposed764* ("guidisposed"). Code to cleanup after the GUI is disposed765* (such as LookAndFeel.uninitialize()) should execute in response to766* this property being fired. Notifications for the "guidisposed"767* property are sent on the event dispatch thread.</li>768* </ul>769* <p>770* If listener is null, no exception is thrown and no action is performed.771*772* @param propertyName one of the property names listed above773* @param listener the PropertyChangeListener to be added774*775* @see #removePropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener)776* @see #getPropertyChangeListeners(java.lang.String)777* @see #addPropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener)778*/779public synchronized void addPropertyChangeListener(780String propertyName,781PropertyChangeListener listener) {782if (listener == null) {783return;784}785if (changeSupport == null) {786changeSupport = new PropertyChangeSupport(this);787}788changeSupport.addPropertyChangeListener(propertyName, listener);789}790791/**792* Removes a PropertyChangeListener from the listener list for a specific793* property. This method should be used to remove PropertyChangeListeners794* that were registered for a specific bound property.795* <p>796* If listener is null, no exception is thrown and no action is performed.797*798* @param propertyName a valid property name799* @param listener the PropertyChangeListener to be removed800*801* @see #addPropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener)802* @see #getPropertyChangeListeners(java.lang.String)803* @see #removePropertyChangeListener(java.beans.PropertyChangeListener)804*/805public synchronized void removePropertyChangeListener(806String propertyName,807PropertyChangeListener listener) {808if (listener == null || changeSupport == null) {809return;810}811changeSupport.removePropertyChangeListener(propertyName, listener);812}813814/**815* Returns an array of all the listeners which have been associated816* with the named property.817*818* @return all of the <code>PropertyChangeListeners</code> associated with819* the named property or an empty array if no listeners have820* been added821*822* @see #addPropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener)823* @see #removePropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener)824* @see #getPropertyChangeListeners825* @since 1.4826*/827public synchronized PropertyChangeListener[] getPropertyChangeListeners(828String propertyName) {829if (changeSupport == null) {830return new PropertyChangeListener[0];831}832return changeSupport.getPropertyChangeListeners(propertyName);833}834835// Set up JavaAWTAccess in SharedSecrets836static {837sun.misc.SharedSecrets.setJavaAWTAccess(new sun.misc.JavaAWTAccess() {838private boolean hasRootThreadGroup(final AppContext ecx) {839return AccessController.doPrivileged(new PrivilegedAction<Boolean>() {840@Override841public Boolean run() {842return ecx.threadGroup.getParent() == null;843}844});845}846847/**848* Returns the AppContext used for applet logging isolation, or null if849* the default global context can be used.850* If there's no applet, or if the caller is a stand alone application,851* or running in the main app context, returns null.852* Otherwise, returns the AppContext of the calling applet.853* @return null if the global default context can be used,854* an AppContext otherwise.855**/856public Object getAppletContext() {857// There's no AppContext: return null.858// No need to call getAppContext() if numAppContext == 0:859// it means that no AppContext has been created yet, and860// we don't want to trigger the creation of a main app861// context since we don't need it.862if (numAppContexts.get() == 0) return null;863864// Get the context from the security manager865AppContext ecx = getExecutionAppContext();866867// Not sure we really need to re-check numAppContexts here.868// If all applets have gone away then we could have a869// numAppContexts coming back to 0. So we recheck870// it here because we don't want to trigger the871// creation of a main AppContext in that case.872// This is probably not 100% MT-safe but should reduce873// the window of opportunity in which that issue could874// happen.875if (numAppContexts.get() > 0) {876// Defaults to thread group caching.877// This is probably not required as we only really need878// isolation in a deployed applet environment, in which879// case ecx will not be null when we reach here880// However it helps emulate the deployed environment,881// in tests for instance.882ecx = ecx != null ? ecx : getAppContext();883}884885// getAppletContext() may be called when initializing the main886// app context - in which case mainAppContext will still be887// null. To work around this issue we simply use888// AppContext.threadGroup.getParent() == null instead, since889// mainAppContext is the only AppContext which should have890// the root TG as its thread group.891// See: JDK-8023258892final boolean isMainAppContext = ecx == null893|| mainAppContext == ecx894|| mainAppContext == null && hasRootThreadGroup(ecx);895896return isMainAppContext ? null : ecx;897}898899});900}901902public static <T> T getSoftReferenceValue(Object key,903Supplier<T> supplier) {904905final AppContext appContext = AppContext.getAppContext();906SoftReference<T> ref = (SoftReference<T>) appContext.get(key);907if (ref != null) {908final T object = ref.get();909if (object != null) {910return object;911}912}913final T object = supplier.get();914ref = new SoftReference<>(object);915appContext.put(key, ref);916return object;917}918}919920final class MostRecentKeyValue {921Object key;922Object value;923MostRecentKeyValue(Object k, Object v) {924key = k;925value = v;926}927void setPair(Object k, Object v) {928key = k;929value = v;930}931}932933934