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 }