001/*
002 * Licensed under the Apache License, Version 2.0 (the "License");
003 * you may not use this file except in compliance with the License.
004 * You may obtain a copy of the License at
005 *
006 *     http://www.apache.org/licenses/LICENSE-2.0
007 *
008 * Unless required by applicable law or agreed to in writing, software
009 * distributed under the License is distributed on an "AS IS" BASIS,
010 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
011 * See the License for the specific language governing permissions and
012 * limitations under the License.
013 */
014package org.gbif.ws.client;
015
016import org.slf4j.Logger;
017import org.slf4j.LoggerFactory;
018
019import feign.RetryableException;
020import feign.Retryer;
021
022@SuppressWarnings("unused")
023public class ClientRetryer implements Retryer {
024
025  private static final Logger LOG = LoggerFactory.getLogger(ClientRetryer.class);
026
027  private final int maxAttempts;
028  private final long period;
029  private final double multiplier;
030  int attempt;
031  long sleptForMillis;
032
033  public ClientRetryer() {
034    this(1000, 3, 1.5);
035  }
036
037  public ClientRetryer(long period, int maxAttempts, double multiplier) {
038    this.period = period;
039    this.maxAttempts = maxAttempts;
040    this.multiplier = multiplier;
041    this.attempt = 1;
042  }
043
044  protected long currentTimeMillis() {
045    return System.currentTimeMillis();
046  }
047
048  @Override
049  public void continueOrPropagate(RetryableException e) {
050    if (attempt++ >= maxAttempts) {
051      LOG.error(
052          "All {} retry attempts failed. Giving up. Last execution was: '{}: {}'",
053          maxAttempts,
054          e.getClass().getSimpleName(),
055          e.getMessage());
056      throw e;
057    }
058
059    long interval;
060    if (e.retryAfter() != null) {
061      interval = e.retryAfter().getTime() - currentTimeMillis();
062      if (interval < 0) {
063        return;
064      }
065    } else {
066      interval = nextMaxInterval();
067    }
068    try {
069      Thread.sleep(interval);
070    } catch (InterruptedException ignored) {
071      Thread.currentThread().interrupt();
072      throw e;
073    }
074    sleptForMillis += interval;
075  }
076
077  /**
078   * Calculates the time interval to a retry attempt. <br>
079   * The interval increases exponentially with each attempt, at a rate of nextInterval *= multiplier
080   * (where multiplier is the backoff factor), to the maximum interval.
081   *
082   * @return time in nanoseconds from now until the next attempt.
083   */
084  long nextMaxInterval() {
085    return (long) (period * Math.pow(multiplier, attempt - 1));
086  }
087
088  @Override
089  public Retryer clone() {
090    return new ClientRetryer(period, maxAttempts, multiplier);
091  }
092}