001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.camel.util.backoff; 018 019import java.time.Duration; 020import java.util.concurrent.TimeUnit; 021 022import org.apache.camel.util.ObjectHelper; 023 024/** 025 * A back-off policy. 026 */ 027public final class BackOff { 028 public static final long NEVER = -1L; 029 public static final Duration MAX_DURATION = Duration.ofMillis(Long.MAX_VALUE); 030 public static final Duration DEFAULT_DELAY = Duration.ofSeconds(2); 031 public static final double DEFAULT_MULTIPLIER = 1f; 032 033 private Duration delay; 034 private Duration maxDelay; 035 private Duration maxElapsedTime; 036 private Long maxAttempts; 037 private Double multiplier; 038 private boolean removeOnComplete; 039 040 public BackOff() { 041 this(DEFAULT_DELAY, MAX_DURATION, MAX_DURATION, Long.MAX_VALUE, DEFAULT_MULTIPLIER, true); 042 } 043 044 public BackOff(Duration delay, Duration maxDelay, Duration maxElapsedTime, Long maxAttempts, Double multiplier, 045 boolean removeOnComplete) { 046 this.delay = ObjectHelper.supplyIfEmpty(delay, () -> DEFAULT_DELAY); 047 this.maxDelay = ObjectHelper.supplyIfEmpty(maxDelay, () -> MAX_DURATION); 048 this.maxElapsedTime = ObjectHelper.supplyIfEmpty(maxElapsedTime, () -> MAX_DURATION); 049 this.maxAttempts = ObjectHelper.supplyIfEmpty(maxAttempts, () -> Long.MAX_VALUE); 050 this.multiplier = ObjectHelper.supplyIfEmpty(multiplier, () -> DEFAULT_MULTIPLIER); 051 this.removeOnComplete = removeOnComplete; 052 } 053 054 // ************************************* 055 // Properties 056 // ************************************* 057 058 public Duration getDelay() { 059 return delay; 060 } 061 062 /** 063 * The delay to wait before retry the operation. 064 */ 065 public void setDelay(Duration delay) { 066 this.delay = delay; 067 } 068 069 public Duration getMaxDelay() { 070 return maxDelay; 071 } 072 073 /** 074 * The maximum back-off time after which the delay is not more increased. 075 */ 076 public void setMaxDelay(Duration maxDelay) { 077 this.maxDelay = maxDelay; 078 } 079 080 public Duration getMaxElapsedTime() { 081 return maxElapsedTime; 082 } 083 084 /** 085 * The maximum elapsed time after which the back-off should be considered exhausted and no more attempts should be 086 * made. 087 */ 088 public void setMaxElapsedTime(Duration maxElapsedTime) { 089 this.maxElapsedTime = maxElapsedTime; 090 } 091 092 public Long getMaxAttempts() { 093 return maxAttempts; 094 } 095 096 /** 097 * The maximum number of attempts after which the back-off should be considered exhausted and no more attempts 098 * should be made. 099 */ 100 public void setMaxAttempts(Long maxAttempts) { 101 this.maxAttempts = maxAttempts; 102 } 103 104 public Double getMultiplier() { 105 return multiplier; 106 } 107 108 /** 109 * The value to multiply the current interval by for each retry attempt. 110 */ 111 public void setMultiplier(Double multiplier) { 112 this.multiplier = multiplier; 113 } 114 115 public boolean isRemoveOnComplete() { 116 return removeOnComplete; 117 } 118 119 /** 120 * Should the task be removed from the timer when its complete successfully. 121 */ 122 public void setRemoveOnComplete(boolean removeOnComplete) { 123 this.removeOnComplete = removeOnComplete; 124 } 125 126 @Override 127 public String toString() { 128 StringBuilder sb = new StringBuilder(256); 129 sb.append("BackOff["); 130 sb.append("delay=").append(delay.toMillis()); 131 if (maxDelay != MAX_DURATION) { 132 sb.append(", maxDelay=").append(maxDelay.toMillis()); 133 } 134 if (maxElapsedTime != MAX_DURATION) { 135 sb.append(", maxElapsedTime=").append(maxElapsedTime.toMillis()); 136 } 137 if (maxAttempts != Long.MAX_VALUE) { 138 sb.append(", maxAttempts=").append(maxAttempts); 139 } 140 if (multiplier != DEFAULT_MULTIPLIER) { 141 sb.append(", multiplier=").append(multiplier); 142 } 143 sb.append(", remove=").append(removeOnComplete); 144 sb.append("]"); 145 return sb.toString(); 146 } 147 148 // ***************************************** 149 // Builder 150 // ***************************************** 151 152 public static Builder builder() { 153 return new Builder(); 154 } 155 156 public static Builder builder(BackOff template) { 157 return new Builder().read(template); 158 } 159 160 /** 161 * A builder for {@link BackOff} 162 */ 163 public static final class Builder { 164 private Duration delay = BackOff.DEFAULT_DELAY; 165 private Duration maxDelay = BackOff.MAX_DURATION; 166 private Duration maxElapsedTime = BackOff.MAX_DURATION; 167 private Long maxAttempts = Long.MAX_VALUE; 168 private Double multiplier = BackOff.DEFAULT_MULTIPLIER; 169 private boolean removeOnComplete = true; 170 171 /** 172 * Read values from the given {@link BackOff} 173 */ 174 public Builder read(BackOff template) { 175 delay = template.delay; 176 maxDelay = template.maxDelay; 177 maxElapsedTime = template.maxElapsedTime; 178 maxAttempts = template.maxAttempts; 179 multiplier = template.multiplier; 180 removeOnComplete = template.removeOnComplete; 181 return this; 182 } 183 184 public Builder delay(Duration delay) { 185 this.delay = delay; 186 return this; 187 } 188 189 public Builder delay(long delay, TimeUnit unit) { 190 return delay(Duration.ofMillis(unit.toMillis(delay))); 191 } 192 193 public Builder delay(long delay) { 194 return delay(Duration.ofMillis(delay)); 195 } 196 197 public Builder maxDelay(Duration maxDelay) { 198 this.maxDelay = maxDelay; 199 return this; 200 } 201 202 public Builder maxDelay(long maxDelay, TimeUnit unit) { 203 return maxDelay(Duration.ofMillis(unit.toMillis(maxDelay))); 204 } 205 206 public Builder maxDelay(long maxDelay) { 207 return maxDelay(Duration.ofMillis(maxDelay)); 208 } 209 210 public Builder maxElapsedTime(Duration maxElapsedTime) { 211 this.maxElapsedTime = maxElapsedTime; 212 return this; 213 } 214 215 public Builder maxElapsedTime(long maxElapsedTime, TimeUnit unit) { 216 return maxElapsedTime(Duration.ofMillis(unit.toMillis(maxElapsedTime))); 217 } 218 219 public Builder maxElapsedTime(long maxElapsedTime) { 220 return maxElapsedTime(Duration.ofMillis(maxElapsedTime)); 221 } 222 223 public Builder maxAttempts(Long attempts) { 224 this.maxAttempts = attempts; 225 return this; 226 } 227 228 public Builder multiplier(Double multiplier) { 229 this.multiplier = multiplier; 230 return this; 231 } 232 233 public Builder removeOnComplete(boolean removeOnComplete) { 234 this.removeOnComplete = removeOnComplete; 235 return this; 236 } 237 238 /** 239 * Build a new instance of {@link BackOff} 240 */ 241 public BackOff build() { 242 return new BackOff(delay, maxDelay, maxElapsedTime, maxAttempts, multiplier, removeOnComplete); 243 } 244 } 245}