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