<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
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>
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.*;
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.
*/
* 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;
}
/**
}
}
+ 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) {