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;
018    
019    import java.io.Serializable;
020    import java.util.Random;
021    
022    import org.apache.commons.logging.Log;
023    import org.apache.commons.logging.LogFactory;
024    
025    // Code taken from the ActiveMQ codebase
026    
027    /**
028     * The policy used to decide how many times to redeliver and the time between
029     * the redeliveries before being sent to a <a
030     * href="http://activemq.apache.org/camel/dead-letter-channel.html">Dead Letter
031     * Channel</a>
032     * <p>
033     * The default values is:
034     * <ul>
035     *   <li>maximumRedeliveries = 5</li>
036     *   <li>initialRedeliveryDelay = 1000L</li>
037     *   <li>maximumRedeliveryDelay = 60 * 1000L</li>
038     *   <li>backOffMultiplier = 2</li>
039     *   <li>useExponentialBackOff = false</li>
040     *   <li>collisionAvoidanceFactor = 0.15d</li>
041     *   <li>useCollisionAvoidance = false</li>
042     * </ul>
043     * <p/>
044     * Setting the maximumRedeliveries to a negative value such as -1 will then always redeliver (unlimited).
045     * Setting the maximumRedeliveries to 0 will disable redelivery.
046     *
047     * @version $Revision: 47354 $
048     */
049    public class RedeliveryPolicy implements Cloneable, Serializable {
050        protected static transient Random randomNumberGenerator;
051        private static final transient Log LOG = LogFactory.getLog(RedeliveryPolicy.class);
052    
053        protected int maximumRedeliveries = 5;
054        protected long initialRedeliveryDelay = 1000L;
055        protected long maximumRedeliveryDelay = 60 * 1000L;
056        protected double backOffMultiplier = 2;
057        protected boolean useExponentialBackOff;
058        // +/-15% for a 30% spread -cgs
059        protected double collisionAvoidanceFactor = 0.15d;
060        protected boolean useCollisionAvoidance;
061    
062        public RedeliveryPolicy() {
063        }
064    
065        @Override
066        public String toString() {
067            return "RedeliveryPolicy[maximumRedeliveries=" + maximumRedeliveries + "]";
068        }
069    
070        public RedeliveryPolicy copy() {
071            try {
072                return (RedeliveryPolicy)clone();
073            } catch (CloneNotSupportedException e) {
074                throw new RuntimeException("Could not clone: " + e, e);
075            }
076        }
077    
078        /**
079         * Returns true if the policy decides that the message exchange should be
080         * redelivered
081         */
082        public boolean shouldRedeliver(int redeliveryCounter) {
083            if (getMaximumRedeliveries() < 0) {
084                return true;
085            }
086            // redeliver until we hitted the max
087            return redeliveryCounter <= getMaximumRedeliveries();
088        }
089    
090    
091        /**
092         * Calculates the new redelivery delay based on the last one then sleeps for the necessary amount of time
093         */
094        public long sleep(long redeliveryDelay) {
095            redeliveryDelay = getRedeliveryDelay(redeliveryDelay);
096    
097            if (redeliveryDelay > 0) {
098                if (LOG.isDebugEnabled()) {
099                    LOG.debug("Sleeping for: " + redeliveryDelay + " millis until attempting redelivery");
100                }
101                try {
102                    Thread.sleep(redeliveryDelay);
103                } catch (InterruptedException e) {
104                    if (LOG.isDebugEnabled()) {
105                        LOG.debug("Thread interrupted: " + e, e);
106                    }
107                }
108            }
109            return redeliveryDelay;
110        }
111    
112    
113        public long getRedeliveryDelay(long previousDelay) {
114            long redeliveryDelay;
115    
116            if (previousDelay == 0) {
117                redeliveryDelay = initialRedeliveryDelay;
118            } else if (useExponentialBackOff && backOffMultiplier > 1) {
119                redeliveryDelay = Math.round(backOffMultiplier * previousDelay);
120            } else {
121                redeliveryDelay = previousDelay;
122            }
123    
124            if (useCollisionAvoidance) {
125    
126                /*
127                 * First random determines +/-, second random determines how far to
128                 * go in that direction. -cgs
129                 */
130                Random random = getRandomNumberGenerator();
131                double variance = (random.nextBoolean() ? collisionAvoidanceFactor : -collisionAvoidanceFactor)
132                                  * random.nextDouble();
133                redeliveryDelay += redeliveryDelay * variance;
134            }
135    
136            if (maximumRedeliveryDelay > 0 && redeliveryDelay > maximumRedeliveryDelay) {
137                redeliveryDelay = maximumRedeliveryDelay;
138            }
139    
140            return redeliveryDelay;
141        }
142    
143    
144        // Builder methods
145        // -------------------------------------------------------------------------
146    
147        /**
148         * Sets the maximum number of times a message exchange will be redelivered
149         */
150        public RedeliveryPolicy maximumRedeliveries(int maximumRedeliveries) {
151            setMaximumRedeliveries(maximumRedeliveries);
152            return this;
153        }
154    
155        /**
156         * Sets the initial redelivery delay in milliseconds on the first redelivery
157         */
158        public RedeliveryPolicy initialRedeliveryDelay(long initialRedeliveryDelay) {
159            setInitialRedeliveryDelay(initialRedeliveryDelay);
160            return this;
161        }
162    
163        /**
164         * Enables collision avoidence which adds some randomization to the backoff
165         * timings to reduce contention probability
166         */
167        public RedeliveryPolicy useCollisionAvoidance() {
168            setUseCollisionAvoidance(true);
169            return this;
170        }
171    
172        /**
173         * Enables exponential backof using the {@link #getBackOffMultiplier()} to
174         * increase the time between retries
175         */
176        public RedeliveryPolicy useExponentialBackOff() {
177            setUseExponentialBackOff(true);
178            return this;
179        }
180    
181        /**
182         * Enables exponential backoff and sets the multiplier used to increase the
183         * delay between redeliveries
184         */
185        public RedeliveryPolicy backOffMultiplier(double multiplier) {
186            useExponentialBackOff();
187            setBackOffMultiplier(multiplier);
188            return this;
189        }
190    
191        /**
192         * Enables collision avoidence and sets the percentage used
193         */
194        public RedeliveryPolicy collisionAvoidancePercent(double collisionAvoidancePercent) {
195            useCollisionAvoidance();
196            setCollisionAvoidancePercent(collisionAvoidancePercent);
197            return this;
198        }
199    
200        /**
201         * Sets the maximum redelivery delay if using exponential back off.
202         * Use -1 if you wish to have no maximum
203         */
204        public RedeliveryPolicy maximumRedeliveryDelay(long maximumRedeliveryDelay) {
205            setMaximumRedeliveryDelay(maximumRedeliveryDelay);
206            return this;
207        }
208    
209        // Properties
210        // -------------------------------------------------------------------------
211        public double getBackOffMultiplier() {
212            return backOffMultiplier;
213        }
214    
215        /**
216         * Sets the multiplier used to increase the delay between redeliveries if
217         * {@link #setUseExponentialBackOff(boolean)} is enabled
218         */
219        public void setBackOffMultiplier(double backOffMultiplier) {
220            this.backOffMultiplier = backOffMultiplier;
221        }
222    
223        public short getCollisionAvoidancePercent() {
224            return (short)Math.round(collisionAvoidanceFactor * 100);
225        }
226    
227        /**
228         * Sets the percentage used for collision avoidence if enabled via
229         * {@link #setUseCollisionAvoidance(boolean)}
230         */
231        public void setCollisionAvoidancePercent(double collisionAvoidancePercent) {
232            this.collisionAvoidanceFactor = collisionAvoidancePercent * 0.01d;
233        }
234    
235        public double getCollisionAvoidanceFactor() {
236            return collisionAvoidanceFactor;
237        }
238    
239        /**
240         * Sets the factor used for collision avoidence if enabled via
241         * {@link #setUseCollisionAvoidance(boolean)}
242         */
243        public void setCollisionAvoidanceFactor(double collisionAvoidanceFactor) {
244            this.collisionAvoidanceFactor = collisionAvoidanceFactor;
245        }
246    
247        public long getInitialRedeliveryDelay() {
248            return initialRedeliveryDelay;
249        }
250    
251        /**
252         * Sets the initial redelivery delay in milliseconds on the first redelivery
253         */
254        public void setInitialRedeliveryDelay(long initialRedeliveryDelay) {
255            this.initialRedeliveryDelay = initialRedeliveryDelay;
256        }
257    
258        public int getMaximumRedeliveries() {
259            return maximumRedeliveries;
260        }
261    
262        /**
263         * Sets the maximum number of times a message exchange will be redelivered.
264         * Setting a negative value will retry forever.
265         */
266        public void setMaximumRedeliveries(int maximumRedeliveries) {
267            this.maximumRedeliveries = maximumRedeliveries;
268        }
269    
270        public long getMaximumRedeliveryDelay() {
271            return maximumRedeliveryDelay;
272        }
273    
274        /**
275         * Sets the maximum redelivery delay if using exponential back off.
276         * Use -1 if you wish to have no maximum
277         */
278        public void setMaximumRedeliveryDelay(long maximumRedeliveryDelay) {
279            this.maximumRedeliveryDelay = maximumRedeliveryDelay;
280        }
281    
282        public boolean isUseCollisionAvoidance() {
283            return useCollisionAvoidance;
284        }
285    
286        /**
287         * Enables/disables collision avoidence which adds some randomization to the
288         * backoff timings to reduce contention probability
289         */
290        public void setUseCollisionAvoidance(boolean useCollisionAvoidance) {
291            this.useCollisionAvoidance = useCollisionAvoidance;
292        }
293    
294        public boolean isUseExponentialBackOff() {
295            return useExponentialBackOff;
296        }
297    
298        /**
299         * Enables/disables exponential backof using the
300         * {@link #getBackOffMultiplier()} to increase the time between retries
301         */
302        public void setUseExponentialBackOff(boolean useExponentialBackOff) {
303            this.useExponentialBackOff = useExponentialBackOff;
304        }
305    
306        protected static synchronized Random getRandomNumberGenerator() {
307            if (randomNumberGenerator == null) {
308                randomNumberGenerator = new Random();
309            }
310            return randomNumberGenerator;
311        }
312    }