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        return "BackOff["
115               + "delay=" + delay.toMillis()
116               + ", maxDelay=" + (maxDelay != MAX_DURATION ? maxDelay.toMillis() : "")
117               + ", maxElapsedTime=" + (maxElapsedTime != MAX_DURATION ? maxElapsedTime.toMillis() : "")
118               + ", maxAttempts=" + maxAttempts
119               + ", multiplier=" + multiplier
120               + ']';
121    }
122
123    // *****************************************
124    // Builder
125    // *****************************************
126
127    public static Builder builder() {
128        return new Builder();
129    }
130
131    public static Builder builder(BackOff template) {
132        return new Builder().read(template);
133    }
134
135    /**
136     * A builder for {@link BackOff}
137     */
138    public static final class Builder {
139        private Duration delay = BackOff.DEFAULT_DELAY;
140        private Duration maxDelay = BackOff.MAX_DURATION;
141        private Duration maxElapsedTime = BackOff.MAX_DURATION;
142        private Long maxAttempts = Long.MAX_VALUE;
143        private Double multiplier = BackOff.DEFAULT_MULTIPLIER;
144
145        /**
146         * Read values from the given {@link BackOff}
147         */
148        public Builder read(BackOff template) {
149            delay = template.delay;
150            maxDelay = template.maxDelay;
151            maxElapsedTime = template.maxElapsedTime;
152            maxAttempts = template.maxAttempts;
153            multiplier = template.multiplier;
154
155            return this;
156        }
157
158        public Builder delay(Duration delay) {
159            this.delay = delay;
160            return this;
161        }
162
163        public Builder delay(long delay, TimeUnit unit) {
164            return delay(Duration.ofMillis(unit.toMillis(delay)));
165        }
166
167        public Builder delay(long delay) {
168            return delay(Duration.ofMillis(delay));
169        }
170
171        public Builder maxDelay(Duration maxDelay) {
172            this.maxDelay = maxDelay;
173            return this;
174        }
175
176        public Builder maxDelay(long maxDelay, TimeUnit unit) {
177            return maxDelay(Duration.ofMillis(unit.toMillis(maxDelay)));
178        }
179
180        public Builder maxDelay(long maxDelay) {
181            return maxDelay(Duration.ofMillis(maxDelay));
182        }
183
184        public Builder maxElapsedTime(Duration maxElapsedTime) {
185            this.maxElapsedTime = maxElapsedTime;
186            return this;
187        }
188
189        public Builder maxElapsedTime(long maxElapsedTime, TimeUnit unit) {
190            return maxElapsedTime(Duration.ofMillis(unit.toMillis(maxElapsedTime)));
191        }
192
193        public Builder maxElapsedTime(long maxElapsedTime) {
194            return maxElapsedTime(Duration.ofMillis(maxElapsedTime));
195        }
196
197        public Builder maxAttempts(Long attempts) {
198            this.maxAttempts = attempts;
199            return this;
200        }
201
202        public Builder multiplier(Double multiplier) {
203            this.multiplier = multiplier;
204            return this;
205        }
206
207        /**
208         * Build a new instance of {@link BackOff}
209         */
210        public BackOff build() {
211            return new BackOff(delay, maxDelay, maxElapsedTime, maxAttempts, multiplier);
212        }
213    }
214}