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.time.temporal.TemporalUnit;
021import java.util.concurrent.TimeUnit;
022
023import org.apache.camel.util.ObjectHelper;
024
025/**
026 * A back-off policy.
027 */
028public final class BackOff {
029    public static final long NEVER = -1L;
030    public static final Duration MAX_DURATION = Duration.ofMillis(Long.MAX_VALUE);
031    public static final Duration DEFAULT_DELAY = Duration.ofSeconds(2);
032    public static final double DEFAULT_MULTIPLIER = 1f;
033
034    private Duration delay;
035    private Duration maxDelay;
036    private Duration maxElapsedTime;
037    private Long maxAttempts;
038    private Double multiplier;
039
040    public BackOff() {
041        this(DEFAULT_DELAY, MAX_DURATION, MAX_DURATION, Long.MAX_VALUE, DEFAULT_MULTIPLIER);
042    }
043
044    public BackOff(Duration delay, Duration maxDelay, Duration maxElapsedTime, Long maxAttempts, Double multiplier) {
045        this.delay = ObjectHelper.supplyIfEmpty(delay, () -> DEFAULT_DELAY);
046        this.maxDelay = ObjectHelper.supplyIfEmpty(maxDelay, () -> MAX_DURATION);
047        this.maxElapsedTime = ObjectHelper.supplyIfEmpty(maxElapsedTime, () -> MAX_DURATION);
048        this.maxAttempts = ObjectHelper.supplyIfEmpty(maxAttempts, () -> Long.MAX_VALUE);
049        this.multiplier = ObjectHelper.supplyIfEmpty(multiplier, () -> DEFAULT_MULTIPLIER);
050    }
051
052    // *************************************
053    // Properties
054    // *************************************
055
056    public Duration getDelay() {
057        return delay;
058    }
059
060    /**
061     * The delay to wait before retry the operation.
062     */
063    public void setDelay(Duration delay) {
064        this.delay = delay;
065    }
066
067    public Duration getMaxDelay() {
068        return maxDelay;
069    }
070
071    /**
072     * The maximum back-off time after which the delay is not more increased.
073     */
074    public void setMaxDelay(Duration maxDelay) {
075        this.maxDelay = maxDelay;
076    }
077
078    public Duration getMaxElapsedTime() {
079        return maxElapsedTime;
080    }
081
082    /**
083     * The maximum elapsed time after which the back-off should be considered
084     * exhausted and no more attempts should be made.
085     */
086    public void setMaxElapsedTime(Duration maxElapsedTime) {
087        this.maxElapsedTime = maxElapsedTime;
088    }
089
090    public Long getMaxAttempts() {
091        return maxAttempts;
092    }
093
094    /**
095     * The maximum number of attempts after which the back-off should be considered
096     * exhausted and no more attempts should be made.
097     */
098    public void setMaxAttempts(Long maxAttempts) {
099        this.maxAttempts = maxAttempts;
100    }
101
102    public Double getMultiplier() {
103        return multiplier;
104    }
105
106    /**
107     * The value to multiply the current interval by for each retry attempt.
108     */
109    public void setMultiplier(Double multiplier) {
110        this.multiplier = multiplier;
111    }
112
113    @Override
114    public String toString() {
115        return "BackOff["
116            + "delay=" + delay
117            + ", maxDelay=" + (maxDelay != MAX_DURATION ? maxDelay : "")
118            + ", maxElapsedTime=" + (maxElapsedTime != MAX_DURATION ? maxElapsedTime : "")
119            + ", maxAttempts=" + maxAttempts
120            + ", multiplier=" + multiplier
121            + ']';
122    }
123
124    // *****************************************
125    // Builder
126    // *****************************************
127
128    public static Builder builder() {
129        return new Builder();
130    }
131
132    public static Builder builder(BackOff template) {
133        return new Builder().read(template);
134    }
135
136    /**
137     * A builder for {@link BackOff}
138     */
139    public static final class Builder {
140        private Duration delay = BackOff.DEFAULT_DELAY;
141        private Duration maxDelay = BackOff.MAX_DURATION;
142        private Duration maxElapsedTime = BackOff.MAX_DURATION;
143        private Long maxAttempts = Long.MAX_VALUE;
144        private Double multiplier = BackOff.DEFAULT_MULTIPLIER;
145
146        /**
147         * Read values from the given {@link BackOff}
148         */
149        public Builder read(BackOff template) {
150            delay = template.delay;
151            maxDelay = template.maxDelay;
152            maxElapsedTime = template.maxElapsedTime;
153            maxAttempts = template.maxAttempts;
154            multiplier = template.multiplier;
155
156            return this;
157        }
158
159        public Builder delay(Duration delay) {
160            this.delay = delay;
161            return this;
162        }
163
164        public Builder delay(long delay, TimeUnit unit) {
165            return delay(Duration.ofMillis(unit.toMillis(delay)));
166        }
167
168        public Builder delay(long delay) {
169            return delay(Duration.ofMillis(delay));
170        }
171
172        public Builder maxDelay(Duration maxDelay) {
173            this.maxDelay = maxDelay;
174            return this;
175        }
176
177        public Builder maxDelay(long maxDelay, TimeUnit unit) {
178            return maxDelay(Duration.ofMillis(unit.toMillis(maxDelay)));
179        }
180
181        public Builder maxDelay(long maxDelay) {
182            return maxDelay(Duration.ofMillis(maxDelay));
183        }
184
185        public Builder maxElapsedTime(Duration maxElapsedTime) {
186            this.maxElapsedTime = maxElapsedTime;
187            return this;
188        }
189
190        public Builder maxElapsedTime(long maxElapsedTime, TimeUnit unit) {
191            return maxElapsedTime(Duration.ofMillis(unit.toMillis(maxElapsedTime)));
192        }
193
194        public Builder maxElapsedTime(long maxElapsedTime) {
195            return maxElapsedTime(Duration.ofMillis(maxElapsedTime));
196        }
197
198        public Builder maxAttempts(Long attempts) {
199            this.maxAttempts = attempts;
200            return this;
201        }
202
203        public Builder multiplier(Double multiplier) {
204            this.multiplier = multiplier;
205            return this;
206        }
207
208        /**
209         * Build a new instance of {@link BackOff}
210         */
211        public BackOff build() {
212            return new BackOff(delay, maxDelay, maxElapsedTime, maxAttempts, multiplier);
213        }
214    }
215}