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