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}