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