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