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) {
|