|
| static final Method OBJECT_TOSTRING; |
| static final Method OBJECT_HASHCODE; |
| static final Method OBJECT_EQUALS; |
|
| static { |
| try { |
| OBJECT_TOSTRING = Object.class.getMethod("toString"); |
| OBJECT_HASHCODE= Object.class.getMethod("hashCode"); |
| OBJECT_EQUALS = Object.class.getMethod("equals", Object.class); |
| } catch (Exception e) { |
| throw new Error("Error retrieving Object.toString() method"); |
| } |
| } |
|
| /** |
| * FunctionInfo has to be immutable to to make the object visible |
| * to other threads fully initialized. This is a prerequisite for |
| * using the class in the double checked locking scenario of {@link Handler#invoke(Object, Method, Object[])} |
| */ |
| private static final class FunctionInfo { |
| final InvocationHandler handler; |
| final Function function; |
| final boolean isVarArgs; |
| final Object methodHandle; |
| final Map<String, ?> options; |
| final Class<?>[] parameterTypes; |
|
| FunctionInfo(Object mh) { |
| this.handler = null; |
| this.function = null; |
| this.isVarArgs = false; |
| this.options = null; |
| this.parameterTypes = null; |
| this.methodHandle = mh; |
| } |
|
| FunctionInfo(InvocationHandler handler, Function function, Class\[\] parameterTypes, boolean isVarArgs, Map options) { |
| this.handler = handler; |
| this.function = function; |
| this.isVarArgs = isVarArgs; |
| this.options = options; |
| this.parameterTypes = parameterTypes; |
| this.methodHandle = null; |
| } |
| } |
|
| private final NativeLibrary nativeLibrary; |
| private final Class<?> interfaceClass; |
| // Library invocation options |
| private final Map<String, Object> options; |
| private final InvocationMapper invocationMapper; |
| private final Map<Method, FunctionInfo> functions = new WeakHashMap<Method, FunctionInfo>(); |
| public Handler(String libname, Class interfaceClass, Map options) { |
|
| if (libname != null && "".equals(libname.trim())) { |
| throw new IllegalArgumentException("Invalid library name \"" + libname + "\""); |
| } |
|
| if (!interfaceClass.isInterface()) { |
| throw new IllegalArgumentException(libname + " does not implement an interface: " + interfaceClass.getName()); |
| } |
|
| this.interfaceClass = interfaceClass; |
| this.options = new HashMap<String, Object>(options); |
| int callingConvention = AltCallingConvention.class.isAssignableFrom(interfaceClass) |
| ? Function.ALT_CONVENTION |
| : Function.C_CONVENTION; |
| if (this.options.get(OPTION_CALLING_CONVENTION) == null) { |
| this.options.put(OPTION_CALLING_CONVENTION, Integer.valueOf(callingConvention)); |
| } |
| if (this.options.get(OPTION_CLASSLOADER) == null) { |
| this.options.put(OPTION_CLASSLOADER, interfaceClass.getClassLoader()); |
| } |
| this.nativeLibrary = NativeLibrary.getInstance(libname, this.options); |
| invocationMapper = (InvocationMapper)this.options.get(OPTION_INVOCATION_MAPPER); |
| } |
|
| public NativeLibrary getNativeLibrary() { |
| return nativeLibrary; |
| } |
|
| public String getLibraryName() { |
| return nativeLibrary.getName(); |
| } |
|
| public Class<?> getInterfaceClass() { |
| return interfaceClass; |
| } |
|
| @Override |
| public Object invoke(Object proxy, Method method, Object[] inArgs) |
| throws Throwable { |
|
| // Intercept Object methods |
| if (OBJECT_TOSTRING.equals(method)) { |
| return "Proxy interface to " + nativeLibrary; |
| } else if (OBJECT_HASHCODE.equals(method)) { |
| return Integer.valueOf(hashCode()); |
| } else if (OBJECT_EQUALS.equals(method)) { |
| Object o = inArgs[0]; |
| if (o != null && Proxy.isProxyClass(o.getClass())) { |
| return Function.valueOf(Proxy.getInvocationHandler(o) == this); |
| } |
| return Boolean.FALSE; |
| } |
|
| // Using the double-checked locking pattern to speed up function calls |
| FunctionInfo f = functions.get(method); |
| if(f == null) { |
| synchronized(functions) { |
| f = functions.get(method); |
| if (f == null) { |
| boolean isDefault = ReflectionUtils.isDefault(method); |
| if(! isDefault) { |
| boolean isVarArgs = Function.isVarArgs(method); |
| InvocationHandler handler = null; |
| if (invocationMapper != null) { |
| handler = invocationMapper.getInvocationHandler(nativeLibrary, method); |
| } |
| Function function = null; |
| Class<?>[] parameterTypes = null; |
| Map<String, Object> options = null; |
| if (handler == null) { |
| // Find the function to invoke |
| function = nativeLibrary.getFunction(method.getName(), method); |
| parameterTypes = method.getParameterTypes(); |
| options = new HashMap<String, Object>(this.options); |
| options.put(Function.OPTION_INVOKING_METHOD, method); |
| } |
| f = new FunctionInfo(handler, function, parameterTypes, isVarArgs, options); |
| } else { |
| f = new FunctionInfo(ReflectionUtils.getMethodHandle(method)); |
| } |
| functions.put(method, f); |
| } |
| } |
| } |
| if (f.methodHandle != null) { |
| return ReflectionUtils.invokeDefaultMethod(proxy, f.methodHandle, inArgs); |
| } else { |
| if (f.isVarArgs) { |
| inArgs = Function.concatenateVarArgs(inArgs); |
| } |
| if (f.handler != null) { |
| return f.handler.invoke(proxy, method, inArgs); |
| } |
| return f.function.invoke(method, f.parameterTypes, method.getReturnType(), inArgs, f.options); |
| } |
| } |
| } |