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.Exchange;
023    import org.apache.camel.LoggingLevel;
024    import org.apache.camel.Predicate;
025    import org.apache.camel.util.ObjectHelper;
026    import org.apache.commons.logging.Log;
027    import org.apache.commons.logging.LogFactory;
028    
029    // Code taken from the ActiveMQ codebase
030    
031    /**
032     * The policy used to decide how many times to redeliver and the time between
033     * the redeliveries before being sent to a <a
034     * href="http://camel.apache.org/dead-letter-channel.html">Dead Letter
035     * Channel</a>
036     * <p>
037     * The default values are:
038     * <ul>
039     *   <li>maximumRedeliveries = 0</li>
040     *   <li>redeliverDelay = 1000L (the initial delay)</li>
041     *   <li>maximumRedeliveryDelay = 60 * 1000L</li>
042     *   <li>backOffMultiplier = 2</li>
043     *   <li>useExponentialBackOff = false</li>
044     *   <li>collisionAvoidanceFactor = 0.15d</li>
045     *   <li>useCollisionAvoidance = false</li>
046     *   <li>retriesExhaustedLogLevel = LoggingLevel.ERROR</li>
047     *   <li>retryAttemptedLogLevel = LoggingLevel.DEBUG</li>
048     *   <li>logRetryStackTrace = false</li>
049     *   <li>logStackTrace = true</li>
050     * </ul>
051     * <p/>
052     * Setting the maximumRedeliveries to a negative value such as -1 will then always redeliver (unlimited).
053     * Setting the maximumRedeliveries to 0 will disable redelivery.
054     * <p/>
055     * This policy can be configured either by one of the following two settings:
056     * <ul>
057     *   <li>using conventional options, using all the options defined above</li>
058     *   <li>using delay pattern to declare intervals for delays</li>
059     * </ul>
060     * <p/>
061     * <b>Note:</b> If using delay patterns then the following options is not used (delay, backOffMultiplier, useExponentialBackOff, useCollisionAvoidance)
062     * <p/>
063     * <b>Using delay pattern</b>:
064     * <br/>The delay pattern syntax is: <tt>limit:delay;limit 2:delay 2;limit 3:delay 3;...;limit N:delay N</tt>.
065     * <p/>
066     * How it works is best illustrate with an example with this pattern: <tt>delayPattern=5:1000;10:5000:20:20000</tt>
067     * <br/>The delays will be for attempt in range 0..4 = 0 millis, 5..9 = 1000 millis, 10..19 = 5000 millis, >= 20 = 20000 millis.
068     * <p/>
069     * If you want to set a starting delay, then use 0 as the first limit, eg: <tt>0:1000;5:5000</tt> will use 1 sec delay
070     * until attempt number 5 where it will use 5 seconds going forward.
071     *
072     * @version $Revision: 905291 $
073     */
074    public class RedeliveryPolicy implements Cloneable, Serializable {
075        protected static transient Random randomNumberGenerator;
076        private static final long serialVersionUID = -338222777701473252L;
077        private static final transient Log LOG = LogFactory.getLog(RedeliveryPolicy.class);
078    
079        protected long redeliverDelay = 1000L;
080        protected int maximumRedeliveries;
081        protected long maximumRedeliveryDelay = 60 * 1000L;
082        protected double backOffMultiplier = 2;
083        protected boolean useExponentialBackOff;
084        // +/-15% for a 30% spread -cgs
085        protected double collisionAvoidanceFactor = 0.15d;
086        protected boolean useCollisionAvoidance;
087        protected LoggingLevel retriesExhaustedLogLevel = LoggingLevel.ERROR;
088        protected LoggingLevel retryAttemptedLogLevel = LoggingLevel.DEBUG;
089        protected boolean logStackTrace = true;
090        protected boolean logRetryStackTrace;
091        protected String delayPattern;
092    
093        public RedeliveryPolicy() {
094        }
095    
096        @Override
097        public String toString() {
098            return "RedeliveryPolicy[maximumRedeliveries=" + maximumRedeliveries
099                + ", redeliverDelay=" + redeliverDelay
100                + ", maximumRedeliveryDelay=" + maximumRedeliveryDelay
101                + ", retriesExhaustedLogLevel=" + retriesExhaustedLogLevel
102                + ", retryAttemptedLogLevel=" + retryAttemptedLogLevel
103                + ", logStackTrace=" + logStackTrace
104                + ", logRetryStackTrace=" + logRetryStackTrace
105                + ", useExponentialBackOff="  + useExponentialBackOff
106                + ", backOffMultiplier=" + backOffMultiplier
107                + ", useCollisionAvoidance=" + useCollisionAvoidance
108                + ", collisionAvoidanceFactor=" + collisionAvoidanceFactor
109                + ", delayPattern=" + delayPattern + "]";
110        }
111    
112        public RedeliveryPolicy copy() {
113            try {
114                return (RedeliveryPolicy)clone();
115            } catch (CloneNotSupportedException e) {
116                throw new RuntimeException("Could not clone: " + e, e);
117            }
118        }
119    
120        /**
121         * Returns true if the policy decides that the message exchange should be
122         * redelivered.
123         *
124         * @param exchange  the current exchange
125         * @param redeliveryCounter  the current retry counter
126         * @param retryUntil  an optional predicate to determine if we should redeliver or not
127         * @return true to redeliver, false to stop
128         */
129        public boolean shouldRedeliver(Exchange exchange, int redeliveryCounter, Predicate retryUntil) {
130            // predicate is always used if provided
131            if (retryUntil != null) {
132                return retryUntil.matches(exchange);
133            }
134    
135            if (getMaximumRedeliveries() < 0) {
136                // retry forever if negative value
137                return true;
138            }
139            // redeliver until we hit the max
140            return redeliveryCounter <= getMaximumRedeliveries();
141        }
142    
143    
144        /**
145         * Calculates the new redelivery delay based on the last one then sleeps for the necessary amount of time
146         *
147         * @param redeliveryDelay  previous redelivery delay
148         * @param redeliveryCounter  number of previous redelivery attempts
149         * @return the calculate delay
150         * @throws InterruptedException is thrown if the sleep is interrupted likely because of shutdown
151         */
152        public long sleep(long redeliveryDelay, int redeliveryCounter) throws InterruptedException {
153            redeliveryDelay = calculateRedeliveryDelay(redeliveryDelay, redeliveryCounter);
154    
155            if (redeliveryDelay > 0) {
156                if (LOG.isDebugEnabled()) {
157                    LOG.debug("Sleeping for: " + redeliveryDelay + " millis until attempting redelivery");
158                }
159                Thread.sleep(redeliveryDelay);
160            }
161            return redeliveryDelay;
162        }
163    
164        protected long calculateRedeliveryDelay(long previousDelay, int redeliveryCounter) {
165            if (ObjectHelper.isNotEmpty(delayPattern)) {
166                // calculate delay using the pattern
167                return calculateRedeliverDelayUsingPattern(delayPattern, redeliveryCounter);
168            }
169    
170            // calculate the delay using the conventional parameters
171            long redeliveryDelay;
172            if (previousDelay == 0) {
173                redeliveryDelay = redeliverDelay;
174            } else if (useExponentialBackOff && backOffMultiplier > 1) {
175                redeliveryDelay = Math.round(backOffMultiplier * previousDelay);
176            } else {
177                redeliveryDelay = previousDelay;
178            }
179    
180            if (useCollisionAvoidance) {
181    
182                /*
183                 * First random determines +/-, second random determines how far to
184                 * go in that direction. -cgs
185                 */
186                Random random = getRandomNumberGenerator();
187                double variance = (random.nextBoolean() ? collisionAvoidanceFactor : -collisionAvoidanceFactor)
188                                  * random.nextDouble();
189                redeliveryDelay += redeliveryDelay * variance;
190            }
191    
192            if (maximumRedeliveryDelay > 0 && redeliveryDelay > maximumRedeliveryDelay) {
193                redeliveryDelay = maximumRedeliveryDelay;
194            }
195    
196            return redeliveryDelay;
197        }
198    
199        /**
200         * Calculates the delay using the delay pattern
201         */
202        protected static long calculateRedeliverDelayUsingPattern(String delayPattern, int redeliveryCounter) {
203            String[] groups = delayPattern.split(";");
204            // find the group where the redelivery counter matches
205            long answer = 0;
206            for (String group : groups) {
207                long delay = Long.valueOf(ObjectHelper.after(group, ":"));
208                int count = Integer.valueOf(ObjectHelper.before(group, ":"));
209                if (count > redeliveryCounter) {
210                    break;
211                } else {
212                    answer = delay;
213                }
214            }
215    
216            return answer;
217        }
218    
219    
220        // Builder methods
221        // -------------------------------------------------------------------------
222    
223        /**
224         * Sets the delay in milliseconds
225         */
226        public RedeliveryPolicy redeliverDelay(long delay) {
227            setRedeliverDelay(delay);
228            return this;
229        }
230    
231        /**
232         * Sets the maximum number of times a message exchange will be redelivered
233         */
234        public RedeliveryPolicy maximumRedeliveries(int maximumRedeliveries) {
235            setMaximumRedeliveries(maximumRedeliveries);
236            return this;
237        }
238    
239        /**
240         * Enables collision avoidance which adds some randomization to the backoff
241         * timings to reduce contention probability
242         */
243        public RedeliveryPolicy useCollisionAvoidance() {
244            setUseCollisionAvoidance(true);
245            return this;
246        }
247    
248        /**
249         * Enables exponential backoff using the {@link #getBackOffMultiplier()} to
250         * increase the time between retries
251         */
252        public RedeliveryPolicy useExponentialBackOff() {
253            setUseExponentialBackOff(true);
254            return this;
255        }
256    
257        /**
258         * Enables exponential backoff and sets the multiplier used to increase the
259         * delay between redeliveries
260         */
261        public RedeliveryPolicy backOffMultiplier(double multiplier) {
262            useExponentialBackOff();
263            setBackOffMultiplier(multiplier);
264            return this;
265        }
266    
267        /**
268         * Enables collision avoidance and sets the percentage used
269         */
270        public RedeliveryPolicy collisionAvoidancePercent(double collisionAvoidancePercent) {
271            useCollisionAvoidance();
272            setCollisionAvoidancePercent(collisionAvoidancePercent);
273            return this;
274        }
275    
276        /**
277         * Sets the maximum redelivery delay if using exponential back off.
278         * Use -1 if you wish to have no maximum
279         */
280        public RedeliveryPolicy maximumRedeliveryDelay(long maximumRedeliveryDelay) {
281            setMaximumRedeliveryDelay(maximumRedeliveryDelay);
282            return this;
283        }
284    
285        /**
286         * Sets the logging level to use for log messages when retries have been exhausted.
287         */
288        public RedeliveryPolicy retriesExhaustedLogLevel(LoggingLevel retriesExhaustedLogLevel) {
289            setRetriesExhaustedLogLevel(retriesExhaustedLogLevel);
290            return this;
291        }    
292    
293        /**
294         * Sets the logging level to use for log messages when retries are attempted.
295         */    
296        public RedeliveryPolicy retryAttemptedLogLevel(LoggingLevel retryAttemptedLogLevel) {
297            setRetryAttemptedLogLevel(retryAttemptedLogLevel);
298            return this;
299        }    
300        
301        /**
302         * Sets whether to log stacktraces for failed messages.
303         */
304        public RedeliveryPolicy logStackTrace(boolean logStackTrace) {
305            setLogStackTrace(logStackTrace);
306            return this;
307        }
308    
309        /**
310         * Sets whether to log stacktrace for failed redelivery attempts
311         */
312        public RedeliveryPolicy logRetryStackTrace(boolean logRetryStackTrace) {
313            setLogRetryStackTrace(logRetryStackTrace);
314            return this;
315        }
316    
317        /**
318         * Sets the delay pattern with delay intervals.
319         */
320        public RedeliveryPolicy delayPattern(String delayPattern) {
321            setDelayPattern(delayPattern);
322            return this;
323        }
324    
325        /**
326         * Disables redelivery by setting maximum redeliveries to 0.
327         */
328        public RedeliveryPolicy disableRedelivery() {
329            setMaximumRedeliveries(0);
330            return this;
331        }
332    
333        // Properties
334        // -------------------------------------------------------------------------
335    
336        public long getRedeliverDelay() {
337            return redeliverDelay;
338        }
339    
340        /**
341         * Sets the delay in milliseconds
342         */
343        public void setRedeliverDelay(long redeliverDelay) {
344            this.redeliverDelay = redeliverDelay;
345            // if max enabled then also set max to this value in case max was too low
346            if (maximumRedeliveryDelay > 0 && redeliverDelay > maximumRedeliveryDelay) {
347                this.maximumRedeliveryDelay = redeliverDelay;
348            }
349        }
350    
351        public double getBackOffMultiplier() {
352            return backOffMultiplier;
353        }
354    
355        /**
356         * Sets the multiplier used to increase the delay between redeliveries if
357         * {@link #setUseExponentialBackOff(boolean)} is enabled
358         */
359        public void setBackOffMultiplier(double backOffMultiplier) {
360            this.backOffMultiplier = backOffMultiplier;
361        }
362    
363        public long getCollisionAvoidancePercent() {
364            return Math.round(collisionAvoidanceFactor * 100);
365        }
366    
367        /**
368         * Sets the percentage used for collision avoidance if enabled via
369         * {@link #setUseCollisionAvoidance(boolean)}
370         */
371        public void setCollisionAvoidancePercent(double collisionAvoidancePercent) {
372            this.collisionAvoidanceFactor = collisionAvoidancePercent * 0.01d;
373        }
374    
375        public double getCollisionAvoidanceFactor() {
376            return collisionAvoidanceFactor;
377        }
378    
379        /**
380         * Sets the factor used for collision avoidance if enabled via
381         * {@link #setUseCollisionAvoidance(boolean)}
382         */
383        public void setCollisionAvoidanceFactor(double collisionAvoidanceFactor) {
384            this.collisionAvoidanceFactor = collisionAvoidanceFactor;
385        }
386    
387        public int getMaximumRedeliveries() {
388            return maximumRedeliveries;
389        }
390    
391        /**
392         * Sets the maximum number of times a message exchange will be redelivered.
393         * Setting a negative value will retry forever.
394         */
395        public void setMaximumRedeliveries(int maximumRedeliveries) {
396            this.maximumRedeliveries = maximumRedeliveries;
397        }
398    
399        public long getMaximumRedeliveryDelay() {
400            return maximumRedeliveryDelay;
401        }
402    
403        /**
404         * Sets the maximum redelivery delay.
405         * Use -1 if you wish to have no maximum
406         */
407        public void setMaximumRedeliveryDelay(long maximumRedeliveryDelay) {
408            this.maximumRedeliveryDelay = maximumRedeliveryDelay;
409        }
410    
411        public boolean isUseCollisionAvoidance() {
412            return useCollisionAvoidance;
413        }
414    
415        /**
416         * Enables/disables collision avoidance which adds some randomization to the
417         * backoff timings to reduce contention probability
418         */
419        public void setUseCollisionAvoidance(boolean useCollisionAvoidance) {
420            this.useCollisionAvoidance = useCollisionAvoidance;
421        }
422    
423        public boolean isUseExponentialBackOff() {
424            return useExponentialBackOff;
425        }
426    
427        /**
428         * Enables/disables exponential backoff using the
429         * {@link #getBackOffMultiplier()} to increase the time between retries
430         */
431        public void setUseExponentialBackOff(boolean useExponentialBackOff) {
432            this.useExponentialBackOff = useExponentialBackOff;
433        }
434    
435        protected static synchronized Random getRandomNumberGenerator() {
436            if (randomNumberGenerator == null) {
437                randomNumberGenerator = new Random();
438            }
439            return randomNumberGenerator;
440        }
441    
442        /**
443         * Sets the logging level to use for log messages when retries have been exhausted.
444         */    
445        public void setRetriesExhaustedLogLevel(LoggingLevel retriesExhaustedLogLevel) {
446            this.retriesExhaustedLogLevel = retriesExhaustedLogLevel;        
447        }
448        
449        public LoggingLevel getRetriesExhaustedLogLevel() {
450            return retriesExhaustedLogLevel;
451        }
452    
453        /**
454         * Sets the logging level to use for log messages when retries are attempted.
455         */    
456        public void setRetryAttemptedLogLevel(LoggingLevel retryAttemptedLogLevel) {
457            this.retryAttemptedLogLevel = retryAttemptedLogLevel;
458        }
459    
460        public LoggingLevel getRetryAttemptedLogLevel() {
461            return retryAttemptedLogLevel;
462        }
463    
464        public String getDelayPattern() {
465            return delayPattern;
466        }
467    
468        /**
469         * Sets an optional delay pattern to use instead of fixed delay.
470         */
471        public void setDelayPattern(String delayPattern) {
472            this.delayPattern = delayPattern;
473        }
474    
475        public boolean isLogStackTrace() {
476            return logStackTrace;
477        }
478    
479        /**
480         * Sets whether stack traces should be logged or not
481         */
482        public void setLogStackTrace(boolean logStackTrace) {
483            this.logStackTrace = logStackTrace;
484        }
485    
486        public boolean isLogRetryStackTrace() {
487            return logRetryStackTrace;
488        }
489    
490        /**
491         * Sets whether stack traces should be logged or not
492         */
493        public void setLogRetryStackTrace(boolean logRetryStackTrace) {
494            this.logRetryStackTrace = logRetryStackTrace;
495        }
496    
497    }