Advertisement
vergepuppeter

RetryAdapter

Jan 25th, 2016
185
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.38 KB | None | 0 0
  1. import java.lang.annotation.Documented;
  2. import java.lang.annotation.Retention;
  3. import java.lang.annotation.Target;
  4.  
  5. import static java.lang.annotation.ElementType.METHOD;
  6. import static java.lang.annotation.RetentionPolicy.RUNTIME;
  7.  
  8. /**
  9. * Makes the Call retry on failure
  10. */
  11. @Documented
  12. @Target(METHOD)
  13. @Retention(RUNTIME)
  14. public @interface Retry {
  15. int value() default 3;
  16. }
  17.  
  18.  
  19. //////////////////////////////////////
  20.  
  21. import java.io.IOException;
  22. import java.lang.annotation.Annotation;
  23. import java.lang.reflect.Type;
  24. import java.util.Random;
  25. import java.util.concurrent.Executors;
  26. import java.util.concurrent.ScheduledExecutorService;
  27. import java.util.concurrent.TimeUnit;
  28.  
  29. import retrofit.Call;
  30. import retrofit.CallAdapter;
  31. import retrofit.Callback;
  32. import retrofit.Response;
  33. import retrofit.Retrofit;
  34.  
  35.  
  36. public class RetryCallAdapterFactory implements CallAdapter.Factory {
  37. private final ScheduledExecutorService mExecutor;
  38.  
  39. private RetryCallAdapterFactory() {
  40. mExecutor = Executors.newScheduledThreadPool(1);
  41. }
  42.  
  43. public static RetryCallAdapterFactory create() {
  44. return new RetryCallAdapterFactory();
  45. }
  46.  
  47. @Override
  48. public CallAdapter<?> get(final Type returnType, Annotation[] annotations, Retrofit retrofit) {
  49. boolean hasRetryAnnotation = false;
  50. int value = 0;
  51. for (Annotation annotation : annotations) {
  52. if (annotation instanceof Retry) {
  53. hasRetryAnnotation = true;
  54. value = ((Retry) annotation).value();
  55. }
  56. }
  57. final boolean shouldRetryCall = hasRetryAnnotation;
  58. final int maxRetries = value;
  59. final CallAdapter<?> delegate = retrofit.nextCallAdapter(this, returnType, annotations);
  60. return new CallAdapter<Object>() {
  61. @Override
  62. public Type responseType() {
  63. return delegate.responseType();
  64. }
  65.  
  66. @Override
  67. public <R> Object adapt(Call<R> call) {
  68. return delegate.adapt(shouldRetryCall ? new RetryingCall<>(call, mExecutor, maxRetries) : call);
  69. }
  70. };
  71. }
  72.  
  73. static final class RetryingCall<T> implements Call<T> {
  74. private final Call<T> mDelegate;
  75. private final ScheduledExecutorService mExecutor;
  76. private final int mMaxRetries;
  77.  
  78. public RetryingCall(Call<T> delegate, ScheduledExecutorService executor, int maxRetries) {
  79. mDelegate = delegate;
  80. mExecutor = executor;
  81. mMaxRetries = maxRetries;
  82. }
  83.  
  84. @Override
  85. public Response<T> execute() throws IOException {
  86. return mDelegate.execute();
  87. }
  88.  
  89. @Override
  90. public void enqueue(Callback<T> callback) {
  91. mDelegate.enqueue(new RetryingCallback<>(mDelegate, callback, mExecutor, mMaxRetries));
  92. }
  93.  
  94. @Override
  95. public void cancel() {
  96. mDelegate.cancel();
  97. }
  98.  
  99. @SuppressWarnings("CloneDoesntCallSuperClone" /* Performing deep clone */)
  100. @Override
  101. public Call<T> clone() {
  102. return new RetryingCall<>(mDelegate.clone(), mExecutor, mMaxRetries);
  103. }
  104. }
  105.  
  106. // Exponential backoff approach from https://developers.google.com/drive/web/handle-errors
  107. static final class RetryingCallback<T> implements Callback<T> {
  108. private static Random random = new Random();
  109. private final int mMaxRetries;
  110. private final Call<T> mCall;
  111. private final Callback<T> mDelegate;
  112. private final ScheduledExecutorService mExecutor;
  113. private final int mRetries;
  114.  
  115. RetryingCallback(Call<T> call, Callback<T> delegate, ScheduledExecutorService executor, int maxRetries) {
  116. this(call, delegate, executor, maxRetries, 0);
  117. }
  118.  
  119. RetryingCallback(Call<T> call, Callback<T> delegate, ScheduledExecutorService executor, int maxRetries, int retries) {
  120. mCall = call;
  121. mDelegate = delegate;
  122. mExecutor = executor;
  123. mMaxRetries = maxRetries;
  124. mRetries = retries;
  125. }
  126.  
  127. @Override
  128. public void onResponse(Response<T> response, Retrofit retrofit) {
  129. mDelegate.onResponse(response, retrofit);
  130. }
  131.  
  132. @Override
  133. public void onFailure(Throwable throwable) {
  134. // Retry failed request
  135. if (mRetries < mMaxRetries) {
  136. retryCall();
  137. } else {
  138. mDelegate.onFailure(new TimeoutError(throwable));
  139. }
  140. }
  141.  
  142. private void retryCall() {
  143. mExecutor.schedule(new Runnable() {
  144. @Override
  145. public void run() {
  146. final Call<T> call = mCall.clone();
  147. call.enqueue(new RetryingCallback<>(call, mDelegate, mExecutor, mMaxRetries, mRetries + 1));
  148. }
  149. }, (1 << mRetries) * 1000 + random.nextInt(1001), TimeUnit.MILLISECONDS);
  150. }
  151. }
  152. }
  153.  
  154.  
  155. //////////////////////////////////////////////
  156.  
  157. import java.io.IOException;
  158.  
  159. public class TimeoutError extends IOException {
  160. private static final long serialVersionUID = -6469766654369165864L;
  161.  
  162. public TimeoutError() {
  163. super();
  164. }
  165.  
  166. public TimeoutError(Throwable cause) {
  167. super(cause);
  168. }
  169. }
  170.  
  171. /////////////////////////////////////////
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement