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.processor.aggregate;
018
019import java.util.Random;
020
021/**
022 * Class to control how failed optimistic locks are tried. This policy supports random and exponential back-off delays.
023 * <p/>
024 * If {@code randomBackOff} is enabled and a value is supplied for {@code retryDelay} the value will be ignored.
025 * <p/>
026 * If {@code randomBackOff} is enabled and no value is set for {@code maximumRetryDelay}, a default value of 1000ms will
027 * be used, the random delay will be between 0 and 1000 milliseconds.
028 * <p/>
029 * If both {@code randomBackOff} and {@code exponentialBackOff} are enabled, {@code exponentialBackOff} will take precedence.
030 * <p/>
031 * If {@code exponentialBackOff} is enabled and a value is set for {@code maximumRetryDelay}, the retry delay will keep
032 * doubling in value until it reaches or exceeds {@code maximumRetryDelay}. After it has reached or exceeded {@code maximumRetryDelay}
033 * the value of {@code maximumRetryDelay} will be used as the retry delay.
034 * <p/>
035 * If both {@code exponentialBackOff} and {@code randomBackOff} are disabled, the value of {@code retryDelay} will be used
036 * as the retry delay and remain constant through all the retry attempts.
037 * <p/>
038 * If the value of {@code maximumRetries} is set above zero, retry attempts will stop at the value specified.
039 * <p/>
040 * The default behaviour of this policy is to retry forever and exponentially increase the back-off delay starting with 50ms.
041 *
042 * @version
043 */
044public class OptimisticLockRetryPolicy {
045    private static final long DEFAULT_MAXIMUM_RETRY_DELAY = 1000L;
046
047    private int maximumRetries;
048    private long retryDelay = 50L;
049    private long maximumRetryDelay;
050    private boolean exponentialBackOff = true;
051    private boolean randomBackOff;
052
053    public OptimisticLockRetryPolicy() {
054    }
055
056    public boolean shouldRetry(final int retryCounter) {
057        return maximumRetries <= 0 || retryCounter < maximumRetries;
058    }
059
060    public void doDelay(final int retryCounter) throws InterruptedException {
061        if (retryDelay > 0 || randomBackOff) {
062            long sleepFor;
063            sleepFor = exponentialBackOff ? (retryDelay << retryCounter)
064                    : (randomBackOff ? new Random().nextInt((int)(maximumRetryDelay > 0 ? maximumRetryDelay : DEFAULT_MAXIMUM_RETRY_DELAY)) : retryDelay);
065            if (maximumRetryDelay > 0 && sleepFor > maximumRetryDelay) {
066                sleepFor = maximumRetryDelay;
067            }
068            Thread.sleep(sleepFor);
069        }
070    }
071
072    public int getMaximumRetries() {
073        return maximumRetries;
074    }
075
076    public void setMaximumRetries(int maximumRetries) {
077        this.maximumRetries = maximumRetries;
078    }
079
080    public OptimisticLockRetryPolicy maximumRetries(int maximumRetries) {
081        setMaximumRetries(maximumRetries);
082        return this;
083    }
084
085    public long getRetryDelay() {
086        return retryDelay;
087    }
088
089    public void setRetryDelay(long retryDelay) {
090        this.retryDelay = retryDelay;
091    }
092
093    public OptimisticLockRetryPolicy retryDelay(long retryDelay) {
094        setRetryDelay(retryDelay);
095        return this;
096    }
097
098    public long getMaximumRetryDelay() {
099        return maximumRetryDelay;
100    }
101
102    public void setMaximumRetryDelay(long maximumRetryDelay) {
103        this.maximumRetryDelay = maximumRetryDelay;
104    }
105
106    public OptimisticLockRetryPolicy maximumRetryDelay(long maximumRetryDelay) {
107        setMaximumRetryDelay(maximumRetryDelay);
108        return this;
109    }
110
111    public boolean isExponentialBackOff() {
112        return exponentialBackOff;
113    }
114
115    public void setExponentialBackOff(boolean exponentialBackOff) {
116        this.exponentialBackOff = exponentialBackOff;
117    }
118
119    public OptimisticLockRetryPolicy exponentialBackOff() {
120        setExponentialBackOff(true);
121        return this;
122    }
123
124    public boolean isRandomBackOff() {
125        return randomBackOff;
126    }
127
128    public void setRandomBackOff(boolean randomBackOff) {
129        this.randomBackOff = randomBackOff;
130    }
131
132    public OptimisticLockRetryPolicy randomBackOff() {
133        setRandomBackOff(true);
134        return this;
135    }
136
137    @Override
138    public String toString() {
139        final StringBuilder sb = new StringBuilder("OptimisticLockRetryPolicy[");
140        sb.append("maximumRetries=").append(maximumRetries);
141        sb.append(", retryDelay=").append(retryDelay);
142        sb.append(", maximumRetryDelay=").append(maximumRetryDelay);
143        sb.append(", exponentialBackOff=").append(exponentialBackOff);
144        sb.append(", randomBackOff=").append(randomBackOff);
145        sb.append(']');
146        return sb.toString();
147    }
148}