# HG changeset patch
# User andrew
# Date 1365712268 -3600
# Node ID 4d66f7ebcf99c1b322f47ff0aa6adadcd995f8f4
# Parent df591e0dfd349dc5986cc17949939c588d5a9690
7064279: Introspector.getBeanInfo() should release some resources in timely manner
Reviewed-by: art, alexp
diff --git a/src/share/classes/java/beans/Beans.java b/src/share/classes/java/beans/Beans.java
--- jdk/src/share/classes/java/beans/Beans.java
+++ jdk/src/share/classes/java/beans/Beans.java
@@ -32,7 +32,6 @@
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.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 @@
* @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 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 @@
if (sm != null) {
sm.checkPropertiesAccess();
}
- AppContext.getAppContext().put(DESIGN_TIME, Boolean.valueOf(isDesignTime));
+ ThreadGroupContext.getContext().setDesignTime(isDesignTime);
}
/**
@@ -466,7 +459,7 @@
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
--- jdk/src/share/classes/java/beans/Introspector.java
+++ jdk/src/share/classes/java/beans/Introspector.java
@@ -38,7 +38,6 @@
import java.util.EventListener;
import java.util.List;
import java.util.TreeMap;
-import sun.awt.AppContext;
import sun.reflect.misc.ReflectUtil;
/**
@@ -103,10 +102,7 @@
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;
@@ -170,21 +166,15 @@
if (!ReflectUtil.isPackageAccessible(beanClass)) {
return (new Introspector(beanClass, null, USE_ALL_BEANINFO)).getBeanInfo();
}
- WeakCache, BeanInfo> beanInfoCache;
+ ThreadGroupContext context = ThreadGroupContext.getContext();
BeanInfo beanInfo;
- synchronized (BEANINFO_CACHE) {
- beanInfoCache = (WeakCache, BeanInfo>) AppContext.getAppContext().get(BEANINFO_CACHE);
-
- if (beanInfoCache == null) {
- beanInfoCache = new WeakCache, 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;
@@ -334,11 +324,8 @@
*/
public static void flushCaches() {
- synchronized (BEANINFO_CACHE) {
- WeakCache beanInfoCache = (WeakCache) AppContext.getAppContext().get(BEANINFO_CACHE);
- if (beanInfoCache != null) {
- beanInfoCache.clear();
- }
+ synchronized (declaredMethodCache) {
+ ThreadGroupContext.getContext().clearBeanInfoCache();
declaredMethodCache.clear();
}
}
@@ -362,11 +349,8 @@
if (clz == null) {
throw new NullPointerException();
}
- synchronized (BEANINFO_CACHE) {
- WeakCache beanInfoCache = (WeakCache) AppContext.getAppContext().get(BEANINFO_CACHE);
- if (beanInfoCache != null) {
- beanInfoCache.put(clz, null);
- }
+ synchronized (declaredMethodCache) {
+ ThreadGroupContext.getContext().removeBeanInfo(clz);
declaredMethodCache.put(clz, null);
}
}
@@ -1313,7 +1297,7 @@
if (!ReflectUtil.isPackageAccessible(clz)) {
return new Method[0];
}
- synchronized (BEANINFO_CACHE) {
+ synchronized (declaredMethodCache) {
Method[] result = declaredMethodCache.get(clz);
if (result == null) {
result = clz.getMethods();
diff --git a/src/share/classes/java/beans/ThreadGroupContext.java b/src/share/classes/java/beans/ThreadGroupContext.java
new file mode 100644
--- /dev/null
+++ jdk/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
--- jdk/test/java/beans/Beans/6669869/TestDesignTime.java
+++ jdk/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 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
--- jdk/test/java/beans/Beans/6669869/TestGuiAvailable.java
+++ jdk/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 void run() {
- SunToolkit.createNewAppContext();
if (Beans.isGuiAvailable() == GraphicsEnvironment.isHeadless()) {
throw new Error("shared GuiAvailable property");
}
diff --git a/test/java/beans/Introspector/7064279/Test7064279.java b/test/java/beans/Introspector/7064279/Test7064279.java
new file mode 100644
--- /dev/null
+++ jdk/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/Test6660539.java b/test/java/beans/Introspector/Test6660539.java
--- jdk/test/java/beans/Introspector/Test6660539.java
+++ jdk/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 void run() {
- SunToolkit.createNewAppContext();
for (PropertyDescriptor pd : getPropertyDescriptors()) {
if (pd.getDisplayName().equals(NAME))
throw new Error("shared BeanInfo cache");