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    }