View Javadoc
1   /*
2    * Licensed under the Apache License, Version 2.0 (the "License");
3    * you may not use this file except in compliance with the License.
4    * You may obtain a copy of the License at
5    *
6    *     http://www.apache.org/licenses/LICENSE-2.0
7    *
8    * Unless required by applicable law or agreed to in writing, software
9    * distributed under the License is distributed on an "AS IS" BASIS,
10   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11   * See the License for the specific language governing permissions and
12   * limitations under the License.
13   */
14  package org.gbif.ws.client;
15  
16  import org.slf4j.Logger;
17  import org.slf4j.LoggerFactory;
18  
19  import feign.RetryableException;
20  import feign.Retryer;
21  
22  @SuppressWarnings("unused")
23  public class ClientRetryer implements Retryer {
24  
25    private static final Logger LOG = LoggerFactory.getLogger(ClientRetryer.class);
26  
27    private final int maxAttempts;
28    private final long period;
29    private final double multiplier;
30    int attempt;
31    long sleptForMillis;
32  
33    public ClientRetryer() {
34      this(1000, 3, 1.5);
35    }
36  
37    public ClientRetryer(long period, int maxAttempts, double multiplier) {
38      this.period = period;
39      this.maxAttempts = maxAttempts;
40      this.multiplier = multiplier;
41      this.attempt = 1;
42    }
43  
44    protected long currentTimeMillis() {
45      return System.currentTimeMillis();
46    }
47  
48    @Override
49    public void continueOrPropagate(RetryableException e) {
50      if (attempt++ >= maxAttempts) {
51        LOG.error(
52            "All {} retry attempts failed. Giving up. Last execution was: '{}: {}'",
53            maxAttempts,
54            e.getClass().getSimpleName(),
55            e.getMessage());
56        throw e;
57      }
58  
59      long interval;
60      if (e.retryAfter() != null) {
61        interval = e.retryAfter().getTime() - currentTimeMillis();
62        if (interval < 0) {
63          return;
64        }
65      } else {
66        interval = nextMaxInterval();
67      }
68      try {
69        Thread.sleep(interval);
70      } catch (InterruptedException ignored) {
71        Thread.currentThread().interrupt();
72        throw e;
73      }
74      sleptForMillis += interval;
75    }
76  
77    /**
78     * Calculates the time interval to a retry attempt. <br>
79     * The interval increases exponentially with each attempt, at a rate of nextInterval *= multiplier
80     * (where multiplier is the backoff factor), to the maximum interval.
81     *
82     * @return time in nanoseconds from now until the next attempt.
83     */
84    long nextMaxInterval() {
85      return (long) (period * Math.pow(multiplier, attempt - 1));
86    }
87  
88    @Override
89    public Retryer clone() {
90      return new ClientRetryer(period, maxAttempts, multiplier);
91    }
92  }