diff --git a/src/share/classes/java/beans/Beans.java b/src/share/classes/java/beans/Beans.java index b71a2ac5a4d3b5d6f57a45e3cbcf9d6f1aa96188..03f0dc8d76e842233b488c508f33b6572ecda815 100644 --- a/src/share/classes/java/beans/Beans.java +++ b/src/share/classes/java/beans/Beans.java @@ -32,7 +32,6 @@ import java.applet.AppletContext; import java.applet.AppletStub; import java.applet.AudioClip; -import java.awt.GraphicsEnvironment; import java.awt.Image; import java.beans.beancontext.BeanContext; @@ -53,15 +52,11 @@ import java.util.Hashtable; import java.util.Iterator; import java.util.Vector; -import sun.awt.AppContext; - /** * This class provides some general purpose beans control methods. */ public class Beans { - private static final Object DESIGN_TIME = new Object(); - private static final Object GUI_AVAILABLE = new Object(); /** *

@@ -395,8 +390,7 @@ public class Beans { * @see DesignMode */ public static boolean isDesignTime() { - Object value = AppContext.getAppContext().get(DESIGN_TIME); - return (value instanceof Boolean) && (Boolean) value; + return ThreadGroupContext.getContext().isDesignTime(); } /** @@ -413,8 +407,7 @@ public class Beans { * */ public static boolean isGuiAvailable() { - Object value = AppContext.getAppContext().get(GUI_AVAILABLE); - return (value instanceof Boolean) ? (Boolean) value : !GraphicsEnvironment.isHeadless(); + return ThreadGroupContext.getContext().isGuiAvailable(); } /** @@ -440,7 +433,7 @@ public class Beans { if (sm != null) { sm.checkPropertiesAccess(); } - AppContext.getAppContext().put(DESIGN_TIME, Boolean.valueOf(isDesignTime)); + ThreadGroupContext.getContext().setDesignTime(isDesignTime); } /** @@ -466,7 +459,7 @@ public class Beans { if (sm != null) { sm.checkPropertiesAccess(); } - AppContext.getAppContext().put(GUI_AVAILABLE, Boolean.valueOf(isGuiAvailable)); + ThreadGroupContext.getContext().setGuiAvailable(isGuiAvailable); } } diff --git a/src/share/classes/java/beans/Introspector.java b/src/share/classes/java/beans/Introspector.java index 36ead726bd43a4a1382be5db5d412f810c2b87ae..2d85beef4c8b349ad1b61c70866843e53f76c5d1 100644 --- a/src/share/classes/java/beans/Introspector.java +++ b/src/share/classes/java/beans/Introspector.java @@ -26,7 +26,6 @@ package java.beans; import com.sun.beans.WeakCache; -import com.sun.beans.finder.BeanInfoFinder; import com.sun.beans.finder.ClassFinder; import java.awt.Component; @@ -44,9 +43,7 @@ import java.util.EventListener; import java.util.EventObject; import java.util.List; import java.util.TreeMap; -import java.util.WeakHashMap; -import sun.awt.AppContext; import sun.reflect.misc.ReflectUtil; /** @@ -98,10 +95,7 @@ public class Introspector { public final static int IGNORE_ALL_BEANINFO = 3; // Static Caches to speed up introspection. - private static WeakCache, Method[]> declaredMethodCache = - new WeakCache, Method[]>(); - - private static final Object BEANINFO_CACHE = new Object(); + private static final WeakCache, Method[]> declaredMethodCache = new WeakCache<>(); private Class beanClass; private BeanInfo explicitBeanInfo; @@ -134,8 +128,6 @@ public class Introspector { static final String SET_PREFIX = "set"; static final String IS_PREFIX = "is"; - private static final Object FINDER_KEY = new Object(); - //====================================================================== // Public methods //====================================================================== @@ -160,20 +152,15 @@ public class Introspector { if (!ReflectUtil.isPackageAccessible(beanClass)) { return (new Introspector(beanClass, null, USE_ALL_BEANINFO)).getBeanInfo(); } - Map, BeanInfo> beanInfoCache; + ThreadGroupContext context = ThreadGroupContext.getContext(); BeanInfo beanInfo; - synchronized (BEANINFO_CACHE) { - beanInfoCache = (Map, BeanInfo>) AppContext.getAppContext().get(BEANINFO_CACHE); - if (beanInfoCache == null) { - beanInfoCache = new WeakHashMap, BeanInfo>(); - AppContext.getAppContext().put(BEANINFO_CACHE, beanInfoCache); - } - beanInfo = beanInfoCache.get(beanClass); + synchronized (declaredMethodCache) { + beanInfo = context.getBeanInfo(beanClass); } if (beanInfo == null) { beanInfo = new Introspector(beanClass, null, USE_ALL_BEANINFO).getBeanInfo(); - synchronized (BEANINFO_CACHE) { - beanInfoCache.put(beanClass, beanInfo); + synchronized (declaredMethodCache) { + context.putBeanInfo(beanClass, beanInfo); } } return beanInfo; @@ -306,7 +293,7 @@ public class Introspector { */ public static String[] getBeanInfoSearchPath() { - return getFinder().getPackages(); + return ThreadGroupContext.getContext().getBeanInfoFinder().getPackages(); } /** @@ -330,7 +317,7 @@ public class Introspector { if (sm != null) { sm.checkPropertiesAccess(); } - getFinder().setPackages(path); + ThreadGroupContext.getContext().getBeanInfoFinder().setPackages(path); } @@ -342,11 +329,8 @@ public class Introspector { */ public static void flushCaches() { - synchronized (BEANINFO_CACHE) { - Map beanInfoCache = (Map) AppContext.getAppContext().get(BEANINFO_CACHE); - if (beanInfoCache != null) { - beanInfoCache.clear(); - } + synchronized (declaredMethodCache) { + ThreadGroupContext.getContext().clearBeanInfoCache(); declaredMethodCache.clear(); } } @@ -370,11 +354,8 @@ public class Introspector { if (clz == null) { throw new NullPointerException(); } - synchronized (BEANINFO_CACHE) { - Map beanInfoCache = (Map) AppContext.getAppContext().get(BEANINFO_CACHE); - if (beanInfoCache != null) { - beanInfoCache.put(clz, null); - } + synchronized (declaredMethodCache) { + ThreadGroupContext.getContext().removeBeanInfo(clz); declaredMethodCache.put(clz, null); } } @@ -452,7 +433,7 @@ public class Introspector { * @return Instance of an explicit BeanInfo class or null if one isn't found. */ private static BeanInfo findExplicitBeanInfo(Class beanClass) { - return getFinder().find(beanClass); + return ThreadGroupContext.getContext().getBeanInfoFinder().find(beanClass); } /** @@ -1275,7 +1256,7 @@ public class Introspector { if (!ReflectUtil.isPackageAccessible(clz)) { return new Method[0]; } - synchronized (BEANINFO_CACHE) { + synchronized (declaredMethodCache) { Method[] result = declaredMethodCache.get(clz); if (result == null) { result = clz.getMethods(); @@ -1426,17 +1407,6 @@ public class Introspector { return false; } - private static BeanInfoFinder getFinder() { - AppContext context = AppContext.getAppContext(); - Object object = context.get(FINDER_KEY); - if (object instanceof BeanInfoFinder) { - return (BeanInfoFinder) object; - } - BeanInfoFinder finder = new BeanInfoFinder(); - context.put(FINDER_KEY, finder); - return finder; - } - /** * Try to create an instance of a named class. * First try the classloader of "sibling", then try the system diff --git a/src/share/classes/java/beans/PropertyEditorManager.java b/src/share/classes/java/beans/PropertyEditorManager.java index 6bdfa95f41b786c49b76913b3a1865a653ec4dd2..3fee3f40887277e7a7715c49cb71e7abc048d49b 100644 --- a/src/share/classes/java/beans/PropertyEditorManager.java +++ b/src/share/classes/java/beans/PropertyEditorManager.java @@ -25,9 +25,6 @@ package java.beans; -import com.sun.beans.finder.PropertyEditorFinder; -import sun.awt.AppContext; - /** * The PropertyEditorManager can be used to locate a property editor for * any given type name. This property editor must support the @@ -55,8 +52,6 @@ import sun.awt.AppContext; public class PropertyEditorManager { - private static final Object FINDER_KEY = new Object(); - /** * Registers an editor class to edit values of the given target class. * If the editor class is {@code null}, @@ -81,7 +76,7 @@ public class PropertyEditorManager { if (sm != null) { sm.checkPropertiesAccess(); } - getFinder().register(targetType, editorClass); + ThreadGroupContext.getContext().getPropertyEditorFinder().register(targetType, editorClass); } /** @@ -92,7 +87,7 @@ public class PropertyEditorManager { * The result is null if no suitable editor can be found. */ public static PropertyEditor findEditor(Class targetType) { - return getFinder().find(targetType); + return ThreadGroupContext.getContext().getPropertyEditorFinder().find(targetType); } /** @@ -104,7 +99,7 @@ public class PropertyEditorManager { * e.g. Sun implementation initially sets to {"sun.beans.editors"}. */ public static String[] getEditorSearchPath() { - return getFinder().getPackages(); + return ThreadGroupContext.getContext().getPropertyEditorFinder().getPackages(); } /** @@ -125,17 +120,6 @@ public class PropertyEditorManager { if (sm != null) { sm.checkPropertiesAccess(); } - getFinder().setPackages(path); - } - - private static PropertyEditorFinder getFinder() { - AppContext context = AppContext.getAppContext(); - Object object = context.get(FINDER_KEY); - if (object instanceof PropertyEditorFinder) { - return (PropertyEditorFinder) object; - } - PropertyEditorFinder finder = new PropertyEditorFinder(); - context.put(FINDER_KEY, finder); - return finder; + ThreadGroupContext.getContext().getPropertyEditorFinder().setPackages(path); } } diff --git a/src/share/classes/java/beans/ThreadGroupContext.java b/src/share/classes/java/beans/ThreadGroupContext.java new file mode 100644 index 0000000000000000000000000000000000000000..dc1d38a1457705a695c4f19e49b9824efbf66fa8 --- /dev/null +++ b/src/share/classes/java/beans/ThreadGroupContext.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.beans; + +import com.sun.beans.finder.BeanInfoFinder; +import com.sun.beans.finder.PropertyEditorFinder; + +import java.awt.GraphicsEnvironment; +import java.util.HashMap; +import java.util.Map; +import java.util.WeakHashMap; + +/** + * The {@code ThreadGroupContext} is an application-dependent + * context referenced by the specific {@link ThreadGroup}. + * This is a replacement for the {@link sun.awt.AppContext}. + * + * @author Sergey Malenkov + */ +final class ThreadGroupContext { + + private static final Map contexts = new WeakHashMap<>(); + + /** + * Returns the appropriate {@code AppContext} for the caller, + * as determined by its {@code ThreadGroup}. + * + * @return the application-dependent context + */ + static ThreadGroupContext getContext() { + ThreadGroup group = Thread.currentThread().getThreadGroup(); + synchronized (contexts) { + ThreadGroupContext context = contexts.get(group); + if (context == null) { + context = new ThreadGroupContext(); + contexts.put(group, context); + } + return context; + } + } + + private volatile boolean isDesignTime; + private volatile Boolean isGuiAvailable; + + private Map, BeanInfo> beanInfoCache; + private BeanInfoFinder beanInfoFinder; + private PropertyEditorFinder propertyEditorFinder; + + + boolean isDesignTime() { + return this.isDesignTime; + } + + void setDesignTime(boolean isDesignTime) { + this.isDesignTime = isDesignTime; + } + + + boolean isGuiAvailable() { + Boolean isGuiAvailable = this.isGuiAvailable; + return (isGuiAvailable != null) + ? isGuiAvailable.booleanValue() + : !GraphicsEnvironment.isHeadless(); + } + + void setGuiAvailable(boolean isGuiAvailable) { + this.isGuiAvailable = Boolean.valueOf(isGuiAvailable); + } + + + BeanInfo getBeanInfo(Class type) { + return (this.beanInfoCache != null) + ? this.beanInfoCache.get(type) + : null; + } + + BeanInfo putBeanInfo(Class type, BeanInfo info) { + if (this.beanInfoCache == null) { + this.beanInfoCache = new WeakHashMap<>(); + } + return this.beanInfoCache.put(type, info); + } + + void removeBeanInfo(Class type) { + if (this.beanInfoCache != null) { + this.beanInfoCache.remove(type); + } + } + + void clearBeanInfoCache() { + if (this.beanInfoCache != null) { + this.beanInfoCache.clear(); + } + } + + + synchronized BeanInfoFinder getBeanInfoFinder() { + if (this.beanInfoFinder == null) { + this.beanInfoFinder = new BeanInfoFinder(); + } + return this.beanInfoFinder; + } + + synchronized PropertyEditorFinder getPropertyEditorFinder() { + if (this.propertyEditorFinder == null) { + this.propertyEditorFinder = new PropertyEditorFinder(); + } + return this.propertyEditorFinder; + } +} diff --git a/test/java/beans/Beans/6669869/TestDesignTime.java b/test/java/beans/Beans/6669869/TestDesignTime.java index b73cee4bbfd0304055b74e30ae8622ab85fbd782..731ab4a9f4b6c4400ebfa8c72a68fe720e4430fd 100644 --- a/test/java/beans/Beans/6669869/TestDesignTime.java +++ b/test/java/beans/Beans/6669869/TestDesignTime.java @@ -29,7 +29,6 @@ */ import java.beans.Beans; -import sun.awt.SunToolkit; public class TestDesignTime implements Runnable { public static void main(String[] args) throws InterruptedException { @@ -44,7 +43,6 @@ public class TestDesignTime implements Runnable { } public void run() { - SunToolkit.createNewAppContext(); if (Beans.isDesignTime()) { throw new Error("shared DesignTime property"); } diff --git a/test/java/beans/Beans/6669869/TestGuiAvailable.java b/test/java/beans/Beans/6669869/TestGuiAvailable.java index 615688286b3866092c6b59103ca06188f9c01526..67d88ae34f5c8d8f0a8163f0038dd62215ab84f8 100644 --- a/test/java/beans/Beans/6669869/TestGuiAvailable.java +++ b/test/java/beans/Beans/6669869/TestGuiAvailable.java @@ -30,7 +30,6 @@ import java.awt.GraphicsEnvironment; import java.beans.Beans; -import sun.awt.SunToolkit; public class TestGuiAvailable implements Runnable { public static void main(String[] args) throws InterruptedException { @@ -45,7 +44,6 @@ public class TestGuiAvailable implements Runnable { } public void run() { - SunToolkit.createNewAppContext(); if (Beans.isGuiAvailable() == GraphicsEnvironment.isHeadless()) { throw new Error("shared GuiAvailable property"); } diff --git a/test/java/beans/Introspector/6380849/TestBeanInfo.java b/test/java/beans/Introspector/6380849/TestBeanInfo.java index 8232756d7bc36d72ed04f4e9db47aa4490836acf..6d685350412547578df372b0395573a7429d54f5 100644 --- a/test/java/beans/Introspector/6380849/TestBeanInfo.java +++ b/test/java/beans/Introspector/6380849/TestBeanInfo.java @@ -41,8 +41,6 @@ import java.beans.Introspector; import java.lang.ref.Reference; import java.lang.reflect.Field; -import sun.awt.SunToolkit; - public class TestBeanInfo implements Runnable { private static final String[] SEARCH_PATH = { "infos" }; // NON-NLS: package name @@ -81,9 +79,6 @@ public class TestBeanInfo implements Runnable { private boolean passed; public void run() { - if (this.passed) { - SunToolkit.createNewAppContext(); - } Introspector.flushCaches(); test(FirstBean.class, FirstBeanBeanInfo.class); @@ -98,7 +93,5 @@ public class TestBeanInfo implements Runnable { test(SecondBean.class, SecondBeanBeanInfo.class); test(ThirdBean.class, null); test(ThirdBeanBeanInfo.class, ThirdBeanBeanInfo.class); - - this.passed = true; } } diff --git a/test/java/beans/Introspector/7064279/Test7064279.java b/test/java/beans/Introspector/7064279/Test7064279.java new file mode 100644 index 0000000000000000000000000000000000000000..91d10fe7d4208f6440ebc6f74498cf60bb185915 --- /dev/null +++ b/test/java/beans/Introspector/7064279/Test7064279.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 7064279 + * @summary Tests that Introspector does not have strong references to context class loader + * @author Sergey Malenkov + */ + +import java.beans.Introspector; +import java.io.File; +import java.lang.ref.WeakReference; +import java.net.URL; +import java.net.URLClassLoader; + +public class Test7064279 { + + public static void main(String[] args) throws Exception { + WeakReference ref = new WeakReference(test("test.jar", "test.Test")); + try { + int[] array = new int[1024]; + while (true) { + array = new int[array.length << 1]; + } + } + catch (OutOfMemoryError error) { + System.gc(); + } + if (null != ref.get()) { + throw new Error("ClassLoader is not released"); + } + } + + private static Object test(String jarName, String className) throws Exception { + StringBuilder sb = new StringBuilder(256); + sb.append("file:"); + sb.append(System.getProperty("test.src", ".")); + sb.append(File.separatorChar); + sb.append(jarName); + + ClassLoader newLoader = new URLClassLoader(new URL[] { new URL(sb.toString()) }); + ClassLoader oldLoader = Thread.currentThread().getContextClassLoader(); + + Thread.currentThread().setContextClassLoader(newLoader); + test(newLoader.loadClass(className)); + Thread.currentThread().setContextClassLoader(oldLoader); + + return newLoader; + } + + private static void test(Class type) throws Exception { + Introspector.getBeanInfo(type); + } +} diff --git a/test/java/beans/Introspector/7064279/test.jar b/test/java/beans/Introspector/7064279/test.jar new file mode 100644 index 0000000000000000000000000000000000000000..7516a335f766541beeef3cf70ec4772be684cc14 Binary files /dev/null and b/test/java/beans/Introspector/7064279/test.jar differ diff --git a/test/java/beans/Introspector/Test6660539.java b/test/java/beans/Introspector/Test6660539.java index d1cb62827627cc767e9519a0ac1cfc6bbaa60ea7..349f1b3930b0bdf5ddeff4ec9865620a70c57735 100644 --- a/test/java/beans/Introspector/Test6660539.java +++ b/test/java/beans/Introspector/Test6660539.java @@ -28,8 +28,6 @@ * @author Sergey Malenkov */ -import sun.awt.SunToolkit; - import java.beans.BeanInfo; import java.beans.IntrospectionException; import java.beans.Introspector; @@ -49,7 +47,6 @@ public class Test6660539 implements Runnable { } public void run() { - SunToolkit.createNewAppContext(); for (PropertyDescriptor pd : getPropertyDescriptors()) { if (pd.getDisplayName().equals(NAME)) throw new Error("shared BeanInfo cache"); diff --git a/test/java/beans/PropertyEditor/6380849/TestPropertyEditor.java b/test/java/beans/PropertyEditor/6380849/TestPropertyEditor.java index 5d42144764049f19333137d3020f74e7376761fa..e07254a2d169163aff45f4226f58aefd575f9c35 100644 --- a/test/java/beans/PropertyEditor/6380849/TestPropertyEditor.java +++ b/test/java/beans/PropertyEditor/6380849/TestPropertyEditor.java @@ -36,7 +36,6 @@ import java.awt.Font; import java.beans.PropertyEditor; import java.beans.PropertyEditorManager; -import sun.awt.SunToolkit; import sun.beans.editors.BooleanEditor; import sun.beans.editors.ByteEditor; import sun.beans.editors.ColorEditor; @@ -77,12 +76,7 @@ public class TestPropertyEditor implements Runnable { } } - private boolean passed; - public void run() { - if (this.passed) { - SunToolkit.createNewAppContext(); - } PropertyEditorManager.registerEditor(ThirdBean.class, ThirdBeanEditor.class); test(FirstBean.class, FirstBeanEditor.class); @@ -135,7 +129,5 @@ public class TestPropertyEditor implements Runnable { test(Color.class, null); test(Font.class, null); test(Enumeration.class, EnumEditor.class); - - this.passed = true; } }