package io.kaif.mobile.retrofit; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.concurrent.ConcurrentHashMap; import retrofit2.adapter.rxjava.HttpException; import retrofit2.http.GET; import rx.Observable; import rx.functions.Func1; /** * TODO * Generate this using annotation processor */ class RetryStaleHandler implements InvocationHandler { private ConcurrentHashMap<Method, MethodInfo> methodCache; private ConcurrentHashMap<Method, Method> retryMethodCache; private Object target; public RetryStaleHandler(Object target) { this.target = target; this.methodCache = new ConcurrentHashMap<>(); this.retryMethodCache = new ConcurrentHashMap<>(); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { MethodInfo methodInfo = getTargetMethodInfo(method); if (methodInfo.canRetry()) { Observable result = (Observable) methodInfo.invoke(target, args); return result.onErrorResumeNext((Func1<Throwable, Observable>) throwable -> { if (throwable instanceof HttpException) { try { return (Observable) getTargetCacheMethod(method).invoke(target, args); } catch (Exception e) { return Observable.error(throwable); } } return Observable.error(throwable); }); } return methodInfo.invoke(target, args); } private MethodInfo getTargetMethodInfo(Method method) throws NoSuchMethodException { MethodInfo methodInfo = methodCache.get(method); if (methodInfo == null) { Method targetMethod = target.getClass() .getMethod(method.getName(), method.getParameterTypes()); methodInfo = new MethodInfo(targetMethod, method.getReturnType().isAssignableFrom(Observable.class), method.isAnnotationPresent(GET.class)); methodCache.putIfAbsent(method, methodInfo); } return methodInfo; } private Method getTargetCacheMethod(Method method) throws NoSuchMethodException { Method targetMethod = retryMethodCache.get(method); if (targetMethod == null) { targetMethod = target.getClass() .getMethod(method.getName() + "$$RetryStale", method.getParameterTypes()); retryMethodCache.putIfAbsent(method, targetMethod); } return targetMethod; } }