< prev index next >

src/java.desktop/share/classes/sun/awt/AppContext.java

Print this page




  54 import java.util.concurrent.atomic.AtomicInteger;
  55 import java.util.function.Supplier;
  56 
  57 /**
  58  * The AppContext is a table referenced by ThreadGroup which stores
  59  * application service instances.  (If you are not writing an application
  60  * service, or don't know what one is, please do not use this class.)
  61  * The AppContext allows applet access to what would otherwise be
  62  * potentially dangerous services, such as the ability to peek at
  63  * EventQueues or change the look-and-feel of a Swing application.<p>
  64  *
  65  * Most application services use a singleton object to provide their
  66  * services, either as a default (such as getSystemEventQueue or
  67  * getDefaultToolkit) or as static methods with class data (System).
  68  * The AppContext works with the former method by extending the concept
  69  * of "default" to be ThreadGroup-specific.  Application services
  70  * lookup their singleton in the AppContext.<p>
  71  *
  72  * For example, here we have a Foo service, with its pre-AppContext
  73  * code:<p>
  74  * <code><pre>
  75  *    public class Foo {
  76  *        private static Foo defaultFoo = new Foo();
  77  *
  78  *        public static Foo getDefaultFoo() {
  79  *            return defaultFoo;
  80  *        }
  81  *
  82  *    ... Foo service methods
  83  *    }</pre></code><p>

  84  *
  85  * The problem with the above is that the Foo service is global in scope,
  86  * so that applets and other untrusted code can execute methods on the
  87  * single, shared Foo instance.  The Foo service therefore either needs
  88  * to block its use by untrusted code using a SecurityManager test, or
  89  * restrict its capabilities so that it doesn't matter if untrusted code
  90  * executes it.<p>
  91  *
  92  * Here's the Foo class written to use the AppContext:<p>
  93  * <code><pre>
  94  *    public class Foo {
  95  *        public static Foo getDefaultFoo() {
  96  *            Foo foo = (Foo)AppContext.getAppContext().get(Foo.class);
  97  *            if (foo == null) {
  98  *                foo = new Foo();
  99  *                getAppContext().put(Foo.class, foo);
 100  *            }
 101  *            return foo;
 102  *        }
 103  *
 104  *    ... Foo service methods
 105  *    }</pre></code><p>

 106  *
 107  * Since a separate AppContext can exist for each ThreadGroup, trusted
 108  * and untrusted code have access to different Foo instances.  This allows
 109  * untrusted code access to "system-wide" services -- the service remains
 110  * within the AppContext "sandbox".  For example, say a malicious applet
 111  * wants to peek all of the key events on the EventQueue to listen for
 112  * passwords; if separate EventQueues are used for each ThreadGroup
 113  * using AppContexts, the only key events that applet will be able to
 114  * listen to are its own.  A more reasonable applet request would be to
 115  * change the Swing default look-and-feel; with that default stored in
 116  * an AppContext, the applet's look-and-feel will change without
 117  * disrupting other applets or potentially the browser itself.<p>
 118  *
 119  * Because the AppContext is a facility for safely extending application
 120  * service support to applets, none of its methods may be blocked by a
 121  * a SecurityManager check in a valid Java implementation.  Applets may
 122  * therefore safely invoke any of its methods without worry of being
 123  * blocked.
 124  *
 125  * Note: If a SecurityManager is installed which derives from


 142     /* Since the contents of an AppContext are unique to each Java
 143      * session, this class should never be serialized. */
 144 
 145     /*
 146      * The key to put()/get() the Java EventQueue into/from the AppContext.
 147      */
 148     public static final Object EVENT_QUEUE_KEY = new StringBuffer("EventQueue");
 149 
 150     /*
 151      * The keys to store EventQueue push/pop lock and condition.
 152      */
 153     public static final Object EVENT_QUEUE_LOCK_KEY = new StringBuilder("EventQueue.Lock");
 154     public static final Object EVENT_QUEUE_COND_KEY = new StringBuilder("EventQueue.Condition");
 155 
 156     /* A map of AppContexts, referenced by ThreadGroup.
 157      */
 158     private static final Map<ThreadGroup, AppContext> threadGroup2appContext =
 159             Collections.synchronizedMap(new IdentityHashMap<ThreadGroup, AppContext>());
 160 
 161     /**
 162      * Returns a set containing all <code>AppContext</code>s.
 163      */
 164     public static Set<AppContext> getAppContexts() {
 165         synchronized (threadGroup2appContext) {
 166             return new HashSet<AppContext>(threadGroup2appContext.values());
 167         }
 168     }
 169 
 170     /* The main "system" AppContext, used by everything not otherwise
 171        contained in another AppContext. It is implicitly created for
 172        standalone apps only (i.e. not applets)
 173      */
 174     private static volatile AppContext mainAppContext = null;
 175 
 176     private static class GetAppContextLock {};
 177     private static final Object getAppContextLock = new GetAppContextLock();
 178 
 179     /*
 180      * The hash map associated with this AppContext.  A private delegate
 181      * is used instead of subclassing HashMap so as to avoid all of
 182      * HashMap's potentially risky methods, such as clear(), elements(),
 183      * putAll(), etc.
 184      */
 185     private final Map<Object, Object> table = new HashMap<>();
 186 
 187     private final ThreadGroup threadGroup;
 188 
 189     /**
 190      * If any <code>PropertyChangeListeners</code> have been registered,
 191      * the <code>changeSupport</code> field describes them.
 192      *
 193      * @see #addPropertyChangeListener
 194      * @see #removePropertyChangeListener
 195      * @see PropertyChangeSupport#firePropertyChange
 196      */
 197     private PropertyChangeSupport changeSupport = null;
 198 
 199     public static final String DISPOSED_PROPERTY_NAME = "disposed";
 200     public static final String GUI_DISPOSED = "guidisposed";
 201 
 202     private enum State {
 203         VALID,
 204         BEING_DISPOSED,
 205         DISPOSED
 206     };
 207 
 208     private volatile State state = State.VALID;
 209 
 210     public boolean isDisposed() {
 211         return state == State.DISPOSED;


 613             if (appContext != AppContext.getAppContext()) {
 614                 // Create a thread that belongs to the thread group associated
 615                 // with the AppContext and invokes EventQueue.postEvent.
 616                 PrivilegedAction<Thread> action = new CreateThreadAction(appContext, r);
 617                 Thread thread = AccessController.doPrivileged(action);
 618                 thread.start();
 619             } else {
 620                 r.run();
 621             }
 622         }
 623     }
 624 
 625     private MostRecentKeyValue mostRecentKeyValue = null;
 626     private MostRecentKeyValue shadowMostRecentKeyValue = null;
 627 
 628     /**
 629      * Returns the value to which the specified key is mapped in this context.
 630      *
 631      * @param   key   a key in the AppContext.
 632      * @return  the value to which the key is mapped in this AppContext;
 633      *          <code>null</code> if the key is not mapped to any value.
 634      * @see     #put(Object, Object)
 635      * @since   1.2
 636      */
 637     public Object get(Object key) {
 638         /*
 639          * The most recent reference should be updated inside a synchronized
 640          * block to avoid a race when put() and get() are executed in
 641          * parallel on different threads.
 642          */
 643         synchronized (table) {
 644             // Note: this most recent key/value caching is thread-hot.
 645             // A simple test using SwingSet found that 72% of lookups
 646             // were matched using the most recent key/value.  By instantiating
 647             // a simple MostRecentKeyValue object on cache misses, the
 648             // cache hits can be processed without synchronization.
 649 
 650             MostRecentKeyValue recent = mostRecentKeyValue;
 651             if ((recent != null) && (recent.key == key)) {
 652                 return recent.value;
 653             }
 654 
 655             Object value = table.get(key);
 656             if(mostRecentKeyValue == null) {
 657                 mostRecentKeyValue = new MostRecentKeyValue(key, value);
 658                 shadowMostRecentKeyValue = new MostRecentKeyValue(key, value);
 659             } else {
 660                 MostRecentKeyValue auxKeyValue = mostRecentKeyValue;
 661                 shadowMostRecentKeyValue.setPair(key, value);
 662                 mostRecentKeyValue = shadowMostRecentKeyValue;
 663                 shadowMostRecentKeyValue = auxKeyValue;
 664             }
 665             return value;
 666         }
 667     }
 668 
 669     /**
 670      * Maps the specified <code>key</code> to the specified
 671      * <code>value</code> in this AppContext.  Neither the key nor the
 672      * value can be <code>null</code>.
 673      * <p>
 674      * The value can be retrieved by calling the <code>get</code> method
 675      * with a key that is equal to the original key.
 676      *
 677      * @param      key     the AppContext key.
 678      * @param      value   the value.
 679      * @return     the previous value of the specified key in this
 680      *             AppContext, or <code>null</code> if it did not have one.
 681      * @exception  NullPointerException  if the key or value is
 682      *               <code>null</code>.
 683      * @see     #get(Object)
 684      * @since   1.2
 685      */
 686     public Object put(Object key, Object value) {
 687         synchronized (table) {
 688             MostRecentKeyValue recent = mostRecentKeyValue;
 689             if ((recent != null) && (recent.key == key))
 690                 recent.value = value;
 691             return table.put(key, value);
 692         }
 693     }
 694 
 695     /**
 696      * Removes the key (and its corresponding value) from this
 697      * AppContext. This method does nothing if the key is not in the
 698      * AppContext.
 699      *
 700      * @param   key   the key that needs to be removed.
 701      * @return  the value to which the key had been mapped in this AppContext,
 702      *          or <code>null</code> if the key did not have a mapping.
 703      * @since   1.2
 704      */
 705     public Object remove(Object key) {
 706         synchronized (table) {
 707             MostRecentKeyValue recent = mostRecentKeyValue;
 708             if ((recent != null) && (recent.key == key))
 709                 recent.value = null;
 710             return table.remove(key);
 711         }
 712     }
 713 
 714     /**
 715      * Returns the root ThreadGroup for all Threads contained within
 716      * this AppContext.
 717      * @since   1.2
 718      */
 719     public ThreadGroup getThreadGroup() {
 720         return threadGroup;
 721     }
 722 


 726      *
 727      * @see java.lang.Thread#getContextClassLoader
 728      */
 729     public ClassLoader getContextClassLoader() {
 730         return contextClassLoader;
 731     }
 732 
 733     /**
 734      * Returns a string representation of this AppContext.
 735      * @since   1.2
 736      */
 737     @Override
 738     public String toString() {
 739         return getClass().getName() + "[threadGroup=" + threadGroup.getName() + "]";
 740     }
 741 
 742     /**
 743      * Returns an array of all the property change listeners
 744      * registered on this component.
 745      *
 746      * @return all of this component's <code>PropertyChangeListener</code>s
 747      *         or an empty array if no property change
 748      *         listeners are currently registered
 749      *
 750      * @see      #addPropertyChangeListener
 751      * @see      #removePropertyChangeListener
 752      * @see      #getPropertyChangeListeners(java.lang.String)
 753      * @see      java.beans.PropertyChangeSupport#getPropertyChangeListeners
 754      * @since    1.4
 755      */
 756     public synchronized PropertyChangeListener[] getPropertyChangeListeners() {
 757         if (changeSupport == null) {
 758             return new PropertyChangeListener[0];
 759         }
 760         return changeSupport.getPropertyChangeListeners();
 761     }
 762 
 763     /**
 764      * Adds a PropertyChangeListener to the listener list for a specific
 765      * property. The specified property may be one of the following:
 766      * <ul>


 805      * @param propertyName a valid property name
 806      * @param listener the PropertyChangeListener to be removed
 807      *
 808      * @see #addPropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener)
 809      * @see #getPropertyChangeListeners(java.lang.String)
 810      * @see PropertyChangeSupport#removePropertyChangeListener(java.beans.PropertyChangeListener)
 811      */
 812     public synchronized void removePropertyChangeListener(
 813                              String propertyName,
 814                              PropertyChangeListener listener) {
 815         if (listener == null || changeSupport == null) {
 816             return;
 817         }
 818         changeSupport.removePropertyChangeListener(propertyName, listener);
 819     }
 820 
 821     /**
 822      * Returns an array of all the listeners which have been associated
 823      * with the named property.
 824      *
 825      * @return all of the <code>PropertyChangeListeners</code> associated with
 826      *         the named property or an empty array if no listeners have
 827      *         been added
 828      *
 829      * @see #addPropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener)
 830      * @see #removePropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener)
 831      * @see #getPropertyChangeListeners
 832      * @since 1.4
 833      */
 834     public synchronized PropertyChangeListener[] getPropertyChangeListeners(
 835                                                         String propertyName) {
 836         if (changeSupport == null) {
 837             return new PropertyChangeListener[0];
 838         }
 839         return changeSupport.getPropertyChangeListeners(propertyName);
 840     }
 841 
 842     // Set up JavaAWTAccess in SharedSecrets
 843     static {
 844         SharedSecrets.setJavaAWTAccess(new JavaAWTAccess() {
 845             private boolean hasRootThreadGroup(final AppContext ecx) {




  54 import java.util.concurrent.atomic.AtomicInteger;
  55 import java.util.function.Supplier;
  56 
  57 /**
  58  * The AppContext is a table referenced by ThreadGroup which stores
  59  * application service instances.  (If you are not writing an application
  60  * service, or don't know what one is, please do not use this class.)
  61  * The AppContext allows applet access to what would otherwise be
  62  * potentially dangerous services, such as the ability to peek at
  63  * EventQueues or change the look-and-feel of a Swing application.<p>
  64  *
  65  * Most application services use a singleton object to provide their
  66  * services, either as a default (such as getSystemEventQueue or
  67  * getDefaultToolkit) or as static methods with class data (System).
  68  * The AppContext works with the former method by extending the concept
  69  * of "default" to be ThreadGroup-specific.  Application services
  70  * lookup their singleton in the AppContext.<p>
  71  *
  72  * For example, here we have a Foo service, with its pre-AppContext
  73  * code:<p>
  74  * <pre>{@code
  75  *    public class Foo {
  76  *        private static Foo defaultFoo = new Foo();
  77  *
  78  *        public static Foo getDefaultFoo() {
  79  *            return defaultFoo;
  80  *        }
  81  *
  82  *    ... Foo service methods
  83  *    }
  84  * }</pre><p>
  85  *
  86  * The problem with the above is that the Foo service is global in scope,
  87  * so that applets and other untrusted code can execute methods on the
  88  * single, shared Foo instance.  The Foo service therefore either needs
  89  * to block its use by untrusted code using a SecurityManager test, or
  90  * restrict its capabilities so that it doesn't matter if untrusted code
  91  * executes it.<p>
  92  *
  93  * Here's the Foo class written to use the AppContext:<p>
  94  * <pre>{@code
  95  *    public class Foo {
  96  *        public static Foo getDefaultFoo() {
  97  *            Foo foo = (Foo)AppContext.getAppContext().get(Foo.class);
  98  *            if (foo == null) {
  99  *                foo = new Foo();
 100  *                getAppContext().put(Foo.class, foo);
 101  *            }
 102  *            return foo;
 103  *        }
 104  *
 105  *    ... Foo service methods
 106  *    }
 107  * }</pre><p>
 108  *
 109  * Since a separate AppContext can exist for each ThreadGroup, trusted
 110  * and untrusted code have access to different Foo instances.  This allows
 111  * untrusted code access to "system-wide" services -- the service remains
 112  * within the AppContext "sandbox".  For example, say a malicious applet
 113  * wants to peek all of the key events on the EventQueue to listen for
 114  * passwords; if separate EventQueues are used for each ThreadGroup
 115  * using AppContexts, the only key events that applet will be able to
 116  * listen to are its own.  A more reasonable applet request would be to
 117  * change the Swing default look-and-feel; with that default stored in
 118  * an AppContext, the applet's look-and-feel will change without
 119  * disrupting other applets or potentially the browser itself.<p>
 120  *
 121  * Because the AppContext is a facility for safely extending application
 122  * service support to applets, none of its methods may be blocked by a
 123  * a SecurityManager check in a valid Java implementation.  Applets may
 124  * therefore safely invoke any of its methods without worry of being
 125  * blocked.
 126  *
 127  * Note: If a SecurityManager is installed which derives from


 144     /* Since the contents of an AppContext are unique to each Java
 145      * session, this class should never be serialized. */
 146 
 147     /*
 148      * The key to put()/get() the Java EventQueue into/from the AppContext.
 149      */
 150     public static final Object EVENT_QUEUE_KEY = new StringBuffer("EventQueue");
 151 
 152     /*
 153      * The keys to store EventQueue push/pop lock and condition.
 154      */
 155     public static final Object EVENT_QUEUE_LOCK_KEY = new StringBuilder("EventQueue.Lock");
 156     public static final Object EVENT_QUEUE_COND_KEY = new StringBuilder("EventQueue.Condition");
 157 
 158     /* A map of AppContexts, referenced by ThreadGroup.
 159      */
 160     private static final Map<ThreadGroup, AppContext> threadGroup2appContext =
 161             Collections.synchronizedMap(new IdentityHashMap<ThreadGroup, AppContext>());
 162 
 163     /**
 164      * Returns a set containing all {@code AppContext}s.
 165      */
 166     public static Set<AppContext> getAppContexts() {
 167         synchronized (threadGroup2appContext) {
 168             return new HashSet<AppContext>(threadGroup2appContext.values());
 169         }
 170     }
 171 
 172     /* The main "system" AppContext, used by everything not otherwise
 173        contained in another AppContext. It is implicitly created for
 174        standalone apps only (i.e. not applets)
 175      */
 176     private static volatile AppContext mainAppContext = null;
 177 
 178     private static class GetAppContextLock {};
 179     private static final Object getAppContextLock = new GetAppContextLock();
 180 
 181     /*
 182      * The hash map associated with this AppContext.  A private delegate
 183      * is used instead of subclassing HashMap so as to avoid all of
 184      * HashMap's potentially risky methods, such as clear(), elements(),
 185      * putAll(), etc.
 186      */
 187     private final Map<Object, Object> table = new HashMap<>();
 188 
 189     private final ThreadGroup threadGroup;
 190 
 191     /**
 192      * If any {@code PropertyChangeListeners} have been registered,
 193      * the {@code changeSupport} field describes them.
 194      *
 195      * @see #addPropertyChangeListener
 196      * @see #removePropertyChangeListener
 197      * @see PropertyChangeSupport#firePropertyChange
 198      */
 199     private PropertyChangeSupport changeSupport = null;
 200 
 201     public static final String DISPOSED_PROPERTY_NAME = "disposed";
 202     public static final String GUI_DISPOSED = "guidisposed";
 203 
 204     private enum State {
 205         VALID,
 206         BEING_DISPOSED,
 207         DISPOSED
 208     };
 209 
 210     private volatile State state = State.VALID;
 211 
 212     public boolean isDisposed() {
 213         return state == State.DISPOSED;


 615             if (appContext != AppContext.getAppContext()) {
 616                 // Create a thread that belongs to the thread group associated
 617                 // with the AppContext and invokes EventQueue.postEvent.
 618                 PrivilegedAction<Thread> action = new CreateThreadAction(appContext, r);
 619                 Thread thread = AccessController.doPrivileged(action);
 620                 thread.start();
 621             } else {
 622                 r.run();
 623             }
 624         }
 625     }
 626 
 627     private MostRecentKeyValue mostRecentKeyValue = null;
 628     private MostRecentKeyValue shadowMostRecentKeyValue = null;
 629 
 630     /**
 631      * Returns the value to which the specified key is mapped in this context.
 632      *
 633      * @param   key   a key in the AppContext.
 634      * @return  the value to which the key is mapped in this AppContext;
 635      *          {@code null} if the key is not mapped to any value.
 636      * @see     #put(Object, Object)
 637      * @since   1.2
 638      */
 639     public Object get(Object key) {
 640         /*
 641          * The most recent reference should be updated inside a synchronized
 642          * block to avoid a race when put() and get() are executed in
 643          * parallel on different threads.
 644          */
 645         synchronized (table) {
 646             // Note: this most recent key/value caching is thread-hot.
 647             // A simple test using SwingSet found that 72% of lookups
 648             // were matched using the most recent key/value.  By instantiating
 649             // a simple MostRecentKeyValue object on cache misses, the
 650             // cache hits can be processed without synchronization.
 651 
 652             MostRecentKeyValue recent = mostRecentKeyValue;
 653             if ((recent != null) && (recent.key == key)) {
 654                 return recent.value;
 655             }
 656 
 657             Object value = table.get(key);
 658             if(mostRecentKeyValue == null) {
 659                 mostRecentKeyValue = new MostRecentKeyValue(key, value);
 660                 shadowMostRecentKeyValue = new MostRecentKeyValue(key, value);
 661             } else {
 662                 MostRecentKeyValue auxKeyValue = mostRecentKeyValue;
 663                 shadowMostRecentKeyValue.setPair(key, value);
 664                 mostRecentKeyValue = shadowMostRecentKeyValue;
 665                 shadowMostRecentKeyValue = auxKeyValue;
 666             }
 667             return value;
 668         }
 669     }
 670 
 671     /**
 672      * Maps the specified {@code key} to the specified
 673      * {@code value} in this AppContext.  Neither the key nor the
 674      * value can be {@code null}.
 675      * <p>
 676      * The value can be retrieved by calling the {@code get} method
 677      * with a key that is equal to the original key.
 678      *
 679      * @param      key     the AppContext key.
 680      * @param      value   the value.
 681      * @return     the previous value of the specified key in this
 682      *             AppContext, or {@code null} if it did not have one.
 683      * @exception  NullPointerException  if the key or value is
 684      *               {@code null}.
 685      * @see     #get(Object)
 686      * @since   1.2
 687      */
 688     public Object put(Object key, Object value) {
 689         synchronized (table) {
 690             MostRecentKeyValue recent = mostRecentKeyValue;
 691             if ((recent != null) && (recent.key == key))
 692                 recent.value = value;
 693             return table.put(key, value);
 694         }
 695     }
 696 
 697     /**
 698      * Removes the key (and its corresponding value) from this
 699      * AppContext. This method does nothing if the key is not in the
 700      * AppContext.
 701      *
 702      * @param   key   the key that needs to be removed.
 703      * @return  the value to which the key had been mapped in this AppContext,
 704      *          or {@code null} if the key did not have a mapping.
 705      * @since   1.2
 706      */
 707     public Object remove(Object key) {
 708         synchronized (table) {
 709             MostRecentKeyValue recent = mostRecentKeyValue;
 710             if ((recent != null) && (recent.key == key))
 711                 recent.value = null;
 712             return table.remove(key);
 713         }
 714     }
 715 
 716     /**
 717      * Returns the root ThreadGroup for all Threads contained within
 718      * this AppContext.
 719      * @since   1.2
 720      */
 721     public ThreadGroup getThreadGroup() {
 722         return threadGroup;
 723     }
 724 


 728      *
 729      * @see java.lang.Thread#getContextClassLoader
 730      */
 731     public ClassLoader getContextClassLoader() {
 732         return contextClassLoader;
 733     }
 734 
 735     /**
 736      * Returns a string representation of this AppContext.
 737      * @since   1.2
 738      */
 739     @Override
 740     public String toString() {
 741         return getClass().getName() + "[threadGroup=" + threadGroup.getName() + "]";
 742     }
 743 
 744     /**
 745      * Returns an array of all the property change listeners
 746      * registered on this component.
 747      *
 748      * @return all of this component's {@code PropertyChangeListener}s
 749      *         or an empty array if no property change
 750      *         listeners are currently registered
 751      *
 752      * @see      #addPropertyChangeListener
 753      * @see      #removePropertyChangeListener
 754      * @see      #getPropertyChangeListeners(java.lang.String)
 755      * @see      java.beans.PropertyChangeSupport#getPropertyChangeListeners
 756      * @since    1.4
 757      */
 758     public synchronized PropertyChangeListener[] getPropertyChangeListeners() {
 759         if (changeSupport == null) {
 760             return new PropertyChangeListener[0];
 761         }
 762         return changeSupport.getPropertyChangeListeners();
 763     }
 764 
 765     /**
 766      * Adds a PropertyChangeListener to the listener list for a specific
 767      * property. The specified property may be one of the following:
 768      * <ul>


 807      * @param propertyName a valid property name
 808      * @param listener the PropertyChangeListener to be removed
 809      *
 810      * @see #addPropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener)
 811      * @see #getPropertyChangeListeners(java.lang.String)
 812      * @see PropertyChangeSupport#removePropertyChangeListener(java.beans.PropertyChangeListener)
 813      */
 814     public synchronized void removePropertyChangeListener(
 815                              String propertyName,
 816                              PropertyChangeListener listener) {
 817         if (listener == null || changeSupport == null) {
 818             return;
 819         }
 820         changeSupport.removePropertyChangeListener(propertyName, listener);
 821     }
 822 
 823     /**
 824      * Returns an array of all the listeners which have been associated
 825      * with the named property.
 826      *
 827      * @return all of the {@code PropertyChangeListeners} associated with
 828      *         the named property or an empty array if no listeners have
 829      *         been added
 830      *
 831      * @see #addPropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener)
 832      * @see #removePropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener)
 833      * @see #getPropertyChangeListeners
 834      * @since 1.4
 835      */
 836     public synchronized PropertyChangeListener[] getPropertyChangeListeners(
 837                                                         String propertyName) {
 838         if (changeSupport == null) {
 839             return new PropertyChangeListener[0];
 840         }
 841         return changeSupport.getPropertyChangeListeners(propertyName);
 842     }
 843 
 844     // Set up JavaAWTAccess in SharedSecrets
 845     static {
 846         SharedSecrets.setJavaAWTAccess(new JavaAWTAccess() {
 847             private boolean hasRootThreadGroup(final AppContext ecx) {


< prev index next >