com.tinkerpop.frames.modules.javahandler.JavaHandlerModule Maven / Gradle / Ivy
Go to download
Windup Frames is an extension of the upstream Frames project, with tools to ease debugging and integration within windup.
package com.tinkerpop.frames.modules.javahandler;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.concurrent.ExecutionException;
import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.Proxy;
import javassist.util.proxy.ProxyFactory;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.tinkerpop.blueprints.Element;
import com.tinkerpop.blueprints.Graph;
import com.tinkerpop.frames.FramedGraph;
import com.tinkerpop.frames.FramedGraphConfiguration;
import com.tinkerpop.frames.modules.Module;
/**
*
* Adds support for calling Java methods to handle a frames call. A call to a
* frames method annotated with {@link JavaHandler} will cause the supplied
* factory to instantiate an instance of the handler class and call the
* appropriate method on it.
*
*
* The default implementation is an inner class of the frame interface named 'Impl'. For example:
*
*
* interface Person {
*
* @JavaHandler
* public String doSomething();
*
* abstract class Impl implements Person, JavaHandlerContext {
* public String doSomething() {
* return "Use Frames!";
* }
* }
*
* }
*
*
* The implementation class can be overridden by using the {@link JavaHandlerClass} annotation.
*
*
* @author Bryn Cooke
*/
public class JavaHandlerModule implements Module {
// We don't want to use the global class cache. Instead we cache the classes
// at the module level.
// Maps from frameClass -> proxy class.
private LoadingCache, Class>> classCache = CacheBuilder
.newBuilder().build(new CacheLoader, Class>>() {
@Override
public Class> load(final Class> frameClass) throws Exception {
final Class> handlerClass = getHandlerClass(frameClass);
ProxyFactory proxyFactory = new ProxyFactory() {
protected ClassLoader getClassLoader() {
return new URLClassLoader(new URL[0], handlerClass.getClassLoader());
}
};
proxyFactory.setUseCache(false);
proxyFactory.setSuperclass(handlerClass);
return proxyFactory.createClass();
}
});
private JavaHandlerFactory factory = new JavaHandlerFactory() {
@Override
public T create(Class handlerClass)
throws InstantiationException, IllegalAccessException {
return handlerClass.newInstance();
}
};
/**
* Provide an alternative factory for creating objects that handle frames
* calls.
*
* @param factory
* The factory to use.
* @return The module.
*/
public JavaHandlerModule withFactory(JavaHandlerFactory factory) {
this.factory = factory;
return this;
}
@Override
public Graph configure(Graph baseGraph, FramedGraphConfiguration config) {
config.addMethodHandler(new JavaMethodHandler(this));
config.addFrameInitializer(new JavaFrameInitializer(this));
return baseGraph;
}
Class> getHandlerClass(Class> frameClass)
throws ClassNotFoundException {
JavaHandlerClass handlerClass = frameClass
.getAnnotation(JavaHandlerClass.class);
if (handlerClass != null) {
return handlerClass.value();
}
if (frameClass.getClassLoader() == null) {
throw new ClassNotFoundException("FrameClass: " + frameClass.getCanonicalName() + " getClassLoader() returned null... no $Impl can be found in this case!");
}
return frameClass.getClassLoader().loadClass(
frameClass.getName() + "$Impl");
}
T createHandler(final Object framedElement, final FramedGraph> graph, final Element element, Class> frameClass,
final Method method) {
try {
Class implClass = (Class) classCache.get(frameClass);
T handler = factory.create(implClass);
((Proxy) handler).setHandler(new MethodHandler() {
private JavaHandlerContextImpl defaultJavahandlerImpl = new JavaHandlerContextImpl(
graph, method, element);
@Override
public Object invoke(Object o, Method m, Method proceed,
Object[] args) throws Throwable {
if (!Modifier.isAbstract(m.getModifiers())) {
return proceed.invoke(o, args);
} else {
if(m.getAnnotation(JavaHandler.class) != null) {
throw new JavaHandlerException("Method " + m + " is marked with @JavaHandler but is not implemented");
}
if (m.getDeclaringClass() == JavaHandlerContext.class) {
return m.invoke(defaultJavahandlerImpl, args);
}
return m.invoke(framedElement, args);
}
}
});
return handler;
} catch (ExecutionException e) {
Throwable cause = e.getCause();
if (cause instanceof ClassNotFoundException) {
throw new JavaHandlerException("Problem locating handler class for " + frameClass, e);
} else {
throw new JavaHandlerException(
"Cannot create class for handling framed method", cause);
}
} catch (InstantiationException e) {
throw new JavaHandlerException(
"Problem instantiating handler class", e);
} catch (IllegalAccessException e) {
throw new JavaHandlerException(
"Problem instantiating handler class", e);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy