001/** 002 * Powerunit - A JDK1.8 test framework 003 * Copyright (C) 2014 Mathieu Boretti. 004 * 005 * This file is part of Powerunit 006 * 007 * Powerunit is free software: you can redistribute it and/or modify 008 * it under the terms of the GNU General Public License as published by 009 * the Free Software Foundation, either version 3 of the License, or 010 * (at your option) any later version. 011 * 012 * Powerunit is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 015 * GNU General Public License for more details. 016 * 017 * You should have received a copy of the GNU General Public License 018 * along with Powerunit. If not, see <http://www.gnu.org/licenses/>. 019 */ 020package ch.powerunit.extensions.async.lang; 021 022import static java.util.Objects.requireNonNull; 023 024import java.time.Duration; 025import java.util.concurrent.TimeUnit; 026import java.util.function.IntToLongFunction; 027import java.util.function.Supplier; 028 029/** 030 * Helpers methods to build {@link RetryPolicy}. 031 * 032 * @since 1.0.0 033 * 034 */ 035public final class RetryPolicies { 036 037 /** 038 * Retry Policy to just do one try. 039 * 040 */ 041 public static final RetryPolicy RETRY_ONLY_ONCE = of(1, 1); 042 043 private RetryPolicies() { 044 } 045 046 /** 047 * Create a new RetryPolicy with constant wait time. 048 * <p> 049 * For example, 050 * 051 * <pre> 052 * RetryPolicy incremental = RetryPolicies.of(3, 12); 053 * </pre> 054 * 055 * Then following of (re-)tries will be (maximum retries): 056 * <ol> 057 * <li>Do a first try</li> 058 * <li>Wait 12 ms</li> 059 * <li>Do a second retry</li> 060 * <li>Wait 12 ms</li> 061 * <li>Do a third retry</li> 062 * </ol> 063 * 064 * @param count 065 * the number of retry. 066 * @param ms 067 * the constant wait time in ms. 068 * @return the RetryPolicy 069 */ 070 public static RetryPolicy of(int count, long ms) { 071 return of(count, addToString(l -> ms, () -> String.format("Constant wait time of %s ms", ms))); 072 } 073 074 /** 075 * Create a new RetryPolicy with constant wait time. 076 * <p> 077 * For example, 078 * 079 * <pre> 080 * RetryPolicy incremental = RetryPolicies.of(3, 15, TimeUnit.MILLISECONDS); 081 * </pre> 082 * 083 * Then following of (re-)tries will be (maximum retries): 084 * <ol> 085 * <li>Do a first try</li> 086 * <li>Wait 15 ms</li> 087 * <li>Do a second retry</li> 088 * <li>Wait 15 ms</li> 089 * <li>Do a third retry</li> 090 * </ol> 091 * 092 * @param count 093 * the number of retry. 094 * @param value 095 * the wait time 096 * @param unit 097 * the unit of the wait time. 098 * @return the RetryPolicy 099 */ 100 public static RetryPolicy of(int count, long value, TimeUnit unit) { 101 return of(count, requireNonNull(unit, "unit can't be null").toMillis(value)); 102 } 103 104 /** 105 * Create a new RetryPolicy with constant wait time. 106 * <p> 107 * For example, 108 * 109 * <pre> 110 * RetryPolicy incremental = RetryPolicies.of(3, Duration.ofMillis(10)); 111 * </pre> 112 * 113 * Then following of (re-)tries will be (maximum retries): 114 * <ol> 115 * <li>Do a first try</li> 116 * <li>Wait 10 ms</li> 117 * <li>Do a second retry</li> 118 * <li>Wait 10 ms</li> 119 * <li>Do a third retry</li> 120 * </ol> 121 * 122 * @param count 123 * the number of retry. 124 * @param duration 125 * the constant duration to wait. 126 * @return the RetryPolicy 127 */ 128 public static RetryPolicy of(int count, Duration duration) { 129 return of(count, requireNonNull(duration, "duration can't be null").toMillis()); 130 } 131 132 /** 133 * Create a new RetryPolicy, by using a generic function to compute the retry 134 * duration. 135 * <p> 136 * For example, using this definition : 137 * 138 * <pre> 139 * RetryPolicy incremental = RetryPolicies.of(3, c -> (long) (Math.random() * 100)); 140 * </pre> 141 * 142 * Then following of (re-)tries will be (maximum retries): 143 * <ol> 144 * <li>Do a first try</li> 145 * <li>Wait a random time between 0 and 100 ms</li> 146 * <li>Do a second retry</li> 147 * <li>Wait a random time between 0 and 100 ms</li> 148 * <li>Do a third retry</li> 149 * </ol> 150 * 151 * @param count 152 * the number of retry. A value of 1 is to do only one try, without 153 * sleep. 154 * @param retryToWaitTime 155 * the function to compute the wait time based on the retry. The 156 * received value by this function starts with 1, and this will be 157 * the first retry (meaning a the initial try has been done before). 158 * @return the RetryPolicy 159 */ 160 public static RetryPolicy of(int count, IntToLongFunction retryToWaitTime) { 161 return new RetryPolicy() { 162 163 @Override 164 public void sleepBetweenRetry(int retry) { 165 RetryPolicies.sleepBetweenRetry(retryToWaitTime.applyAsLong(retry)); 166 } 167 168 @Override 169 public int getCount() { 170 return count; 171 } 172 173 @Override 174 public String toString() { 175 return String.format("total count = %s, with sleep method = %s", count, retryToWaitTime); 176 } 177 }; 178 } 179 180 /** 181 * Create a new RetryPolicy, that wait each time more time : first time the 182 * received duration, second time twice, etc. 183 * <p> 184 * For example, using this definition : 185 * 186 * <pre> 187 * RetryPolicy incremental = RetryPolicies.ofIncremental(3, 20); 188 * </pre> 189 * 190 * Then following of (re-)tries will be (maximum retries): 191 * <ol> 192 * <li>Do a first try</li> 193 * <li>Wait 20ms</li> 194 * <li>Do a second retry</li> 195 * <li>Wait 40ms</li> 196 * <li>Do a third retry</li> 197 * </ol> 198 * 199 * @param count 200 * the number of retry. 201 * @param ms 202 * the time in ms that will be combined with the retry number 203 * @return the RetryPolicy 204 */ 205 public static RetryPolicy ofIncremental(int count, long ms) { 206 return of(count, addToString(retry -> retry * ms, () -> String.format("Incremental retry based on %s ms", ms))); 207 } 208 209 /** 210 * Create a new RetryPolicy, that wait each time more time : first time the 211 * received duration, second time twice, etc. 212 * <p> 213 * For example, using this definition : 214 * 215 * <pre> 216 * RetryPolicy incremental = RetryPolicies.ofIncremental(3, Duration.ofMillis(50)); 217 * </pre> 218 * 219 * Then following of (re-)tries will be (maximum retries): 220 * <ol> 221 * <li>Do a first try</li> 222 * <li>Wait 50ms</li> 223 * <li>Do a second retry</li> 224 * <li>Wait 100ms</li> 225 * <li>Do a third retry</li> 226 * </ol> 227 * 228 * @param count 229 * the number of retry. 230 * @param duration 231 * the duration that will be combined with the retry number 232 * @return the RetryPolicy 233 */ 234 public static RetryPolicy ofIncremental(int count, Duration duration) { 235 return ofIncremental(count, requireNonNull(duration, "duration can't be null").toMillis()); 236 } 237 238 private static IntToLongFunction addToString(IntToLongFunction target, Supplier<String> toString) { 239 return new IntToLongFunction() { 240 241 @Override 242 public long applyAsLong(int value) { 243 return target.applyAsLong(value); 244 } 245 246 @Override 247 public String toString() { 248 return toString.get(); 249 } 250 }; 251 } 252 253 private static void sleepBetweenRetry(long ms) { 254 try { 255 Thread.sleep(ms); 256 } catch (InterruptedException e) { 257 // ignore 258 } 259 } 260}