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