changeset 632:9765266e5aea

6397609: DOC: De-register API required for PropertyEditorManager and/or doc change Reviewed-by: peterz, rupashka
author malenkov
date Wed, 03 Sep 2008 21:00:04 +0400
parents 71df74bef5ba
children 2055acc62a85
files src/share/classes/com/sun/beans/WeakCache.java src/share/classes/java/beans/PropertyEditorManager.java test/java/beans/PropertyEditor/MemoryClassLoader.java test/java/beans/PropertyEditor/Test6397609.java test/java/beans/PropertyEditor/TestEditor.java
diffstat 5 files changed, 331 insertions(+), 145 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/beans/WeakCache.java	Wed Sep 03 21:00:04 2008 +0400
@@ -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<K, V> {
+    private final Map<K, Reference<V>> map = new WeakHashMap<K, Reference<V>>();
+
+    /**
+     * 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<V> 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<V>(value));
+        }
+        else {
+            this.map.remove(key);
+        }
+    }
+}
--- a/src/share/classes/java/beans/PropertyEditorManager.java	Mon Sep 01 17:36:57 2008 +0400
+++ b/src/share/classes/java/beans/PropertyEditorManager.java	Wed Sep 03 21:00:04 2008 +0400
@@ -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 @@
 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.
+     * <p>
+     * If there is a security manager, its {@code checkPropertiesAccess}
+     * method is called. This could result in a {@linkplain SecurityException}.
      *
-     * <p>First, if there is a security manager, its <code>checkPropertiesAccess</code>
-     * 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
-     *             <code>checkPropertiesAccess</code> 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 @@
      * @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 @@
      *         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 @@
      *              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<?>, Class<?>> registry;
+
+    static {
+        registry = new WeakCache<Class<?>, Class<?>>();
         registry.put(Byte.TYPE, ByteEditor.class);
         registry.put(Short.TYPE, ShortEditor.class);
         registry.put(Integer.TYPE, IntegerEditor.class);
@@ -187,7 +180,4 @@
         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;
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/beans/PropertyEditor/MemoryClassLoader.java	Wed Sep 03 21:00:04 2008 +0400
@@ -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<Source> list = new ArrayList<Source>();
+        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<JavaFileManager> {
+        private final Map<String, Output> map = new HashMap<String, Output>();
+
+        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;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/beans/PropertyEditor/Test6397609.java	Wed Sep 03 21:00:04 2008 +0400
@@ -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;
+    }
+}
--- a/test/java/beans/PropertyEditor/TestEditor.java	Mon Sep 01 17:36:57 2008 +0400
+++ b/test/java/beans/PropertyEditor/TestEditor.java	Wed Sep 03 21:00:04 2008 +0400
@@ -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 @@
     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 @@
                 : 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<String, MemoryClass> map = new HashMap<String, MemoryClass>();
-        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);
         }
     }
 }