diff --git a/jdk/src/share/classes/com/sun/beans/WeakCache.java b/jdk/src/share/classes/com/sun/beans/WeakCache.java new file mode 100644 index 00000000000..461c48e1fd3 --- /dev/null +++ b/jdk/src/share/classes/com/sun/beans/WeakCache.java @@ -0,0 +1,84 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package com.sun.beans; + +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; + +import java.util.Map; +import java.util.WeakHashMap; + +/** + * A hashtable-based cache with weak keys and weak values. + * An entry in the map will be automatically removed + * when its key is no longer in the ordinary use. + * A value will be automatically removed as well + * when it is no longer in the ordinary use. + * + * @since 1.7 + * + * @author Sergey A. Malenkov + */ +public final class WeakCache { + private final Map> map = new WeakHashMap>(); + + /** + * Returns a value to which the specified {@code key} is mapped, + * or {@code null} if this map contains no mapping for the {@code key}. + * + * @param key the key whose associated value is returned + * @return a value to which the specified {@code key} is mapped + */ + public V get(K key) { + Reference reference = this.map.get(key); + if (reference == null) { + return null; + } + V value = reference.get(); + if (value == null) { + this.map.remove(key); + } + return value; + } + + /** + * Associates the specified {@code value} with the specified {@code key}. + * Removes the mapping for the specified {@code key} from this cache + * if it is present and the specified {@code value} is {@code null}. + * If the cache previously contained a mapping for the {@code key}, + * the old value is replaced by the specified {@code value}. + * + * @param key the key with which the specified value is associated + * @param value the value to be associated with the specified key + */ + public void put(K key, V value) { + if (value != null) { + this.map.put(key, new WeakReference(value)); + } + else { + this.map.remove(key); + } + } +} diff --git a/jdk/src/share/classes/java/beans/PropertyEditorManager.java b/jdk/src/share/classes/java/beans/PropertyEditorManager.java index 055df1ad98c..a456c2bff54 100644 --- a/jdk/src/share/classes/java/beans/PropertyEditorManager.java +++ b/jdk/src/share/classes/java/beans/PropertyEditorManager.java @@ -1,5 +1,5 @@ /* - * Copyright 1996-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1996-2008 Sun Microsystems, Inc. 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 @@ -25,6 +25,7 @@ package java.beans; +import com.sun.beans.WeakCache; import sun.beans.editors.*; /** @@ -55,32 +56,30 @@ import sun.beans.editors.*; public class PropertyEditorManager { /** - * Register an editor class to be used to edit values of - * a given target class. + * Registers an editor class to edit values of the given target class. + * If the editor class is {@code null}, + * then any existing definition will be removed. + * Thus this method can be used to cancel the registration. + * The registration is canceled automatically + * if either the target or editor class is unloaded. + *

+ * If there is a security manager, its {@code checkPropertiesAccess} + * method is called. This could result in a {@linkplain SecurityException}. * - *

First, if there is a security manager, its checkPropertiesAccess - * method is called. This could result in a SecurityException. + * @param targetType the class object of the type to be edited + * @param editorClass the class object of the editor class + * @throws SecurityException if a security manager exists and + * its {@code checkPropertiesAccess} method + * doesn't allow setting of system properties * - * @param targetType the Class object of the type to be edited - * @param editorClass the Class object of the editor class. If - * this is null, then any existing definition will be removed. - * @exception SecurityException if a security manager exists and its - * checkPropertiesAccess method doesn't allow setting - * of system properties. * @see SecurityManager#checkPropertiesAccess */ - - public static void registerEditor(Class targetType, Class editorClass) { + public static synchronized void registerEditor(Class targetType, Class editorClass) { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPropertiesAccess(); } - initialize(); - if (editorClass == null) { - registry.remove(targetType); - } else { - registry.put(targetType, editorClass); - } + registry.put(targetType, editorClass); } /** @@ -90,10 +89,8 @@ public class PropertyEditorManager { * @return An editor object for the given target class. * The result is null if no suitable editor can be found. */ - public static synchronized PropertyEditor findEditor(Class targetType) { - initialize(); - Class editorClass = (Class)registry.get(targetType); + Class editorClass = registry.get(targetType); if (editorClass != null) { try { Object o = editorClass.newInstance(); @@ -143,10 +140,7 @@ public class PropertyEditorManager { * e.g. Sun implementation initially sets to {"sun.beans.editors"}. */ public static synchronized String[] getEditorSearchPath() { - // Return a copy of the searchPath. - String result[] = new String[searchPath.length]; - System.arraycopy(searchPath, 0, result, 0, searchPath.length); - return result; + return searchPath.clone(); } /** @@ -162,23 +156,22 @@ public class PropertyEditorManager { * of system properties. * @see SecurityManager#checkPropertiesAccess */ - - public static synchronized void setEditorSearchPath(String path[]) { + public static synchronized void setEditorSearchPath(String[] path) { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPropertiesAccess(); } - if (path == null) { - path = new String[0]; - } - searchPath = path; + searchPath = (path != null) + ? path.clone() + : EMPTY; } - private static synchronized void initialize() { - if (registry != null) { - return; - } - registry = new java.util.Hashtable(); + private static String[] searchPath = { "sun.beans.editors" }; + private static final String[] EMPTY = {}; + private static final WeakCache, Class> registry; + + static { + registry = new WeakCache, Class>(); registry.put(Byte.TYPE, ByteEditor.class); registry.put(Short.TYPE, ShortEditor.class); registry.put(Integer.TYPE, IntegerEditor.class); @@ -187,7 +180,4 @@ public class PropertyEditorManager { registry.put(Float.TYPE, FloatEditor.class); registry.put(Double.TYPE, DoubleEditor.class); } - - private static String[] searchPath = { "sun.beans.editors" }; - private static java.util.Hashtable registry; } diff --git a/jdk/test/java/beans/PropertyEditor/MemoryClassLoader.java b/jdk/test/java/beans/PropertyEditor/MemoryClassLoader.java new file mode 100644 index 00000000000..cd462f88306 --- /dev/null +++ b/jdk/test/java/beans/PropertyEditor/MemoryClassLoader.java @@ -0,0 +1,143 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +import java.io.ByteArrayOutputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.tools.FileObject; +import javax.tools.ForwardingJavaFileManager; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileManager; +import javax.tools.JavaFileObject.Kind; +import javax.tools.SimpleJavaFileObject; +import javax.tools.ToolProvider; + +public final class MemoryClassLoader extends ClassLoader { + private final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + private final MemoryFileManager manager = new MemoryFileManager(this.compiler); + + public Class compile(String name, String content) { + compile(new Source(name, content)); + try { + return findClass(name); + } + catch (ClassNotFoundException exception) { + throw new Error(exception); + } + } + + public void compile(Source... sources) { + List list = new ArrayList(); + if (sources != null) { + for (Source source : sources) { + if (source != null) { + list.add(source); + } + } + } + synchronized (this.manager) { + this.compiler.getTask(null, this.manager, null, null, null, list).call(); + } + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + synchronized (this.manager) { + Output mc = this.manager.map.remove(name); + if (mc != null) { + byte[] array = mc.toByteArray(); + return defineClass(name, array, 0, array.length); + } + } + return super.findClass(name); + } + + private static final class MemoryFileManager extends ForwardingJavaFileManager { + private final Map map = new HashMap(); + + MemoryFileManager(JavaCompiler compiler) { + super(compiler.getStandardFileManager(null, null, null)); + } + + @Override + public Output getJavaFileForOutput(Location location, String name, Kind kind, FileObject source) { + Output mc = this.map.get(name); + if (mc == null) { + mc = new Output(name); + this.map.put(name, mc); + } + return mc; + } + } + + private static class MemoryFileObject extends SimpleJavaFileObject { + MemoryFileObject(String name, Kind kind) { + super(toURI(name, kind.extension), kind); + } + + private static URI toURI(String name, String extension) { + try { + return new URI("mfm:///" + name.replace('.', '/') + extension); + } + catch (URISyntaxException exception) { + throw new Error(exception); + } + } + } + + private static final class Output extends MemoryFileObject { + private final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + Output(String name) { + super(name, Kind.CLASS); + } + + byte[] toByteArray() { + return this.baos.toByteArray(); + } + + @Override + public ByteArrayOutputStream openOutputStream() { + this.baos.reset(); + return this.baos; + } + } + + public static final class Source extends MemoryFileObject { + private final String content; + + Source(String name, String content) { + super(name, Kind.SOURCE); + this.content = content; + } + + @Override + public CharSequence getCharContent(boolean ignore) { + return this.content; + } + } +} diff --git a/jdk/test/java/beans/PropertyEditor/Test6397609.java b/jdk/test/java/beans/PropertyEditor/Test6397609.java new file mode 100644 index 00000000000..ac7065e0b3b --- /dev/null +++ b/jdk/test/java/beans/PropertyEditor/Test6397609.java @@ -0,0 +1,59 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6397609 + * @summary Tests autocleaning + * @author Sergey Malenkov + */ + +import java.beans.PropertyEditorManager; + +public class Test6397609 { + public static void main(String[] args) throws Exception { + MemoryClassLoader loader = new MemoryClassLoader(); + PropertyEditorManager.registerEditor( + Object.class, + loader.compile("Editor", + "public class Editor extends java.beans.PropertyEditorSupport {}")); + + if (!isEditorExist(Object.class)) { + throw new Error("the editor is lost"); + } + loader = null; // clean the reference + if (isEditorExist(Object.class)) { + throw new Error("unexpected editor is found"); + } + } + + private static boolean isEditorExist(Class type) { + for (int i = 0; i < 10; i++) { + System.gc(); // clean all weak references + if (null == PropertyEditorManager.findEditor(type)) { + return false; + } + } + return true; + } +} diff --git a/jdk/test/java/beans/PropertyEditor/TestEditor.java b/jdk/test/java/beans/PropertyEditor/TestEditor.java index 85a9ec338a5..25c7acf6090 100644 --- a/jdk/test/java/beans/PropertyEditor/TestEditor.java +++ b/jdk/test/java/beans/PropertyEditor/TestEditor.java @@ -1,5 +1,5 @@ /* - * Copyright 2006-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2006-2008 Sun Microsystems, Inc. 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 @@ -23,18 +23,6 @@ import java.beans.PropertyEditor; import java.beans.PropertyEditorManager; -import java.io.ByteArrayOutputStream; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import javax.tools.FileObject; -import javax.tools.ForwardingJavaFileManager; -import javax.tools.JavaCompiler; -import javax.tools.JavaFileObject.Kind; -import javax.tools.SimpleJavaFileObject; -import javax.tools.ToolProvider; final class TestEditor { private final PropertyEditor editor; @@ -53,8 +41,7 @@ final class TestEditor { void testJava(Object value) { this.editor.setValue(value); - MemoryFileManager manager = new MemoryFileManager(); - Object object = manager.invoke(this.editor.getJavaInitializationString()); + Object object = execute("Executor", "execute", this.editor.getJavaInitializationString()); System.out.println("Property value before: " + value); System.out.println("Property value after: " + object); @@ -87,98 +74,21 @@ final class TestEditor { : object1.equals(object2); } - private static final class MemoryFileManager extends ForwardingJavaFileManager { - private static final String CLASS = "Executor"; - private static final String METHOD = "execute"; - private static final JavaCompiler COMPILER = ToolProvider.getSystemJavaCompiler(); - private final Map map = new HashMap(); - private final MemoryClassLoader loader = new MemoryClassLoader(); + private static Object execute(String classname, String methodname, String value) { + String content + = "public class " + classname + " {" + + " public static Object " + methodname + "() throws Exception {" + + " return " + value + ";" + + " }" + + "}"; - MemoryFileManager() { - super(COMPILER.getStandardFileManager(null, null, null)); + try { + MemoryClassLoader loader = new MemoryClassLoader(); + Class type = loader.compile(classname, content); + return type.getMethod(methodname).invoke(null); } - - public Object invoke(String expression) { - MemorySource file = new MemorySource(CLASS, METHOD, expression); - if (!COMPILER.getTask(null, this, null, null, null, Arrays.asList(file)).call()) - throw new Error("compilation failed"); - - MemoryClass mc = this.map.get(CLASS); - if (mc == null) - throw new Error("class not found: " + CLASS); - - Class c = this.loader.loadClass(CLASS, mc.toByteArray()); - try { - return c.getMethod(METHOD).invoke(null); - } - catch (Exception exception) { - throw new Error(exception); - } - } - - public MemoryClass getJavaFileForOutput(Location location, String name, Kind kind, FileObject source) { - MemoryClass type = this.map.get(name); - if (type == null) { - type = new MemoryClass(name); - this.map.put(name, type); - } - return type; - } - } - - private static final class MemoryClassLoader extends ClassLoader { - public Class loadClass(String name, byte[] array) { - return defineClass(name, array, 0, array.length); - } - } - - private static class MemoryObject extends SimpleJavaFileObject { - protected MemoryObject(String name, Kind kind) { - super(toURI(name, kind.extension), kind); - } - - private static URI toURI(String name, String extension) { - try { - return new URI("mfm:///" + name.replace('.', '/') + extension); - } - catch (URISyntaxException exception) { - throw new Error(exception); - } - } - } - - private static final class MemoryClass extends MemoryObject { - private final ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - MemoryClass(String className) { - super(className, Kind.CLASS); - } - - public ByteArrayOutputStream openOutputStream() { - this.baos.reset(); - return this.baos; - } - - public byte[] toByteArray() { - return this.baos.toByteArray(); - } - } - - private static final class MemorySource extends MemoryObject { - private final String value; - - MemorySource(String className, String methodName, String expression) { - super(className, Kind.SOURCE); - this.value - = "public class " + className + " {\n" - + " public static Object " + methodName + "() throws Exception {\n" - + " return " + expression + ";\n" - + " }\n" - + "}\n"; - } - - public CharSequence getCharContent(boolean ignore) { - return this.value; + catch (Exception exception) { + throw new Error(exception); } } }