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: 61064 $ 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 }