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.slf4j.Logger;
027 import org.slf4j.LoggerFactory;
028
029 /**
030 * The policy used to decide how many times to redeliver and the time between
031 * the redeliveries before being sent to a <a
032 * href="http://camel.apache.org/dead-letter-channel.html">Dead Letter
033 * Channel</a>
034 * <p>
035 * The default values are:
036 * <ul>
037 * <li>maximumRedeliveries = 0</li>
038 * <li>redeliveryDelay = 1000L (the initial delay)</li>
039 * <li>maximumRedeliveryDelay = 60 * 1000L</li>
040 * <li>asyncDelayedRedelivery = false</li>
041 * <li>backOffMultiplier = 2</li>
042 * <li>useExponentialBackOff = false</li>
043 * <li>collisionAvoidanceFactor = 0.15d</li>
044 * <li>useCollisionAvoidance = false</li>
045 * <li>retriesExhaustedLogLevel = LoggingLevel.ERROR</li>
046 * <li>retryAttemptedLogLevel = LoggingLevel.DEBUG</li>
047 * <li>logRetryAttempted = true</li>
048 * <li>logRetryStackTrace = false</li>
049 * <li>logStackTrace = true</li>
050 * <li>logHandled = false</li>
051 * <li>logExhausted = true</li>
052 * </ul>
053 * <p/>
054 * Setting the maximumRedeliveries to a negative value such as -1 will then always redeliver (unlimited).
055 * Setting the maximumRedeliveries to 0 will disable redelivery.
056 * <p/>
057 * This policy can be configured either by one of the following two settings:
058 * <ul>
059 * <li>using conventional options, using all the options defined above</li>
060 * <li>using delay pattern to declare intervals for delays</li>
061 * </ul>
062 * <p/>
063 * <b>Note:</b> If using delay patterns then the following options is not used (delay, backOffMultiplier, useExponentialBackOff, useCollisionAvoidance)
064 * <p/>
065 * <b>Using delay pattern</b>:
066 * <br/>The delay pattern syntax is: <tt>limit:delay;limit 2:delay 2;limit 3:delay 3;...;limit N:delay N</tt>.
067 * <p/>
068 * How it works is best illustrate with an example with this pattern: <tt>delayPattern=5:1000;10:5000:20:20000</tt>
069 * <br/>The delays will be for attempt in range 0..4 = 0 millis, 5..9 = 1000 millis, 10..19 = 5000 millis, >= 20 = 20000 millis.
070 * <p/>
071 * 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
072 * until attempt number 5 where it will use 5 seconds going forward.
073 *
074 * @version
075 */
076 public class RedeliveryPolicy implements Cloneable, Serializable {
077 protected static transient Random randomNumberGenerator;
078 private static final long serialVersionUID = -338222777701473252L;
079 private static final transient Logger LOG = LoggerFactory.getLogger(RedeliveryPolicy.class);
080
081 protected long redeliveryDelay = 1000L;
082 protected int maximumRedeliveries;
083 protected long maximumRedeliveryDelay = 60 * 1000L;
084 protected double backOffMultiplier = 2;
085 protected boolean useExponentialBackOff;
086 // +/-15% for a 30% spread -cgs
087 protected double collisionAvoidanceFactor = 0.15d;
088 protected boolean useCollisionAvoidance;
089 protected LoggingLevel retriesExhaustedLogLevel = LoggingLevel.ERROR;
090 protected LoggingLevel retryAttemptedLogLevel = LoggingLevel.DEBUG;
091 protected boolean logStackTrace = true;
092 protected boolean logRetryStackTrace;
093 protected boolean logHandled;
094 protected boolean logContinued;
095 protected boolean logExhausted = true;
096 protected boolean logRetryAttempted = true;
097 protected String delayPattern;
098 protected boolean asyncDelayedRedelivery;
099
100 public RedeliveryPolicy() {
101 }
102
103 @Override
104 public String toString() {
105 return "RedeliveryPolicy[maximumRedeliveries=" + maximumRedeliveries
106 + ", redeliveryDelay=" + redeliveryDelay
107 + ", maximumRedeliveryDelay=" + maximumRedeliveryDelay
108 + ", asyncDelayedRedelivery=" + asyncDelayedRedelivery
109 + ", retriesExhaustedLogLevel=" + retriesExhaustedLogLevel
110 + ", retryAttemptedLogLevel=" + retryAttemptedLogLevel
111 + ", logRetryAttempted=" + logRetryAttempted
112 + ", logStackTrace=" + logStackTrace
113 + ", logRetryStackTrace=" + logRetryStackTrace
114 + ", logHandled=" + logHandled
115 + ", logContinued=" + logContinued
116 + ", logExhausted=" + logExhausted
117 + ", useExponentialBackOff=" + useExponentialBackOff
118 + ", backOffMultiplier=" + backOffMultiplier
119 + ", useCollisionAvoidance=" + useCollisionAvoidance
120 + ", collisionAvoidanceFactor=" + collisionAvoidanceFactor
121 + ", delayPattern=" + delayPattern + "]";
122 }
123
124 public RedeliveryPolicy copy() {
125 try {
126 return (RedeliveryPolicy)clone();
127 } catch (CloneNotSupportedException e) {
128 throw new RuntimeException("Could not clone: " + e, e);
129 }
130 }
131
132 /**
133 * Returns true if the policy decides that the message exchange should be
134 * redelivered.
135 *
136 * @param exchange the current exchange
137 * @param redeliveryCounter the current retry counter
138 * @param retryWhile an optional predicate to determine if we should redeliver or not
139 * @return true to redeliver, false to stop
140 */
141 public boolean shouldRedeliver(Exchange exchange, int redeliveryCounter, Predicate retryWhile) {
142 // predicate is always used if provided
143 if (retryWhile != null) {
144 return retryWhile.matches(exchange);
145 }
146
147 if (getMaximumRedeliveries() < 0) {
148 // retry forever if negative value
149 return true;
150 }
151 // redeliver until we hit the max
152 return redeliveryCounter <= getMaximumRedeliveries();
153 }
154
155
156 /**
157 * Calculates the new redelivery delay based on the last one and then <b>sleeps</b> for the necessary amount of time.
158 * <p/>
159 * This implementation will block while sleeping.
160 *
161 * @param redeliveryDelay previous redelivery delay
162 * @param redeliveryCounter number of previous redelivery attempts
163 * @return the calculate delay
164 * @throws InterruptedException is thrown if the sleep is interrupted likely because of shutdown
165 */
166 public long sleep(long redeliveryDelay, int redeliveryCounter) throws InterruptedException {
167 redeliveryDelay = calculateRedeliveryDelay(redeliveryDelay, redeliveryCounter);
168
169 if (redeliveryDelay > 0) {
170 sleep(redeliveryDelay);
171 }
172 return redeliveryDelay;
173 }
174
175 /**
176 * Sleeps for the given delay
177 *
178 * @param redeliveryDelay the delay
179 * @throws InterruptedException is thrown if the sleep is interrupted likely because of shutdown
180 */
181 public void sleep(long redeliveryDelay) throws InterruptedException {
182 LOG.debug("Sleeping for: {} millis until attempting redelivery", redeliveryDelay);
183 Thread.sleep(redeliveryDelay);
184 }
185
186 /**
187 * Calculates the new redelivery delay based on the last one
188 *
189 * @param previousDelay previous redelivery delay
190 * @param redeliveryCounter number of previous redelivery attempts
191 * @return the calculate delay
192 */
193 public long calculateRedeliveryDelay(long previousDelay, int redeliveryCounter) {
194 if (ObjectHelper.isNotEmpty(delayPattern)) {
195 // calculate delay using the pattern
196 return calculateRedeliverDelayUsingPattern(delayPattern, redeliveryCounter);
197 }
198
199 // calculate the delay using the conventional parameters
200 long redeliveryDelayResult;
201 if (previousDelay == 0) {
202 redeliveryDelayResult = redeliveryDelay;
203 } else if (useExponentialBackOff && backOffMultiplier > 1) {
204 redeliveryDelayResult = Math.round(backOffMultiplier * previousDelay);
205 } else {
206 redeliveryDelayResult = previousDelay;
207 }
208
209 if (useCollisionAvoidance) {
210
211 /*
212 * First random determines +/-, second random determines how far to
213 * go in that direction. -cgs
214 */
215 Random random = getRandomNumberGenerator();
216 double variance = (random.nextBoolean() ? collisionAvoidanceFactor : -collisionAvoidanceFactor)
217 * random.nextDouble();
218 redeliveryDelayResult += redeliveryDelayResult * variance;
219 }
220
221 // ensure the calculated result is not bigger than the max delay (if configured)
222 if (maximumRedeliveryDelay > 0 && redeliveryDelayResult > maximumRedeliveryDelay) {
223 redeliveryDelayResult = maximumRedeliveryDelay;
224 }
225
226 return redeliveryDelayResult;
227 }
228
229 /**
230 * Calculates the delay using the delay pattern
231 */
232 protected static long calculateRedeliverDelayUsingPattern(String delayPattern, int redeliveryCounter) {
233 String[] groups = delayPattern.split(";");
234 // find the group where the redelivery counter matches
235 long answer = 0;
236 for (String group : groups) {
237 long delay = Long.valueOf(ObjectHelper.after(group, ":"));
238 int count = Integer.valueOf(ObjectHelper.before(group, ":"));
239 if (count > redeliveryCounter) {
240 break;
241 } else {
242 answer = delay;
243 }
244 }
245
246 return answer;
247 }
248
249 // Builder methods
250 // -------------------------------------------------------------------------
251
252 /**
253 * Sets the initial redelivery delay in milliseconds
254 *
255 * @deprecated will be removed in the near future. Instead use {@link #redeliveryDelay(long)} instead
256 */
257 @Deprecated
258 public RedeliveryPolicy redeliverDelay(long delay) {
259 return redeliveryDelay(delay);
260 }
261
262 /**
263 * Sets the initial redelivery delay in milliseconds
264 */
265 public RedeliveryPolicy redeliveryDelay(long delay) {
266 setRedeliveryDelay(delay);
267 return this;
268 }
269
270 /**
271 * Sets the maximum number of times a message exchange will be redelivered
272 */
273 public RedeliveryPolicy maximumRedeliveries(int maximumRedeliveries) {
274 setMaximumRedeliveries(maximumRedeliveries);
275 return this;
276 }
277
278 /**
279 * Enables collision avoidance which adds some randomization to the backoff
280 * timings to reduce contention probability
281 */
282 public RedeliveryPolicy useCollisionAvoidance() {
283 setUseCollisionAvoidance(true);
284 return this;
285 }
286
287 /**
288 * Enables exponential backoff using the {@link #getBackOffMultiplier()} to
289 * increase the time between retries
290 */
291 public RedeliveryPolicy useExponentialBackOff() {
292 setUseExponentialBackOff(true);
293 return this;
294 }
295
296 /**
297 * Enables exponential backoff and sets the multiplier used to increase the
298 * delay between redeliveries
299 */
300 public RedeliveryPolicy backOffMultiplier(double multiplier) {
301 useExponentialBackOff();
302 setBackOffMultiplier(multiplier);
303 return this;
304 }
305
306 /**
307 * Enables collision avoidance and sets the percentage used
308 */
309 public RedeliveryPolicy collisionAvoidancePercent(double collisionAvoidancePercent) {
310 useCollisionAvoidance();
311 setCollisionAvoidancePercent(collisionAvoidancePercent);
312 return this;
313 }
314
315 /**
316 * Sets the maximum redelivery delay if using exponential back off.
317 * Use -1 if you wish to have no maximum
318 */
319 public RedeliveryPolicy maximumRedeliveryDelay(long maximumRedeliveryDelay) {
320 setMaximumRedeliveryDelay(maximumRedeliveryDelay);
321 return this;
322 }
323
324 /**
325 * Sets the logging level to use for log messages when retries have been exhausted.
326 */
327 public RedeliveryPolicy retriesExhaustedLogLevel(LoggingLevel retriesExhaustedLogLevel) {
328 setRetriesExhaustedLogLevel(retriesExhaustedLogLevel);
329 return this;
330 }
331
332 /**
333 * Sets the logging level to use for log messages when retries are attempted.
334 */
335 public RedeliveryPolicy retryAttemptedLogLevel(LoggingLevel retryAttemptedLogLevel) {
336 setRetryAttemptedLogLevel(retryAttemptedLogLevel);
337 return this;
338 }
339
340 /**
341 * Sets whether to log retry attempts
342 */
343 public RedeliveryPolicy logRetryAttempted(boolean logRetryAttempted) {
344 setLogRetryAttempted(logRetryAttempted);
345 return this;
346 }
347
348 /**
349 * Sets whether to log stacktrace for failed messages.
350 */
351 public RedeliveryPolicy logStackTrace(boolean logStackTrace) {
352 setLogStackTrace(logStackTrace);
353 return this;
354 }
355
356 /**
357 * Sets whether to log stacktrace for failed redelivery attempts
358 */
359 public RedeliveryPolicy logRetryStackTrace(boolean logRetryStackTrace) {
360 setLogRetryStackTrace(logRetryStackTrace);
361 return this;
362 }
363
364 /**
365 * Sets whether to log errors even if its handled
366 */
367 public RedeliveryPolicy logHandled(boolean logHandled) {
368 setLogHandled(logHandled);
369 return this;
370 }
371
372 /**
373 * Sets whether to log exhausted errors
374 */
375 public RedeliveryPolicy logExhausted(boolean logExhausted) {
376 setLogExhausted(logExhausted);
377 return this;
378 }
379
380 /**
381 * Sets the delay pattern with delay intervals.
382 */
383 public RedeliveryPolicy delayPattern(String delayPattern) {
384 setDelayPattern(delayPattern);
385 return this;
386 }
387
388 /**
389 * Disables redelivery by setting maximum redeliveries to 0.
390 */
391 public RedeliveryPolicy disableRedelivery() {
392 setMaximumRedeliveries(0);
393 return this;
394 }
395
396 /**
397 * Allow asynchronous delayed redelivery.
398 *
399 * @see #setAsyncDelayedRedelivery(boolean)
400 */
401 public RedeliveryPolicy asyncDelayedRedelivery() {
402 setAsyncDelayedRedelivery(true);
403 return this;
404 }
405
406 // Properties
407 // -------------------------------------------------------------------------
408
409 /**
410 * @deprecated will be removed in the near future. Instead use {@link #getRedeliveryDelay()}
411 */
412 @Deprecated
413 public long getRedeliverDelay() {
414 return getRedeliveryDelay();
415 }
416
417 /**
418 * @deprecated will be removed in the near future. Instead use {@link #setRedeliveryDelay(long)}
419 */
420 @Deprecated
421 public void setRedeliverDelay(long redeliveryDelay) {
422 setRedeliveryDelay(redeliveryDelay);
423 }
424
425 public long getRedeliveryDelay() {
426 return redeliveryDelay;
427 }
428
429 /**
430 * Sets the initial redelivery delay in milliseconds
431 */
432 public void setRedeliveryDelay(long redeliverDelay) {
433 this.redeliveryDelay = redeliverDelay;
434 // if max enabled then also set max to this value in case max was too low
435 if (maximumRedeliveryDelay > 0 && redeliverDelay > maximumRedeliveryDelay) {
436 this.maximumRedeliveryDelay = redeliverDelay;
437 }
438 }
439
440 public double getBackOffMultiplier() {
441 return backOffMultiplier;
442 }
443
444 /**
445 * Sets the multiplier used to increase the delay between redeliveries if
446 * {@link #setUseExponentialBackOff(boolean)} is enabled
447 */
448 public void setBackOffMultiplier(double backOffMultiplier) {
449 this.backOffMultiplier = backOffMultiplier;
450 }
451
452 public long getCollisionAvoidancePercent() {
453 return Math.round(collisionAvoidanceFactor * 100);
454 }
455
456 /**
457 * Sets the percentage used for collision avoidance if enabled via
458 * {@link #setUseCollisionAvoidance(boolean)}
459 */
460 public void setCollisionAvoidancePercent(double collisionAvoidancePercent) {
461 this.collisionAvoidanceFactor = collisionAvoidancePercent * 0.01d;
462 }
463
464 public double getCollisionAvoidanceFactor() {
465 return collisionAvoidanceFactor;
466 }
467
468 /**
469 * Sets the factor used for collision avoidance if enabled via
470 * {@link #setUseCollisionAvoidance(boolean)}
471 */
472 public void setCollisionAvoidanceFactor(double collisionAvoidanceFactor) {
473 this.collisionAvoidanceFactor = collisionAvoidanceFactor;
474 }
475
476 public int getMaximumRedeliveries() {
477 return maximumRedeliveries;
478 }
479
480 /**
481 * Sets the maximum number of times a message exchange will be redelivered.
482 * Setting a negative value will retry forever.
483 */
484 public void setMaximumRedeliveries(int maximumRedeliveries) {
485 this.maximumRedeliveries = maximumRedeliveries;
486 }
487
488 public long getMaximumRedeliveryDelay() {
489 return maximumRedeliveryDelay;
490 }
491
492 /**
493 * Sets the maximum redelivery delay.
494 * Use -1 if you wish to have no maximum
495 */
496 public void setMaximumRedeliveryDelay(long maximumRedeliveryDelay) {
497 this.maximumRedeliveryDelay = maximumRedeliveryDelay;
498 }
499
500 public boolean isUseCollisionAvoidance() {
501 return useCollisionAvoidance;
502 }
503
504 /**
505 * Enables/disables collision avoidance which adds some randomization to the
506 * backoff timings to reduce contention probability
507 */
508 public void setUseCollisionAvoidance(boolean useCollisionAvoidance) {
509 this.useCollisionAvoidance = useCollisionAvoidance;
510 }
511
512 public boolean isUseExponentialBackOff() {
513 return useExponentialBackOff;
514 }
515
516 /**
517 * Enables/disables exponential backoff using the
518 * {@link #getBackOffMultiplier()} to increase the time between retries
519 */
520 public void setUseExponentialBackOff(boolean useExponentialBackOff) {
521 this.useExponentialBackOff = useExponentialBackOff;
522 }
523
524 protected static synchronized Random getRandomNumberGenerator() {
525 if (randomNumberGenerator == null) {
526 randomNumberGenerator = new Random();
527 }
528 return randomNumberGenerator;
529 }
530
531 /**
532 * Sets the logging level to use for log messages when retries have been exhausted.
533 */
534 public void setRetriesExhaustedLogLevel(LoggingLevel retriesExhaustedLogLevel) {
535 this.retriesExhaustedLogLevel = retriesExhaustedLogLevel;
536 }
537
538 public LoggingLevel getRetriesExhaustedLogLevel() {
539 return retriesExhaustedLogLevel;
540 }
541
542 /**
543 * Sets the logging level to use for log messages when retries are attempted.
544 */
545 public void setRetryAttemptedLogLevel(LoggingLevel retryAttemptedLogLevel) {
546 this.retryAttemptedLogLevel = retryAttemptedLogLevel;
547 }
548
549 public LoggingLevel getRetryAttemptedLogLevel() {
550 return retryAttemptedLogLevel;
551 }
552
553 public String getDelayPattern() {
554 return delayPattern;
555 }
556
557 /**
558 * Sets an optional delay pattern to use instead of fixed delay.
559 */
560 public void setDelayPattern(String delayPattern) {
561 this.delayPattern = delayPattern;
562 }
563
564 public boolean isLogStackTrace() {
565 return logStackTrace;
566 }
567
568 /**
569 * Sets whether stack traces should be logged or not
570 */
571 public void setLogStackTrace(boolean logStackTrace) {
572 this.logStackTrace = logStackTrace;
573 }
574
575 public boolean isLogRetryStackTrace() {
576 return logRetryStackTrace;
577 }
578
579 /**
580 * Sets whether stack traces should be logged or not
581 */
582 public void setLogRetryStackTrace(boolean logRetryStackTrace) {
583 this.logRetryStackTrace = logRetryStackTrace;
584 }
585
586 public boolean isLogHandled() {
587 return logHandled;
588 }
589
590 /**
591 * Sets whether errors should be logged even if its handled
592 */
593 public void setLogHandled(boolean logHandled) {
594 this.logHandled = logHandled;
595 }
596
597 public boolean isLogContinued() {
598 return logContinued;
599 }
600
601 /**
602 * Sets whether errors should be logged even if its continued
603 */
604 public void setLogContinued(boolean logContinued) {
605 this.logContinued = logContinued;
606 }
607
608 public boolean isLogRetryAttempted() {
609 return logRetryAttempted;
610 }
611
612 /**
613 * Sets whether retry attempts should be logged or not
614 */
615 public void setLogRetryAttempted(boolean logRetryAttempted) {
616 this.logRetryAttempted = logRetryAttempted;
617 }
618
619 public boolean isLogExhausted() {
620 return logExhausted;
621 }
622
623 /**
624 * Sets whether exhausted exceptions should be logged or not
625 */
626 public void setLogExhausted(boolean logExhausted) {
627 this.logExhausted = logExhausted;
628 }
629
630 public boolean isAsyncDelayedRedelivery() {
631 return asyncDelayedRedelivery;
632 }
633
634 /**
635 * Sets whether asynchronous delayed redelivery is allowed.
636 * <p/>
637 * This is disabled by default.
638 * <p/>
639 * When enabled it allows Camel to schedule a future task for delayed
640 * redelivery which prevents current thread from blocking while waiting.
641 * <p/>
642 * Exchange which is transacted will however always use synchronous delayed redelivery
643 * because the transaction must execute in the same thread context.
644 *
645 * @param asyncDelayedRedelivery whether asynchronous delayed redelivery is allowed
646 */
647 public void setAsyncDelayedRedelivery(boolean asyncDelayedRedelivery) {
648 this.asyncDelayedRedelivery = asyncDelayedRedelivery;
649 }
650 }