package org.restler.spring.data.proxy;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.restler.client.*;
import org.restler.util.Pair;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class ProxyCallEnhancer implements CallEnhancer {
private final Cache<Pair<Class<?>, Object>, Object> cache;
public ProxyCallEnhancer(long maxCacheSize) {
cache = CacheBuilder.newBuilder().
maximumSize(maxCacheSize).
weakValues().
build();
}
@Override
public Object apply(Call call, CallExecutor callExecutor) {
Object object = callExecutor.execute(call);
Object result = object;
if(object instanceof Object[]) {
Object[] array = (Object[])object;
for(int i = 0; i < array.length; ++i) {
initProxyObject(array[i], callExecutor);
array[i] = getResourceFromCache(array[i]);
}
} else if(object instanceof Collection) {
Collection collection = (Collection) object;
Collection resultCollection = null;
try {
resultCollection = collection.getClass().getConstructor().newInstance();
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
new RestlerException("Can't get default constructor for collection. " + collection, e);
}
for(Object item : collection) {
initProxyObject(item, callExecutor);
resultCollection.add(getResourceFromCache(item));
}
result = resultCollection;
} else {
initProxyObject(object, callExecutor);
result = getResourceFromCache(object);
}
return result;
}
private void initProxyObject(Object object, CallExecutor callExecutor) {
if(object instanceof ResourceProxy) {
List<CallEnhancer> proxyEnhancer = new ArrayList<>();
proxyEnhancer.add(this);
CallExecutionChain chain = new CallExecutionChain(callExecutor, proxyEnhancer);
ResourceProxy resourceProxy = (ResourceProxy)object;
resourceProxy.setExecutor(chain);
}
}
private Object getResourceFromCache(Object object) {
if(object instanceof ResourceProxy) {
ResourceProxy resource = (ResourceProxy)object;
Class<?> clazz = resource.getObject().getClass();
Object id = resource.getResourceId();
Object value = cache.getIfPresent(new Pair<Class<?>, Object>(clazz, id));
if (value != null) {
return value;
} else {
cache.put(new Pair<>(clazz, id), resource);
return resource;
}
}
return object;
}
}