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
039    public BackOff() {
040        this(DEFAULT_DELAY, MAX_DURATION, MAX_DURATION, Long.MAX_VALUE, DEFAULT_MULTIPLIER);
041    }
042
043    public BackOff(Duration delay, Duration maxDelay, Duration maxElapsedTime, Long maxAttempts, Double multiplier) {
044        this.delay = ObjectHelper.supplyIfEmpty(delay, () -> DEFAULT_DELAY);
045        this.maxDelay = ObjectHelper.supplyIfEmpty(maxDelay, () -> MAX_DURATION);
046        this.maxElapsedTime = ObjectHelper.supplyIfEmpty(maxElapsedTime, () -> MAX_DURATION);
047        this.maxAttempts = ObjectHelper.supplyIfEmpty(maxAttempts, () -> Long.MAX_VALUE);
048        this.multiplier = ObjectHelper.supplyIfEmpty(multiplier, () -> DEFAULT_MULTIPLIER);
049    }
050
051    // *************************************
052    // Properties
053    // *************************************
054
055    /**
056     * @return the delay to wait before retry the operation.
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
086     * exhausted and no more attempts should be 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
098     * exhausted and no more attempts should be made.
099     *
100     * @param maxAttempts
101     */
102    public void setMaxAttempts(Long maxAttempts) {
103        this.maxAttempts = maxAttempts;
104    }
105
106    public Double getMultiplier() {
107        return multiplier;
108    }
109
110    /**
111     * The value to multiply the current interval by for each retry attempt.
112     */
113    public void setMultiplier(Double multiplier) {
114        this.multiplier = multiplier;
115    }
116
117    @Override
118    public String toString() {
119        return "BackOff{"
120            + "delay=" + delay
121            + ", maxDelay=" + maxDelay
122            + ", maxElapsedTime=" + maxElapsedTime
123            + ", maxAttempts=" + maxAttempts
124            + ", multiplier=" + multiplier
125            + '}';
126    }
127
128    // *****************************************
129    // Builder
130    // *****************************************
131
132    public static Builder builder() {
133        return new Builder();
134    }
135
136    public static Builder builder(BackOff template) {
137        return new Builder().read(template);
138    }
139
140    /**
141     * A builder for {@link BackOff}
142     */
143    public static final class Builder {
144        private Duration delay = BackOff.DEFAULT_DELAY;
145        private Duration maxDelay = BackOff.MAX_DURATION;
146        private Duration maxElapsedTime = BackOff.MAX_DURATION;
147        private Long maxAttempts = Long.MAX_VALUE;
148        private Double multiplier = BackOff.DEFAULT_MULTIPLIER;
149
150        /**
151         * Read values from the given {@link BackOff}
152         */
153        public Builder read(BackOff template) {
154            delay = template.delay;
155            maxDelay = template.maxDelay;
156            maxElapsedTime = template.maxElapsedTime;
157            maxAttempts = template.maxAttempts;
158            multiplier = template.multiplier;
159
160            return this;
161        }
162
163        public Builder delay(Duration delay) {
164            this.delay = delay;
165            return this;
166        }
167
168        public Builder delay(long delay, TimeUnit unit) {
169            return delay(Duration.ofMillis(unit.toMillis(delay)));
170        }
171
172        public Builder delay(long delay) {
173            return delay(Duration.ofMillis(delay));
174        }
175
176        public Builder maxDelay(Duration maxDelay) {
177            this.maxDelay = maxDelay;
178            return this;
179        }
180
181        public Builder maxDelay(long maxDelay, TimeUnit unit) {
182            return maxDelay(Duration.ofMillis(unit.toMillis(maxDelay)));
183        }
184
185        public Builder maxDelay(long maxDelay) {
186            return maxDelay(Duration.ofMillis(maxDelay));
187        }
188
189        public Builder maxElapsedTime(Duration maxElapsedTime) {
190            this.maxElapsedTime = maxElapsedTime;
191            return this;
192        }
193
194        public Builder maxElapsedTime(long maxElapsedTime, TimeUnit unit) {
195            return maxElapsedTime(Duration.ofMillis(unit.toMillis(maxElapsedTime)));
196        }
197
198        public Builder maxElapsedTime(long maxElapsedTime) {
199            return maxElapsedTime(Duration.ofMillis(maxElapsedTime));
200        }
201
202        public Builder maxAttempts(Long attempts) {
203            this.maxAttempts = attempts;
204            return this;
205        }
206
207        public Builder multiplier(Double multiplier) {
208            this.multiplier = multiplier;
209            return this;
210        }
211
212        /**
213         * Build a new instance of {@link BackOff}
214         */
215        public BackOff build() {
216            return new BackOff(delay, maxDelay, maxElapsedTime, maxAttempts, multiplier);
217        }
218    }
219}