All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
dev.vality.woody.api.proxy.ProxyInvocationHandler Maven / Gradle / Ivy
package dev.vality.woody.api.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.TreeMap;
import java.util.function.BiFunction;
public final class ProxyInvocationHandler implements InvocationHandler {
private final Map callMap;
private final InvocationTargetProvider targetProvider;
public ProxyInvocationHandler(Class iface, InvocationTargetProvider targetProvider,
MethodCallerFactory callerFactory, MethodCallInterceptor callInterceptor) {
this.targetProvider = targetProvider;
this.callMap = createCallMap(callInterceptor, targetProvider, iface, callerFactory);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
CallerBundle methodCallerBundle = callMap.get(method);
if (methodCallerBundle != null) {
return methodCallerBundle.interceptor.intercept(proxy, args, methodCallerBundle.caller);
} else {
return method.invoke(targetProvider.getTarget(), args);
}
}
private Map createCallMap(MethodCallInterceptor callInterceptor,
InvocationTargetProvider targetProvider, Class iface,
MethodCallerFactory callerFactory) {
Class targetType = targetProvider.getTargetType();
if (!iface.isAssignableFrom(targetType)) {
throw new IllegalArgumentException("Target object class doesn't implement referred interface");
}
Map callerMap = new TreeMap<>(MethodShadow.METHOD_COMPARATOR);
Method[] targetIfaceMethods = MethodShadow.getShadowedMethods(targetType, iface);
for (Method method : targetIfaceMethods) {
callerMap.put(MethodShadow.getSameMethod(method, iface),
new CallerBundle(callerFactory.getInstance(targetProvider, method), callInterceptor));
}
return addObjectMethods(iface, callerFactory, callerMap);
}
private Map addObjectMethods(Class iface, MethodCallerFactory callerFactory,
Map callerMap) {
SingleTargetProvider objTargetProvider =
new SingleTargetProvider(Object.class, this);//ref leak on init, assume it's a trusted code
MethodCallInterceptor directCallInterceptor = MethodCallInterceptors.directCallInterceptor();
BiFunction targetExtractor = (src, stub) -> {
InvocationHandler invocationHandler = null;
try {
invocationHandler = Proxy.getInvocationHandler(src);
if (!(invocationHandler instanceof ProxyInvocationHandler)) {
invocationHandler = null;
}
} catch (IllegalArgumentException ignored) {
//ignore
}
return invocationHandler == null ? stub : invocationHandler;
};
try {
//it's expected that this handler is bound only for one dedicated proxy
Method objMethod = objTargetProvider.getClass().getMethod("hashCode");
callerMap.put(objMethod, new CallerBundle(callerFactory.getInstance(objTargetProvider, objMethod,
(src, args) -> targetExtractor.apply(src, ProxyInvocationHandler.this).hashCode()),
directCallInterceptor));
objMethod = objTargetProvider.getClass().getMethod("toString");
callerMap.put(objMethod, new CallerBundle(callerFactory.getInstance(objTargetProvider, objMethod,
(src, args) -> iface.getName() + "@" +
targetExtractor.apply(src, ProxyInvocationHandler.this).hashCode()),
directCallInterceptor));
objMethod = objTargetProvider.getClass().getMethod("equals", Object.class);
callerMap.put(objMethod, new CallerBundle(callerFactory.getInstance(objTargetProvider, objMethod,
(src, args) -> targetExtractor.apply(args[0], null) == ProxyInvocationHandler.this),
directCallInterceptor));
} catch (NoSuchMethodException e) {
throw new IllegalStateException("Object methods're not found", e);
}
return callerMap;
}
private static class CallerBundle {
private final InstanceMethodCaller caller;
private final MethodCallInterceptor interceptor;
public CallerBundle(InstanceMethodCaller caller, MethodCallInterceptor interceptor) {
this.caller = caller;
this.interceptor = interceptor;
}
}
}