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