package uk.ac.cam.echo.client;
import org.glassfish.jersey.client.proxy.WebResourceFactory;
import org.glassfish.jersey.internal.util.ReflectionHelper;
import org.glassfish.jersey.media.sse.EventOutput;
import uk.ac.cam.echo.client.data.BaseData;
import uk.ac.cam.echo.data.Message;
import uk.ac.cam.echo.data.async.Handler;
import uk.ac.cam.echo.data.async.Subscription;
import uk.ac.cam.echo.data.resources.Resource;
import javax.ws.rs.client.WebTarget;
import java.lang.reflect.*;
import java.security.AccessController;
import java.util.Collection;
public class ResourceFactory implements InvocationHandler{
private final Object subResource;
private final ClientApi api;
private ResourceFactory(Object c, ClientApi api) {
subResource = c;
this.api = api;
}
public static Object newResource(Class<?> resourceInterface, Object resource, ClientApi api) {
return Proxy.newProxyInstance(AccessController.doPrivileged(ReflectionHelper.getClassLoaderPA(resourceInterface)),
new Class[]{resourceInterface},
new ResourceFactory(resource, api));
}
public static Object newResource(Class<?> resourceInterface, ClientApi api) {
return newResource(resourceInterface,
WebResourceFactory.newResource(resourceInterface, api.getServer()), api);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// System.out.println(method.getReturnType());
if (method.getReturnType() == EventOutput.class)
throw new RuntimeException("You can't call this from the client");
if (method.getReturnType() == Subscription.class) {
ParameterizedType type = (ParameterizedType) args[0].getClass().getGenericInterfaces()[0];
Class clazz = (Class) type.getActualTypeArguments()[0];
InvocationHandler ih = Proxy.getInvocationHandler(subResource);
Field targetField = ih.getClass().getDeclaredField("target");
targetField.setAccessible(true);
return new ClientSubscription(api,
(WebTarget) targetField.get(ih), (Handler<Message>)args[0], clazz);
}
Object res = method.invoke(subResource, args);
if (res instanceof Resource)
res = newResource(res.getClass().getInterfaces()[0], res, api);
if (res instanceof BaseData)
((BaseData) res).setApi(api);
if (res instanceof Collection)
for (Object s: (Collection) res) {
if (s instanceof BaseData)
((BaseData) s).setApi(api);
}
return res;
}
}