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