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 }