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