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    // Code taken from the ActiveMQ codebase
023    
024    /**
025     * The policy used to decide how many times to redeliver and the time between
026     * the redeliveries before being sent to a <a
027     * href="http://activemq.apache.org/camel/dead-letter-channel.html">Dead Letter
028     * Channel</a>
029     * <p>
030     * The default values is:
031     * <ul>
032     *   <li>maximumRedeliveries = 6</li>
033     *   <li>initialRedeliveryDelay = 1000L</li>
034     *   <li>backOffMultiplier = 2</li>
035     *   <li>useExponentialBackOff = false</li>
036     *   <li>collisionAvoidanceFactor = 0.15d</li>
037     *   <li>useCollisionAvoidance = false</li>
038     * </ul>
039     *
040     * @version $Revision: 37863 $
041     */
042    public class RedeliveryPolicy implements Cloneable, Serializable {
043        protected static transient Random randomNumberGenerator;
044        protected int maximumRedeliveries = 6;
045        protected long initialRedeliveryDelay = 1000L;
046        protected double backOffMultiplier = 2;
047        protected boolean useExponentialBackOff;
048        // +/-15% for a 30% spread -cgs
049        protected double collisionAvoidanceFactor = 0.15d;
050        protected boolean useCollisionAvoidance;
051    
052        public RedeliveryPolicy() {
053        }
054    
055        @Override
056        public String toString() {
057            return "RedeliveryPolicy[maximumRedeliveries=" + maximumRedeliveries + "]";
058        }
059    
060        public RedeliveryPolicy copy() {
061            try {
062                return (RedeliveryPolicy)clone();
063            } catch (CloneNotSupportedException e) {
064                throw new RuntimeException("Could not clone: " + e, e);
065            }
066        }
067    
068        /**
069         * Returns true if the policy decides that the message exchange should be
070         * redelivered
071         */
072        public boolean shouldRedeliver(int redeliveryCounter) {
073            if (getMaximumRedeliveries() < 0) {
074                return true;
075            }
076            return redeliveryCounter < getMaximumRedeliveries();
077        }
078    
079        // Builder methods
080        // -------------------------------------------------------------------------
081    
082        /**
083         * Sets the maximum number of times a message exchange will be redelivered
084         */
085        public RedeliveryPolicy maximumRedeliveries(int maximumRedeliveries) {
086            setMaximumRedeliveries(maximumRedeliveries);
087            return this;
088        }
089    
090        /**
091         * Sets the initial redelivery delay in milliseconds on the first redelivery
092         */
093        public RedeliveryPolicy initialRedeliveryDelay(long initialRedeliveryDelay) {
094            setInitialRedeliveryDelay(initialRedeliveryDelay);
095            return this;
096        }
097    
098        /**
099         * Enables collision avoidence which adds some randomization to the backoff
100         * timings to reduce contention probability
101         */
102        public RedeliveryPolicy useCollisionAvoidance() {
103            setUseCollisionAvoidance(true);
104            return this;
105        }
106    
107        /**
108         * Enables exponential backof using the {@link #getBackOffMultiplier()} to
109         * increase the time between retries
110         */
111        public RedeliveryPolicy useExponentialBackOff() {
112            setUseExponentialBackOff(true);
113            return this;
114        }
115    
116        /**
117         * Enables exponential backoff and sets the multiplier used to increase the
118         * delay between redeliveries
119         */
120        public RedeliveryPolicy backOffMultiplier(double multiplier) {
121            useExponentialBackOff();
122            setBackOffMultiplier(multiplier);
123            return this;
124        }
125    
126        /**
127         * Enables collision avoidence and sets the percentage used
128         */
129        public RedeliveryPolicy collisionAvoidancePercent(double collisionAvoidancePercent) {
130            useCollisionAvoidance();
131            setCollisionAvoidancePercent(collisionAvoidancePercent);
132            return this;
133        }
134    
135        // Properties
136        // -------------------------------------------------------------------------
137        public double getBackOffMultiplier() {
138            return backOffMultiplier;
139        }
140    
141        /**
142         * Sets the multiplier used to increase the delay between redeliveries if
143         * {@link #setUseExponentialBackOff(boolean)} is enabled
144         */
145        public void setBackOffMultiplier(double backOffMultiplier) {
146            this.backOffMultiplier = backOffMultiplier;
147        }
148    
149        public short getCollisionAvoidancePercent() {
150            return (short)Math.round(collisionAvoidanceFactor * 100);
151        }
152    
153        /**
154         * Sets the percentage used for collision avoidence if enabled via
155         * {@link #setUseCollisionAvoidance(boolean)}
156         */
157        public void setCollisionAvoidancePercent(double collisionAvoidancePercent) {
158            this.collisionAvoidanceFactor = collisionAvoidancePercent * 0.01d;
159        }
160    
161        public double getCollisionAvoidanceFactor() {
162            return collisionAvoidanceFactor;
163        }
164    
165        /**
166         * Sets the factor used for collision avoidence if enabled via
167         * {@link #setUseCollisionAvoidance(boolean)}
168         */
169        public void setCollisionAvoidanceFactor(double collisionAvoidanceFactor) {
170            this.collisionAvoidanceFactor = collisionAvoidanceFactor;
171        }
172    
173        public long getInitialRedeliveryDelay() {
174            return initialRedeliveryDelay;
175        }
176    
177        /**
178         * Sets the initial redelivery delay in milliseconds on the first redelivery
179         */
180        public void setInitialRedeliveryDelay(long initialRedeliveryDelay) {
181            this.initialRedeliveryDelay = initialRedeliveryDelay;
182        }
183    
184        public int getMaximumRedeliveries() {
185            return maximumRedeliveries;
186        }
187    
188        /**
189         * Sets the maximum number of times a message exchange will be redelivered.
190         * Setting a negative value will retry forever.
191         */
192        public void setMaximumRedeliveries(int maximumRedeliveries) {
193            this.maximumRedeliveries = maximumRedeliveries;
194        }
195    
196        public long getRedeliveryDelay(long previousDelay) {
197            long redeliveryDelay;
198    
199            if (previousDelay == 0) {
200                redeliveryDelay = initialRedeliveryDelay;
201            } else if (useExponentialBackOff && backOffMultiplier > 1) {
202                redeliveryDelay = Math.round(backOffMultiplier * previousDelay);
203            } else {
204                redeliveryDelay = previousDelay;
205            }
206    
207            if (useCollisionAvoidance) {
208    
209                /*
210                 * First random determines +/-, second random determines how far to
211                 * go in that direction. -cgs
212                 */
213                Random random = getRandomNumberGenerator();
214                double variance = (random.nextBoolean() ? collisionAvoidanceFactor : -collisionAvoidanceFactor)
215                                  * random.nextDouble();
216                redeliveryDelay += redeliveryDelay * variance;
217            }
218    
219            return redeliveryDelay;
220        }
221    
222        public boolean isUseCollisionAvoidance() {
223            return useCollisionAvoidance;
224        }
225    
226        /**
227         * Enables/disables collision avoidence which adds some randomization to the
228         * backoff timings to reduce contention probability
229         */
230        public void setUseCollisionAvoidance(boolean useCollisionAvoidance) {
231            this.useCollisionAvoidance = useCollisionAvoidance;
232        }
233    
234        public boolean isUseExponentialBackOff() {
235            return useExponentialBackOff;
236        }
237    
238        /**
239         * Enables/disables exponential backof using the
240         * {@link #getBackOffMultiplier()} to increase the time between retries
241         */
242        public void setUseExponentialBackOff(boolean useExponentialBackOff) {
243            this.useExponentialBackOff = useExponentialBackOff;
244        }
245    
246        protected static synchronized Random getRandomNumberGenerator() {
247            if (randomNumberGenerator == null) {
248                randomNumberGenerator = new Random();
249            }
250            return randomNumberGenerator;
251        }
252    }