package net.contextfw.remoting.fluent;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Stack;
public final class RemotingProxy implements InvocationHandler {
private final Class<?> remotedInterface;
private final RemotingConnection connection;
private ProxiedInvocation previous;
private ProxiedInvocation first;
private final boolean isFirst;
private RemotingProxy(boolean isFirst, RemotingConnection connection, Class<?> remotedInterface, ProxiedInvocation first,
ProxiedInvocation previous) {
this.connection = connection;
this.remotedInterface = remotedInterface;
this.first = first;
this.previous = previous;
this.isFirst = isFirst;
}
@SuppressWarnings("unchecked")
private static <T> T createProxy(boolean isFirst, RemotingConnection connection, Class<T> remotedInterface,
ProxiedInvocation first, ProxiedInvocation previous) {
return (T) Proxy.newProxyInstance(remotedInterface.getClassLoader(), new Class[] { remotedInterface },
new RemotingProxy(isFirst, connection, remotedInterface, first, previous));
}
@Override
public Object invoke(Object proxy, Method method, Object[] parameters) throws Throwable {
Class<?> returnType = method.getReturnType();
String methodName = method.getName();
InvocationError error = ProxiedInvocation.getNonAllowedMethodError(methodName);
if (error == null) {
ProxiedInvocation current = new ProxiedInvocation(remotedInterface,
method.getName(), method.getParameterTypes(), parameters);
if (isFirst) {
first = current;
previous = current;
}
else {
previous.setNextInvocation(current);
}
if (returnType != null && isRemoted(returnType)) {
return createProxy(false, connection, returnType, first, current);
} else {
return first.invokeRemoted(connection);
}
}
else {
throw new RemotingException(error);
}
}
private boolean isRemoted(Class<?> type) {
if (!type.isInterface()) {
return false;
}
Stack<Class<?>> stack = new Stack<Class<?>>();
stack.add(type);
while (!stack.isEmpty()) {
Class<?> current = stack.pop();
if (current.isAnnotationPresent(Remoted.class)) {
return true;
}
else {
for (Class<?> superType : current.getInterfaces()) {
stack.push(superType);
}
}
}
return false;
}
public static <T> T createProxy(Class<T> remotedInterface, RemotingConnection connection) {
return RemotingProxy.createProxy(true, connection, remotedInterface, null, null);
}
}