Invocation Handler
At first I create Invocation handler implementation in Jython:
from java.lang.reflect import InvocationHandler
class DelegateInvocationHandler(InvocationHandler):
def __init__(self, delegateto):
self.delegateto = delegateto
def invoke(self, proxy, method, args):
name = method.getName()
func = getattr(self.delegateto, name)
params = method.getParameterTypes()
if params == None or len(params) == 0:
return func()
else:
return func(*args)
- delegateto is a jython (or java) object which will handle interface method invocations
- we should check target function to arguments count and if necessary use multiple paramemeters, i.e. *-sign for arguments decomposition (args is an array but we have to pass each array element as separate argument to the method)
In Java utility class we create an instance of proxy with the given handler instance from Jython, i.e.:
public class ProxyUtil {
public static Object createProxy(Class[] interfaces, InvocationHandler h) throws Exception {
ClassLoader loader = ProxyUtil.class.getClassLoader();
return Proxy.newProxyInstance(loader, interfaces, h);
}
}
And we can create Proxy in Jython now: ProxyUtil.createProxy([ProcessContext], DelegateInvocationHandler(debugContext))
- ProcessContext is a package private interface
- debugContext is an instance of class defined in Jython
Set of problems you can get in a case if your methods use primitives or its object wrappers as return types, i.e. the following inerface usage
interface ProcessContext {
boolean isInProgress();
}
can cause the following problems (and it doesn't matter where method call will be performed in Java code or Jython): java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.BooleanFor other cases you can get exception look like this:
java.lang.ClassCastException: java.math.BigDecimal cannot be cast to java.lang.Doubleand others.
Solution
I created wrapper for Jython Invocation handler for coercing data types:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class PyTypeToJavaTypeCoerceInvocationHandler implements InvocationHandler {
private InvocationHandler wrapped;
public PyTypeToJavaTypeCoerceInvocationHandler(InvocationHandler h) {
super();
this.wrapped = h;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = this.wrapped.invoke(proxy, method, args);
if (result != null) {
Class actualType = method.getReturnType();
Class returnedType = result.getClass();
if (isBoolean(actualType) && !isBoolean(returnedType)) {
int value = ((Number) result).intValue();
return Boolean.valueOf(value == 1);
} else if (isLong(actualType) && !isLong(returnedType)) {
Number n = (Number) result;
return Long.valueOf(n.longValue());
} else if (isInteger(actualType) && !isInteger(returnedType)) {
Number n = (Number) result;
return Integer.valueOf(n.intValue());
} else if (isDouble(actualType) && !isDouble(returnedType)) {
Number n = (Number) result;
return Double.valueOf(n.doubleValue());
} else if (isFloat(actualType) && !isFloat(returnedType)) {
Number n = (Number) result;
return Float.valueOf(n.floatValue());
} else if (isByte(actualType) && !isByte(returnedType)) {
Number n = (Number) result;
return Byte.valueOf(n.byteValue());
} else if (isShort(actualType) && !isShort(returnedType)) {
Number n = (Number) result;
return Short.valueOf(n.shortValue());
}
}
return result;
}
private boolean isShort(Class type) {
return type == Short.TYPE || type == Short.class;
}
private boolean isByte(Class type) {
return type == Byte.TYPE || type == Byte.class;
}
private boolean isFloat(Class type) {
return type == Float.TYPE || type == Float.class;
}
private boolean isDouble(Class type) {
return type == Double.TYPE || type == Double.class;
}
private boolean isLong(Class type) {
return type == Long.TYPE || type == Long.class;
}
private boolean isBoolean(Class type) {
return type == Boolean.TYPE || type == Boolean.class;
}
private boolean isInteger(Class type) {
return type == Integer.TYPE || type == Integer.class;
}
}
and wrapped the given Invocation Handler from Jython like this: public class ProxyUtil {
public static Object createProxy(Class[] interfaces, InvocationHandler h) throws Exception {
ClassLoader loader = ProxyUtil.class.getClassLoader();
InvocationHandler wrapper = new PyTypeToJavaTypeCoerceInvocationHandler(h);
return Proxy.newProxyInstance(loader, interfaces, wrapper);
}
}
And all works fine now!
