![JAR search and dependency download from the Maven repository](/logo.png)
cloud.tianai.rpc.common.bytecode.Proxy Maven / Gradle / Ivy
package cloud.tianai.rpc.common.bytecode;
import cloud.tianai.rpc.common.util.ClassUtils;
import cloud.tianai.rpc.common.util.ReflectUtils;
import javax.imageio.stream.FileImageOutputStream;
import java.io.FileOutputStream;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
/**
* Proxy.
*/
public abstract class Proxy {
public static final InvocationHandler RETURN_NULL_INVOKER = (proxy, method, args) -> null;
public static final InvocationHandler THROW_UNSUPPORTED_INVOKER = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
throw new UnsupportedOperationException("Method [" + ReflectUtils.getName(method) + "] unimplemented.");
}
};
private static final AtomicLong PROXY_CLASS_COUNTER = new AtomicLong(0);
private static final String PACKAGE_NAME = Proxy.class.getPackage().getName();
private static final Map> PROXY_CACHE_MAP = new WeakHashMap>();
private static final Object PENDING_GENERATION_MARKER = new Object();
protected Proxy() {
}
/**
* Get proxy.
*
* @param ics interface class array.
* @return Proxy instance.
*/
public static Proxy getProxy(Class>... ics) {
return getProxy(ClassUtils.getClassLoader(Proxy.class), ics);
}
/**
* Get proxy.
*
* @param cl class loader.
* @param ics interface class array.
* @return Proxy instance.
*/
public static Proxy getProxy(ClassLoader cl, Class>... ics) {
if (ics.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < ics.length; i++) {
String itf = ics[i].getName();
if (!ics[i].isInterface()) {
throw new RuntimeException(itf + " is not a interface.");
}
Class> tmp = null;
try {
tmp = Class.forName(itf, false, cl);
} catch (ClassNotFoundException e) {
}
if (tmp != ics[i]) {
throw new IllegalArgumentException(ics[i] + " is not visible from class loader");
}
sb.append(itf).append(';');
}
// use interface class name list as key.
String key = sb.toString();
// get cache by class loader.
final Map cache;
synchronized (PROXY_CACHE_MAP) {
cache = PROXY_CACHE_MAP.computeIfAbsent(cl, k -> new HashMap<>());
}
Proxy proxy = null;
synchronized (cache) {
do {
Object value = cache.get(key);
if (value instanceof Reference>) {
proxy = (Proxy) ((Reference>) value).get();
if (proxy != null) {
return proxy;
}
}
if (value == PENDING_GENERATION_MARKER) {
try {
cache.wait();
} catch (InterruptedException e) {
}
} else {
cache.put(key, PENDING_GENERATION_MARKER);
break;
}
}
while (true);
}
long id = PROXY_CLASS_COUNTER.getAndIncrement();
String pkg = null;
ClassGenerator ccp = null, ccm = null;
try {
ccp = ClassGenerator.newInstance(cl);
Set worked = new HashSet<>();
List methods = new ArrayList<>();
for (int i = 0; i < ics.length; i++) {
if (!Modifier.isPublic(ics[i].getModifiers())) {
String npkg = ics[i].getPackage().getName();
if (pkg == null) {
pkg = npkg;
} else {
if (!pkg.equals(npkg)) {
throw new IllegalArgumentException("non-public interfaces from different packages");
}
}
}
ccp.addInterface(ics[i]);
for (Method method : ics[i].getMethods()) {
String desc = ReflectUtils.getDesc(method);
if (worked.contains(desc) || Modifier.isStatic(method.getModifiers())) {
continue;
}
if (ics[i].isInterface() && Modifier.isStatic(method.getModifiers())) {
continue;
}
worked.add(desc);
int ix = methods.size();
Class> rt = method.getReturnType();
Class>[] pts = method.getParameterTypes();
StringBuilder code = new StringBuilder("Object[] args = new Object[").append(pts.length).append("];");
for (int j = 0; j < pts.length; j++) {
code.append(" args[").append(j).append("] = ($w)$").append(j + 1).append(";");
}
code.append(" Object ret = handler.invoke(this, methods[").append(ix).append("], args);");
if (!Void.TYPE.equals(rt)) {
code.append(" return ").append(asArgument(rt, "ret")).append(";");
}
methods.add(method);
ccp.addMethod(method.getName(), method.getModifiers(), rt, pts, method.getExceptionTypes(), code.toString());
}
}
if (pkg == null) {
pkg = PACKAGE_NAME;
}
// create ProxyInstance class.
String pcn = pkg + ".proxy" + id;
ccp.setClassName(pcn);
ccp.addField("public static java.lang.reflect.Method[] methods;");
ccp.addField("private " + InvocationHandler.class.getName() + " handler;");
ccp.addConstructor(Modifier.PUBLIC, new Class>[]{InvocationHandler.class}, new Class>[0], "handler=$1;");
ccp.addDefaultConstructor();
Class> clazz = ccp.toClass();
clazz.getField("methods").set(null, methods.toArray(new Method[0]));
// create Proxy class.
String fcn = Proxy.class.getName() + id;
ccm = ClassGenerator.newInstance(cl);
ccm.setClassName(fcn);
ccm.addDefaultConstructor();
ccm.setSuperClass(Proxy.class);
ccm.addMethod("public Object newInstance(" + InvocationHandler.class.getName() + " h){ return new " + pcn + "($1); }");
Class> pc = ccm.toClass();
proxy = (Proxy) pc.newInstance();
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
} finally {
// release ClassGenerator
if (ccp != null) {
ccp.release();
}
if (ccm != null) {
ccm.release();
}
synchronized (cache) {
if (proxy == null) {
cache.remove(key);
} else {
cache.put(key, new WeakReference(proxy));
}
cache.notifyAll();
}
}
return proxy;
}
private static String asArgument(Class> cl, String name) {
if (cl.isPrimitive()) {
if (Boolean.TYPE == cl) {
return name + "==null?false:((Boolean)" + name + ").booleanValue()";
}
if (Byte.TYPE == cl) {
return name + "==null?(byte)0:((Byte)" + name + ").byteValue()";
}
if (Character.TYPE == cl) {
return name + "==null?(char)0:((Character)" + name + ").charValue()";
}
if (Double.TYPE == cl) {
return name + "==null?(double)0:((Double)" + name + ").doubleValue()";
}
if (Float.TYPE == cl) {
return name + "==null?(float)0:((Float)" + name + ").floatValue()";
}
if (Integer.TYPE == cl) {
return name + "==null?(int)0:((Integer)" + name + ").intValue()";
}
if (Long.TYPE == cl) {
return name + "==null?(long)0:((Long)" + name + ").longValue()";
}
if (Short.TYPE == cl) {
return name + "==null?(short)0:((Short)" + name + ").shortValue()";
}
throw new RuntimeException(name + " is unknown primitive type.");
}
return "(" + ReflectUtils.getName(cl) + ")" + name;
}
/**
* get instance with default handler.
*
* @return instance.
*/
public Object newInstance() {
return newInstance(THROW_UNSUPPORTED_INVOKER);
}
/**
* get instance with special handler.
*
* @return instance.
*/
abstract public Object newInstance(InvocationHandler handler);
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy