]> source.dussan.org Git - javassist.git/commitdiff
JIRA JASSIST-28 (including a performance bug) has been fixed.
authorchiba <chiba@30ef5769-5b8d-40dd-aea6-55b5d6557bb3>
Thu, 2 Nov 2006 06:01:24 +0000 (06:01 +0000)
committerchiba <chiba@30ef5769-5b8d-40dd-aea6-55b5d6557bb3>
Thu, 2 Nov 2006 06:01:24 +0000 (06:01 +0000)
git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@327 30ef5769-5b8d-40dd-aea6-55b5d6557bb3

Readme.html
src/main/javassist/util/proxy/ProxyFactory.java

index ff1324cff466bb26df557cdefd165042d1645048..ce7eb953b6284113718afebcaea9496ae6b97b14 100644 (file)
@@ -285,6 +285,12 @@ see javassist.Dump.
 <ul>
        <li>A bug in CodeConverter#replaceFieldRead() and CodeConverter#replaceFieldWrite()
        was fixed. <a href="http://jira.jboss.com/jira/browse/JBAOP-284">JBAOP-284</a>.
+
+    <li>A synchronization bug and a performance bug in <code>javassist.util.proxy</code>
+    have been fixed
+    (<a href="http://jira.jboss.com/jira/browse/JASSIST-28">JASSIST-28</a>).
+    Now generated proxy classes are cached.  To turn the caching off,
+    set <code>ProxyFactory.useCache</code> to <code>false</code>.
 </ul>
 
 <p>-version 3.3 on August 17, 2006
@@ -698,7 +704,7 @@ Howard Lewis Ship, Richard Jones, Marjan Sterjev,
 Bruce McDonald, Mark Brennan, Vlad Skarzhevskyy,
 Brett Randall, Tsuyoshi Murakami, Nathan Meyers, Yoshiyuki Usui
 Yutaka Sunaga, Arjan van der Meer, Bruce Eckel, Guillaume Pothier,
-Kumar Matcha, Andreas Salathe, Renat Zubairov,
+Kumar Matcha, Andreas Salathe, Renat Zubairov, Armin Haaf
 and all other contributors for their contributions.
 
 <p><br>
index 566b809d4bd4e0474ecdf306201b861968d0e98b..f42714d8e54e4c1718e3b97b382d7887fce75d66 100644 (file)
@@ -23,9 +23,11 @@ import java.lang.reflect.Member;
 import java.lang.reflect.Modifier;
 import java.security.ProtectionDomain;
 import java.util.HashMap;
+import java.util.WeakHashMap;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
+import java.lang.ref.WeakReference;
 
 import javassist.CannotCompileException;
 import javassist.bytecode.*;
@@ -123,6 +125,56 @@ public class ProxyFactory {
     private static final String HANDLER_SETTER = "setHandler";
     private static final String HANDLER_SETTER_TYPE = "(" + HANDLER_TYPE + ")V";
 
+    /**
+     * If true, a generated proxy class is cached and it will be reused
+     * when generating the proxy class with the same properties is requested.
+     * The default value is true.
+     *
+     * @since 3.4
+     */
+    public static boolean useCache = true; 
+
+    private static WeakHashMap proxyCache = new WeakHashMap();
+
+    static class CacheKey {
+        private String classes;
+        private MethodFilter filter;
+        private int hash;
+        WeakReference proxyClass;
+
+        public CacheKey(Class superClass, Class[] interfaces, MethodFilter f) {
+            classes = getKey(superClass, interfaces);
+            hash = classes.hashCode();
+            filter = f;
+            proxyClass = null;
+        }
+
+        public int hashCode() { return hash; }
+
+        public boolean equals(Object obj) {
+            if (obj instanceof CacheKey) {
+                CacheKey target = (CacheKey)obj;
+                return target.filter == filter && target.classes.equals(classes);
+            }
+            else
+                return false;
+        }
+
+        static String getKey(Class superClass, Class[] interfaces) {
+            StringBuffer sbuf = new StringBuffer();
+            if (superClass != null)
+                sbuf.append(superClass.getName());
+            sbuf.append(':');
+            if (interfaces != null) {
+                int len = interfaces.length;
+                for (int i = 0; i < len; i++)
+                    sbuf.append(interfaces[i].getName()).append(',');
+            }
+
+            return sbuf.toString();
+        }
+    }
+
     /**
      * Constructs a factory of proxy class.
      */
@@ -174,21 +226,76 @@ public class ProxyFactory {
      * Generates a proxy class.
      */
     public Class createClass() {
-        if (thisClass == null)
-            try {
-                ClassFile cf = make();
-                ClassLoader cl = getClassLoader();
-                if (writeDirectory != null)
-                    FactoryHelper.writeFile(cf, writeDirectory);
+        if (thisClass == null) {
+            ClassLoader cl = getClassLoader();
+            if (useCache)
+                createClass2(cl);
+            else
+                createClass3(cl);
+        }
 
-                thisClass = FactoryHelper.toClass(cf, cl, getDomain());
-                setHandler();
+        return thisClass;
+    }
+
+    private void createClass2(ClassLoader cl) {
+        CacheKey key = new CacheKey(superClass, interfaces, methodFilter);
+        synchronized (proxyCache) {
+            HashMap cacheForTheLoader = (HashMap)proxyCache.get(cl);
+            if (cacheForTheLoader == null) {
+                cacheForTheLoader = new HashMap();
+                proxyCache.put(cl, cacheForTheLoader);
+                cacheForTheLoader.put(key, key);
             }
-            catch (CannotCompileException e) {
-                throw new RuntimeException(e.getMessage(), e);
+            else {
+                CacheKey found = (CacheKey)cacheForTheLoader.get(key);
+                if (found == null)
+                    cacheForTheLoader.put(key, key);
+                else {
+                    key = found;
+                    Class c = isValidEntry(key);    // no need to synchronize
+                    if (c != null) {
+                        thisClass = c;
+                        return;
+                    }
+                }
             }
+        }
+
+        synchronized (key) {
+            Class c = isValidEntry(key);
+            if (c == null) {
+                createClass3(cl);
+                key.proxyClass = new WeakReference(thisClass);
+            }
+            else
+                thisClass = c; 
+        }
+    }
+
+    private Class isValidEntry(CacheKey key) {
+        WeakReference ref = key.proxyClass;
+        if (ref != null) {
+            Class c = (Class)ref.get();
+            if(c != null && getHandler(c) == handler)
+                return c;
+        }
+
+        return null;
+    }
+
+    private void createClass3(ClassLoader cl) {
+        try {
+            ClassFile cf = make();
+            if (writeDirectory != null)
+                FactoryHelper.writeFile(cf, writeDirectory);
+
+            thisClass = FactoryHelper.toClass(cf, cl, getDomain());
+            setHandler();
+        }
+        catch (CannotCompileException e) {
+            throw new RuntimeException(e.getMessage(), e);
+        }
 
-        return thisClass;
     }
 
     /**
@@ -307,6 +414,19 @@ public class ProxyFactory {
             }
     }
 
+    static MethodHandler getHandler(Class clazz) {
+        try {
+            Field f = clazz.getField(DEFAULT_INTERCEPTOR);
+            f.setAccessible(true);
+            MethodHandler h = (MethodHandler)f.get(null);
+            f.setAccessible(false);
+            return h;
+        }
+        catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
     private static int counter = 0;
 
     private static synchronized String makeProxyName(String classname) {