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}