package org.restler.spring.data.proxy; import com.google.common.collect.ImmutableMultimap; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.InvocationHandler; import org.restler.client.Call; import org.restler.client.CallExecutor; import org.restler.client.RestlerException; import org.restler.http.HttpCall; import org.restler.http.HttpMethod; import org.restler.util.UriBuilder; import org.springframework.beans.BeanUtils; import javax.persistence.EmbeddedId; import javax.persistence.Id; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.HashMap; public class ResourceProxyMaker { public Object make(Class<?> aClass, Object object, HashMap<String, String> hrefs) { net.sf.cglib.proxy.InvocationHandler handler = new ResourceInvocationHandler(object, hrefs); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(aClass); enhancer.setInterfaces(new Class<?>[]{ResourceProxy.class}); enhancer.setCallback(handler); Object proxy = enhancer.create(); try { for(Field objectField : aClass.getDeclaredFields()) { objectField.setAccessible(true); objectField.set(proxy, objectField.get(object)); objectField.setAccessible(false); } } catch (IllegalAccessException e) { throw new RestlerException("Illegal access to field.", e); } return proxy; } private Object getWrappedId(Object object, String id) { Field[] fields = object.getClass().getDeclaredFields(); Class fieldClass; for (Field field : fields) { if (field.getDeclaredAnnotation(Id.class) != null || field.getDeclaredAnnotation(EmbeddedId.class) != null) { fieldClass = field.getType(); field.setAccessible(true); try { return fieldClass.getConstructor(String.class).newInstance(id); } catch (IllegalAccessException e) { throw new RestlerException("Access denied to change id", e); } catch (InvocationTargetException e) { throw new RestlerException("Can't create id wrapper", e); } catch (NoSuchMethodException | InstantiationException e) { throw new RestlerException("Could not instantiate id object", e); } } } return null; } private String getHrefByMethod(Method method, HashMap<String, String> hrefs) { String methodName = method.getName(); if(methodName.startsWith("get")) { String hrefName = ""; if(methodName.length() > 3) { hrefName = methodName.substring(3, 4).toLowerCase() + methodName.substring(4); } return hrefs.get(hrefName); } return null; } private class ResourceInvocationHandler implements InvocationHandler { private Object object; private HashMap<String, String> hrefs; private CallExecutor executor; public ResourceInvocationHandler(Object object, HashMap<String, String> hrefs) { this.object = object; this.hrefs = hrefs; this.executor = null; } @Override public Object invoke(Object o, Method method, Object[] args) throws Throwable { if(method.equals(ResourceProxy.class.getMethod("getResourceId"))) { String self = hrefs.get("self"); return getWrappedId(object, self.substring(self.lastIndexOf("/")+1)); }else if(method.equals(ResourceProxy.class.getMethod("getRepositoryUri"))) { String self = hrefs.get("self"); return self.substring(0, self.lastIndexOf("/")); }else if(method.equals(ResourceProxy.class.getMethod("getSelfUri"))) { return hrefs.get("self"); }else if(method.equals(ResourceProxy.class.getMethod("getObject"))) { return object; }else if(method.equals(ResourceProxy.class.getMethod("getHrefs"))) { return hrefs; }else if(method.equals(ResourceProxy.class.getMethod("setExecutor", CallExecutor.class))) { executor = (CallExecutor) args[0]; return null; } if(executor == null) { throw new IllegalStateException("Executor must be initialized. For initialize executor use ProxyCallEnhancer."); } String uri = getHrefByMethod(method, hrefs); if(uri != null) { String fieldName = BeanUtils.findPropertyForMethod(method).getName(); Field field = object.getClass().getDeclaredField(fieldName); field.setAccessible(true); Call httpCall = new HttpCall(new UriBuilder(uri).build(), HttpMethod.GET, null, ImmutableMultimap.of(), method.getGenericReturnType()); Object newValue = executor.execute(httpCall); field.set(object, newValue); field.set(o, newValue); return field.get(object); } return method.invoke(object, args); } } }