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     */
017    package org.apache.camel.processor.aggregate;
018    
019    import 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     */
044    public 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    }