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