package org.netbeans.gradle.project.others; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Arrays; import java.util.concurrent.atomic.AtomicReference; import org.jtrim.utils.ExceptionHelper; import org.netbeans.gradle.model.util.CollectionUtils; public final class PluginClassImplementation { private final ClassFinder type; private final InvocationHandler invocationHandler; private final AtomicReference<Object> instanceRef; public PluginClassImplementation( ClassFinder type, Object delegateInstance) { this(type, new SimpleDelegator(delegateInstance)); } public PluginClassImplementation( ClassFinder type, Object delegateInstance, InvocationHandlerFactory exceptionalCases) { this(type, new MultiDelegator(exceptionalCases, new SimpleDelegator(delegateInstance))); } private PluginClassImplementation( ClassFinder type, InvocationHandler invocationHandler) { ExceptionHelper.checkNotNullArgument(type, "type"); ExceptionHelper.checkNotNullArgument(invocationHandler, "invocationHandler"); this.type = type; this.invocationHandler = invocationHandler; this.instanceRef = new AtomicReference<>(null); } public Object tryGetAsPluginClass() { Object result = instanceRef.get(); if (result == null) { instanceRef.compareAndSet(null, tryCreateProxyInstance()); result = instanceRef.get(); } return result; } private Object tryCreateProxyInstance() { Class<?> pluginClass = type.tryGetClass(); if (pluginClass == null) { return null; } ClassLoader classLoader = pluginClass.getClassLoader(); return Proxy.newProxyInstance(classLoader, new Class<?>[]{pluginClass}, invocationHandler); } private static final class MultiDelegator implements InvocationHandler { private final InvocationHandlerFactory[] factories; public MultiDelegator(InvocationHandlerFactory... factories) { this.factories = factories.clone(); CollectionUtils.checkNoNullElements(Arrays.asList(this.factories), "factories"); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { for (InvocationHandlerFactory factory: factories) { InvocationHandler handler = factory.tryGetInvocationHandler(proxy, method, args); if (handler != null) { return handler.invoke(proxy, method, args); } } throw new UnsupportedOperationException("No handler for method: " + method); } } private static final class SimpleDelegator implements InvocationHandler, InvocationHandlerFactory { private final Object delegate; public SimpleDelegator(Object delegate) { ExceptionHelper.checkNotNullArgument(delegate, "delegate"); this.delegate = delegate; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Method delegateMethod = delegate.getClass().getMethod(method.getName(), method.getParameterTypes()); return delegateMethod.invoke(delegate, args); } @Override public InvocationHandler tryGetInvocationHandler(Object proxy, Method method, Object[] args) { return this; } } }