001package com.box.sdkgen.networking.retries;
002
003import static com.box.sdkgen.internal.utils.UtilsManager.random;
004
005import com.box.sdkgen.networking.fetchoptions.FetchOptions;
006import com.box.sdkgen.networking.fetchresponse.FetchResponse;
007
008public class BoxRetryStrategy implements RetryStrategy {
009
010  public int maxAttempts;
011
012  public double retryRandomizationFactor;
013
014  public double retryBaseInterval;
015
016  public int maxRetriesOnException;
017
018  public BoxRetryStrategy() {
019    this.maxAttempts = 5;
020    this.retryRandomizationFactor = 0.5d;
021    this.retryBaseInterval = 1d;
022    this.maxRetriesOnException = 2;
023  }
024
025  protected BoxRetryStrategy(Builder builder) {
026    this.maxAttempts = builder.maxAttempts;
027    this.retryRandomizationFactor = builder.retryRandomizationFactor;
028    this.retryBaseInterval = builder.retryBaseInterval;
029    this.maxRetriesOnException = builder.maxRetriesOnException;
030  }
031
032  @Override
033  public boolean shouldRetry(
034      FetchOptions fetchOptions, FetchResponse fetchResponse, int attemptNumber) {
035    if (fetchResponse.getStatus() == 0) {
036      return attemptNumber <= this.maxRetriesOnException;
037    }
038    boolean isSuccessful = fetchResponse.getStatus() >= 200 && fetchResponse.getStatus() < 400;
039    String retryAfterHeader =
040        (fetchResponse.getHeaders().containsKey("Retry-After")
041            ? fetchResponse.getHeaders().get("Retry-After")
042            : null);
043    boolean isAcceptedWithRetryAfter =
044        fetchResponse.getStatus() == 202 && !(retryAfterHeader == null);
045    if (attemptNumber >= this.maxAttempts) {
046      return false;
047    }
048    if (isAcceptedWithRetryAfter) {
049      return true;
050    }
051    if (fetchResponse.getStatus() >= 500) {
052      return true;
053    }
054    if (fetchResponse.getStatus() == 429) {
055      return true;
056    }
057    if (fetchResponse.getStatus() == 401 && !(fetchOptions.getAuth() == null)) {
058      fetchOptions.getAuth().refreshToken(fetchOptions.getNetworkSession());
059      return true;
060    }
061    if (isSuccessful) {
062      return false;
063    }
064    return false;
065  }
066
067  @Override
068  public double retryAfter(
069      FetchOptions fetchOptions, FetchResponse fetchResponse, int attemptNumber) {
070    String retryAfterHeader =
071        (fetchResponse.getHeaders().containsKey("Retry-After")
072            ? fetchResponse.getHeaders().get("Retry-After")
073            : null);
074    if (!(retryAfterHeader == null)) {
075      return Double.parseDouble(retryAfterHeader);
076    }
077    double randomization =
078        random(1 - this.retryRandomizationFactor, 1 + this.retryRandomizationFactor);
079    double exponential = Math.pow(2, attemptNumber);
080    return exponential * this.retryBaseInterval * randomization;
081  }
082
083  public int getMaxAttempts() {
084    return maxAttempts;
085  }
086
087  public double getRetryRandomizationFactor() {
088    return retryRandomizationFactor;
089  }
090
091  public double getRetryBaseInterval() {
092    return retryBaseInterval;
093  }
094
095  public int getMaxRetriesOnException() {
096    return maxRetriesOnException;
097  }
098
099  public static class Builder {
100
101    protected Integer maxAttempts;
102
103    protected Double retryRandomizationFactor;
104
105    protected Double retryBaseInterval;
106
107    protected Integer maxRetriesOnException;
108
109    public Builder() {}
110
111    public Builder maxAttempts(int maxAttempts) {
112      this.maxAttempts = maxAttempts;
113      return this;
114    }
115
116    public Builder retryRandomizationFactor(double retryRandomizationFactor) {
117      this.retryRandomizationFactor = retryRandomizationFactor;
118      return this;
119    }
120
121    public Builder retryBaseInterval(double retryBaseInterval) {
122      this.retryBaseInterval = retryBaseInterval;
123      return this;
124    }
125
126    public Builder maxRetriesOnException(int maxRetriesOnException) {
127      this.maxRetriesOnException = maxRetriesOnException;
128      return this;
129    }
130
131    public BoxRetryStrategy build() {
132      if (this.maxAttempts == null) {
133        this.maxAttempts = 5;
134      }
135      if (this.retryRandomizationFactor == null) {
136        this.retryRandomizationFactor = 0.5d;
137      }
138      if (this.retryBaseInterval == null) {
139        this.retryBaseInterval = 1d;
140      }
141      if (this.maxRetriesOnException == null) {
142        this.maxRetriesOnException = 2;
143      }
144      return new BoxRetryStrategy(this);
145    }
146  }
147}