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