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