|
|
@@ -32,6 +32,11 @@ import java.lang.ref.WeakReference; |
|
|
|
import javassist.CannotCompileException; |
|
|
|
import javassist.bytecode.*; |
|
|
|
|
|
|
|
/* |
|
|
|
* This class is implemented only with the lower-level API of Javassist. |
|
|
|
* This design decision is for maximizing performance. |
|
|
|
*/ |
|
|
|
|
|
|
|
/** |
|
|
|
* Factory of dynamic proxy classes. |
|
|
|
* |
|
|
@@ -92,6 +97,11 @@ import javassist.bytecode.*; |
|
|
|
* } |
|
|
|
* </pre></ul> |
|
|
|
* |
|
|
|
* <p>A proxy object generated by <code>ProxyFactory</code> is serializable |
|
|
|
* if its super class or interfaces implement a <code>java.io.Serializable</code>. |
|
|
|
* However, a serialized proxy object will not be compatible with future releases. |
|
|
|
* The serialization support should be used for short-term storage or RMI. |
|
|
|
* |
|
|
|
* @see MethodHandler |
|
|
|
* @since 3.1 |
|
|
|
*/ |
|
|
@@ -117,6 +127,7 @@ public class ProxyFactory { |
|
|
|
|
|
|
|
private static final String HOLDER = "_methods_"; |
|
|
|
private static final String HOLDER_TYPE = "[Ljava/lang/reflect/Method;"; |
|
|
|
private static final String METHOD_FILTER_FIELD = "_method_filter"; |
|
|
|
private static final String HANDLER = "handler"; |
|
|
|
private static final String NULL_INTERCEPTOR_HOLDER = "javassist.util.proxy.RuntimeSupport"; |
|
|
|
private static final String DEFAULT_INTERCEPTOR = "default_interceptor"; |
|
|
@@ -137,8 +148,8 @@ public class ProxyFactory { |
|
|
|
private static WeakHashMap proxyCache = new WeakHashMap(); |
|
|
|
|
|
|
|
static class CacheKey { |
|
|
|
private String classes; |
|
|
|
private MethodFilter filter; |
|
|
|
String classes; |
|
|
|
MethodFilter filter; |
|
|
|
private int hash; |
|
|
|
WeakReference proxyClass; |
|
|
|
MethodHandler handler; |
|
|
@@ -233,10 +244,12 @@ public class ProxyFactory { |
|
|
|
public Class createClass() { |
|
|
|
if (thisClass == null) { |
|
|
|
ClassLoader cl = getClassLoader(); |
|
|
|
if (useCache) |
|
|
|
createClass2(cl); |
|
|
|
else |
|
|
|
createClass3(cl); |
|
|
|
synchronized (proxyCache) { |
|
|
|
if (useCache) |
|
|
|
createClass2(cl); |
|
|
|
else |
|
|
|
createClass3(cl); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return thisClass; |
|
|
@@ -244,7 +257,12 @@ public class ProxyFactory { |
|
|
|
|
|
|
|
private void createClass2(ClassLoader cl) { |
|
|
|
CacheKey key = new CacheKey(superClass, interfaces, methodFilter, handler); |
|
|
|
synchronized (proxyCache) { |
|
|
|
/* |
|
|
|
* Excessive concurrency causes a large memory footprint and slows the |
|
|
|
* execution speed down (with JDK 1.5). Thus, we use a jumbo lock for |
|
|
|
* reducing concrrency. |
|
|
|
*/ |
|
|
|
// synchronized (proxyCache) { |
|
|
|
HashMap cacheForTheLoader = (HashMap)proxyCache.get(cl); |
|
|
|
if (cacheForTheLoader == null) { |
|
|
|
cacheForTheLoader = new HashMap(); |
|
|
@@ -264,9 +282,9 @@ public class ProxyFactory { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
// } |
|
|
|
|
|
|
|
synchronized (key) { |
|
|
|
// synchronized (key) { |
|
|
|
Class c = isValidEntry(key); |
|
|
|
if (c == null) { |
|
|
|
createClass3(cl); |
|
|
@@ -274,7 +292,7 @@ public class ProxyFactory { |
|
|
|
} |
|
|
|
else |
|
|
|
thisClass = c; |
|
|
|
} |
|
|
|
// } |
|
|
|
} |
|
|
|
|
|
|
|
private Class isValidEntry(CacheKey key) { |
|
|
@@ -295,7 +313,8 @@ public class ProxyFactory { |
|
|
|
FactoryHelper.writeFile(cf, writeDirectory); |
|
|
|
|
|
|
|
thisClass = FactoryHelper.toClass(cf, cl, getDomain()); |
|
|
|
setHandler(); |
|
|
|
setField(DEFAULT_INTERCEPTOR, handler); |
|
|
|
setField(METHOD_FILTER_FIELD, methodFilter); |
|
|
|
} |
|
|
|
catch (CannotCompileException e) { |
|
|
|
throw new RuntimeException(e.getMessage(), e); |
|
|
@@ -303,6 +322,40 @@ public class ProxyFactory { |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
private void setField(String fieldName, Object value) { |
|
|
|
if (thisClass != null && value != null) |
|
|
|
try { |
|
|
|
Field f = thisClass.getField(fieldName); |
|
|
|
f.setAccessible(true); |
|
|
|
f.set(null, value); |
|
|
|
f.setAccessible(false); |
|
|
|
} |
|
|
|
catch (Exception e) { |
|
|
|
throw new RuntimeException(e); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static MethodFilter getFilter(Class clazz) { |
|
|
|
return (MethodFilter)getField(clazz, METHOD_FILTER_FIELD); |
|
|
|
} |
|
|
|
|
|
|
|
static MethodHandler getHandler(Class clazz) { |
|
|
|
return (MethodHandler)getField(clazz, DEFAULT_INTERCEPTOR); |
|
|
|
} |
|
|
|
|
|
|
|
private static Object getField(Class clazz, String fieldName) { |
|
|
|
try { |
|
|
|
Field f = clazz.getField(fieldName); |
|
|
|
f.setAccessible(true); |
|
|
|
Object value = f.get(null); |
|
|
|
f.setAccessible(false); |
|
|
|
return value; |
|
|
|
} |
|
|
|
catch (Exception e) { |
|
|
|
throw new RuntimeException(e); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* A provider of class loaders. |
|
|
|
* |
|
|
@@ -403,33 +456,7 @@ public class ProxyFactory { |
|
|
|
*/ |
|
|
|
public void setHandler(MethodHandler mi) { |
|
|
|
handler = mi; |
|
|
|
setHandler(); |
|
|
|
} |
|
|
|
|
|
|
|
private void setHandler() { |
|
|
|
if (thisClass != null && handler != null) |
|
|
|
try { |
|
|
|
Field f = thisClass.getField(DEFAULT_INTERCEPTOR); |
|
|
|
f.setAccessible(true); |
|
|
|
f.set(null, handler); |
|
|
|
f.setAccessible(false); |
|
|
|
} |
|
|
|
catch (Exception e) { |
|
|
|
throw new RuntimeException(e); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
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); |
|
|
|
} |
|
|
|
setField(DEFAULT_INTERCEPTOR, handler); |
|
|
|
} |
|
|
|
|
|
|
|
private static int counter = 0; |
|
|
@@ -473,6 +500,11 @@ public class ProxyFactory { |
|
|
|
finfo2.setAccessFlags(AccessFlag.PRIVATE); |
|
|
|
cf.addField(finfo2); |
|
|
|
|
|
|
|
FieldInfo finfo3 = new FieldInfo(pool, METHOD_FILTER_FIELD, |
|
|
|
"Ljavassist/util/proxy/MethodFilter;"); |
|
|
|
finfo3.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC); |
|
|
|
cf.addField(finfo3); |
|
|
|
|
|
|
|
HashMap allMethods = getMethods(superClass, interfaces); |
|
|
|
int size = allMethods.size(); |
|
|
|
makeConstructors(classname, cf, pool, classname); |
|
|
@@ -480,6 +512,8 @@ public class ProxyFactory { |
|
|
|
addMethodsHolder(cf, pool, classname, s); |
|
|
|
addSetter(classname, cf, pool); |
|
|
|
|
|
|
|
cf.addMethod(makeWriteReplace(pool)); |
|
|
|
|
|
|
|
thisClass = null; |
|
|
|
return cf; |
|
|
|
} |
|
|
@@ -932,4 +966,21 @@ public class ProxyFactory { |
|
|
|
else |
|
|
|
code.addCheckcast(type.getName()); |
|
|
|
} |
|
|
|
|
|
|
|
private static MethodInfo makeWriteReplace(ConstPool cp) { |
|
|
|
MethodInfo minfo = new MethodInfo(cp, "writeReplace", "()Ljava/lang/Object;"); |
|
|
|
String[] list = new String[1]; |
|
|
|
list[0] = "java.io.ObjectStreamException"; |
|
|
|
ExceptionsAttribute ea = new ExceptionsAttribute(cp); |
|
|
|
ea.setExceptions(list); |
|
|
|
minfo.setExceptionsAttribute(ea); |
|
|
|
Bytecode code = new Bytecode(cp, 0, 1); |
|
|
|
code.addAload(0); |
|
|
|
code.addInvokestatic("javassist.util.proxy.RuntimeSupport", |
|
|
|
"makeSerializedProxy", |
|
|
|
"(Ljava/lang/Object;)Ljavassist/util/proxy/SerializedProxy;"); |
|
|
|
code.addOpcode(Opcode.ARETURN); |
|
|
|
minfo.setCodeAttribute(code.toCodeAttribute()); |
|
|
|
return minfo; |
|
|
|
} |
|
|
|
} |