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.commons.logging.Log; 023 import org.apache.commons.logging.LogFactory; 024 025 // Code taken from the ActiveMQ codebase 026 027 /** 028 * The policy used to decide how many times to redeliver and the time between 029 * the redeliveries before being sent to a <a 030 * href="http://activemq.apache.org/camel/dead-letter-channel.html">Dead Letter 031 * Channel</a> 032 * <p> 033 * The default values is: 034 * <ul> 035 * <li>maximumRedeliveries = 5</li> 036 * <li>delay = 1000L (the initial delay)</li> 037 * <li>maximumRedeliveryDelay = 60 * 1000L</li> 038 * <li>backOffMultiplier = 2</li> 039 * <li>useExponentialBackOff = false</li> 040 * <li>collisionAvoidanceFactor = 0.15d</li> 041 * <li>useCollisionAvoidance = false</li> 042 * </ul> 043 * <p/> 044 * Setting the maximumRedeliveries to a negative value such as -1 will then always redeliver (unlimited). 045 * Setting the maximumRedeliveries to 0 will disable redelivery. 046 * 047 * @version $Revision: 695104 $ 048 */ 049 public class RedeliveryPolicy extends DelayPolicy { 050 protected static transient Random randomNumberGenerator; 051 private static final transient Log LOG = LogFactory.getLog(RedeliveryPolicy.class); 052 053 protected int maximumRedeliveries = 5; 054 protected long maximumRedeliveryDelay = 60 * 1000L; 055 protected double backOffMultiplier = 2; 056 protected boolean useExponentialBackOff; 057 // +/-15% for a 30% spread -cgs 058 protected double collisionAvoidanceFactor = 0.15d; 059 protected boolean useCollisionAvoidance; 060 061 public RedeliveryPolicy() { 062 } 063 064 @Override 065 public String toString() { 066 return "RedeliveryPolicy[maximumRedeliveries=" + maximumRedeliveries 067 + ", initialRedeliveryDelay=" + delay 068 + ", maximumRedeliveryDelay=" + maximumRedeliveryDelay 069 + ", useExponentialBackOff=" + useExponentialBackOff 070 + ", backOffMultiplier=" + backOffMultiplier 071 + ", useCollisionAvoidance=" + useCollisionAvoidance 072 + ", collisionAvoidanceFactor=" + collisionAvoidanceFactor + "]"; 073 } 074 075 public RedeliveryPolicy copy() { 076 try { 077 return (RedeliveryPolicy)clone(); 078 } catch (CloneNotSupportedException e) { 079 throw new RuntimeException("Could not clone: " + e, e); 080 } 081 } 082 083 /** 084 * Returns true if the policy decides that the message exchange should be 085 * redelivered 086 */ 087 public boolean shouldRedeliver(int redeliveryCounter) { 088 if (getMaximumRedeliveries() < 0) { 089 return true; 090 } 091 // redeliver until we hitted the max 092 return redeliveryCounter <= getMaximumRedeliveries(); 093 } 094 095 096 /** 097 * Calculates the new redelivery delay based on the last one then sleeps for the necessary amount of time 098 */ 099 public long sleep(long redeliveryDelay) { 100 redeliveryDelay = getRedeliveryDelay(redeliveryDelay); 101 102 if (redeliveryDelay > 0) { 103 if (LOG.isDebugEnabled()) { 104 LOG.debug("Sleeping for: " + redeliveryDelay + " millis until attempting redelivery"); 105 } 106 try { 107 Thread.sleep(redeliveryDelay); 108 } catch (InterruptedException e) { 109 if (LOG.isDebugEnabled()) { 110 LOG.debug("Thread interrupted: " + e, e); 111 } 112 } 113 } 114 return redeliveryDelay; 115 } 116 117 118 public long getRedeliveryDelay(long previousDelay) { 119 long redeliveryDelay; 120 121 if (previousDelay == 0) { 122 redeliveryDelay = delay; 123 } else if (useExponentialBackOff && backOffMultiplier > 1) { 124 redeliveryDelay = Math.round(backOffMultiplier * previousDelay); 125 } else { 126 redeliveryDelay = previousDelay; 127 } 128 129 if (useCollisionAvoidance) { 130 131 /* 132 * First random determines +/-, second random determines how far to 133 * go in that direction. -cgs 134 */ 135 Random random = getRandomNumberGenerator(); 136 double variance = (random.nextBoolean() ? collisionAvoidanceFactor : -collisionAvoidanceFactor) 137 * random.nextDouble(); 138 redeliveryDelay += redeliveryDelay * variance; 139 } 140 141 if (maximumRedeliveryDelay > 0 && redeliveryDelay > maximumRedeliveryDelay) { 142 redeliveryDelay = maximumRedeliveryDelay; 143 } 144 145 return redeliveryDelay; 146 } 147 148 149 // Builder methods 150 // ------------------------------------------------------------------------- 151 152 /** 153 * Sets the maximum number of times a message exchange will be redelivered 154 */ 155 public RedeliveryPolicy maximumRedeliveries(int maximumRedeliveries) { 156 setMaximumRedeliveries(maximumRedeliveries); 157 return this; 158 } 159 160 /** 161 * Sets the initial redelivery delay in milliseconds on the first redelivery 162 * 163 * @deprecated use delay. Will be removed in Camel 2.0. 164 */ 165 public RedeliveryPolicy initialRedeliveryDelay(long initialRedeliveryDelay) { 166 setDelay(initialRedeliveryDelay); 167 return this; 168 } 169 170 /** 171 * Enables collision avoidance which adds some randomization to the backoff 172 * timings to reduce contention probability 173 */ 174 public RedeliveryPolicy useCollisionAvoidance() { 175 setUseCollisionAvoidance(true); 176 return this; 177 } 178 179 /** 180 * Enables exponential backoff using the {@link #getBackOffMultiplier()} to 181 * increase the time between retries 182 */ 183 public RedeliveryPolicy useExponentialBackOff() { 184 setUseExponentialBackOff(true); 185 return this; 186 } 187 188 /** 189 * Enables exponential backoff and sets the multiplier used to increase the 190 * delay between redeliveries 191 */ 192 public RedeliveryPolicy backOffMultiplier(double multiplier) { 193 useExponentialBackOff(); 194 setBackOffMultiplier(multiplier); 195 return this; 196 } 197 198 /** 199 * Enables collision avoidance and sets the percentage used 200 */ 201 public RedeliveryPolicy collisionAvoidancePercent(double collisionAvoidancePercent) { 202 useCollisionAvoidance(); 203 setCollisionAvoidancePercent(collisionAvoidancePercent); 204 return this; 205 } 206 207 /** 208 * Sets the maximum redelivery delay if using exponential back off. 209 * Use -1 if you wish to have no maximum 210 */ 211 public RedeliveryPolicy maximumRedeliveryDelay(long maximumRedeliveryDelay) { 212 setMaximumRedeliveryDelay(maximumRedeliveryDelay); 213 return this; 214 } 215 216 // Properties 217 // ------------------------------------------------------------------------- 218 public double getBackOffMultiplier() { 219 return backOffMultiplier; 220 } 221 222 /** 223 * Sets the multiplier used to increase the delay between redeliveries if 224 * {@link #setUseExponentialBackOff(boolean)} is enabled 225 */ 226 public void setBackOffMultiplier(double backOffMultiplier) { 227 this.backOffMultiplier = backOffMultiplier; 228 } 229 230 public short getCollisionAvoidancePercent() { 231 return (short)Math.round(collisionAvoidanceFactor * 100); 232 } 233 234 /** 235 * Sets the percentage used for collision avoidance if enabled via 236 * {@link #setUseCollisionAvoidance(boolean)} 237 */ 238 public void setCollisionAvoidancePercent(double collisionAvoidancePercent) { 239 this.collisionAvoidanceFactor = collisionAvoidancePercent * 0.01d; 240 } 241 242 public double getCollisionAvoidanceFactor() { 243 return collisionAvoidanceFactor; 244 } 245 246 /** 247 * Sets the factor used for collision avoidance if enabled via 248 * {@link #setUseCollisionAvoidance(boolean)} 249 */ 250 public void setCollisionAvoidanceFactor(double collisionAvoidanceFactor) { 251 this.collisionAvoidanceFactor = collisionAvoidanceFactor; 252 } 253 254 /** 255 * @deprecated use delay instead. Will be removed in Camel 2.0. 256 */ 257 public long getInitialRedeliveryDelay() { 258 return getDelay(); 259 } 260 261 /** 262 * Sets the initial redelivery delay in milliseconds on the first redelivery 263 * 264 * @deprecated use delay instead. Will be removed in Camel 2.0. 265 */ 266 public void setInitialRedeliveryDelay(long initialRedeliveryDelay) { 267 setDelay(initialRedeliveryDelay); 268 } 269 270 public int getMaximumRedeliveries() { 271 return maximumRedeliveries; 272 } 273 274 /** 275 * Sets the maximum number of times a message exchange will be redelivered. 276 * Setting a negative value will retry forever. 277 */ 278 public void setMaximumRedeliveries(int maximumRedeliveries) { 279 this.maximumRedeliveries = maximumRedeliveries; 280 } 281 282 public long getMaximumRedeliveryDelay() { 283 return maximumRedeliveryDelay; 284 } 285 286 /** 287 * Sets the maximum redelivery delay if using exponential back off. 288 * Use -1 if you wish to have no maximum 289 */ 290 public void setMaximumRedeliveryDelay(long maximumRedeliveryDelay) { 291 this.maximumRedeliveryDelay = maximumRedeliveryDelay; 292 } 293 294 public boolean isUseCollisionAvoidance() { 295 return useCollisionAvoidance; 296 } 297 298 /** 299 * Enables/disables collision avoidance which adds some randomization to the 300 * backoff timings to reduce contention probability 301 */ 302 public void setUseCollisionAvoidance(boolean useCollisionAvoidance) { 303 this.useCollisionAvoidance = useCollisionAvoidance; 304 } 305 306 public boolean isUseExponentialBackOff() { 307 return useExponentialBackOff; 308 } 309 310 /** 311 * Enables/disables exponential backoff using the 312 * {@link #getBackOffMultiplier()} to increase the time between retries 313 */ 314 public void setUseExponentialBackOff(boolean useExponentialBackOff) { 315 this.useExponentialBackOff = useExponentialBackOff; 316 } 317 318 protected static synchronized Random getRandomNumberGenerator() { 319 if (randomNumberGenerator == null) { 320 randomNumberGenerator = new Random(); 321 } 322 return randomNumberGenerator; 323 } 324 }