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: 565361 $
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            return redeliveryCounter < getMaximumRedeliveries();
064        }
065    
066        // Builder methods
067        // -------------------------------------------------------------------------
068    
069        /**
070         * Sets the maximum number of times a message exchange will be redelivered
071         */
072        public RedeliveryPolicy maximumRedeliveries(int maximumRedeliveries) {
073            setMaximumRedeliveries(maximumRedeliveries);
074            return this;
075        }
076    
077        /**
078         * Sets the initial redelivery delay in milliseconds on the first redelivery
079         */
080        public RedeliveryPolicy initialRedeliveryDelay(long initialRedeliveryDelay) {
081            setInitialRedeliveryDelay(initialRedeliveryDelay);
082            return this;
083        }
084    
085        /**
086         * Enables collision avoidence which adds some randomization to the backoff
087         * timings to reduce contention probability
088         */
089        public RedeliveryPolicy useCollisionAvoidance() {
090            setUseCollisionAvoidance(true);
091            return this;
092        }
093    
094        /**
095         * Enables exponential backof using the {@link #getBackOffMultiplier()} to
096         * increase the time between retries
097         */
098        public RedeliveryPolicy useExponentialBackOff() {
099            setUseExponentialBackOff(true);
100            return this;
101        }
102    
103        /**
104         * Enables exponential backoff and sets the multiplier used to increase the
105         * delay between redeliveries
106         */
107        public RedeliveryPolicy backOffMultiplier(double multiplier) {
108            useExponentialBackOff();
109            setBackOffMultiplier(multiplier);
110            return this;
111        }
112    
113        /**
114         * Enables collision avoidence and sets the percentage used
115         */
116        public RedeliveryPolicy collisionAvoidancePercent(double collisionAvoidancePercent) {
117            useCollisionAvoidance();
118            setCollisionAvoidancePercent(collisionAvoidancePercent);
119            return this;
120        }
121    
122        // Properties
123        // -------------------------------------------------------------------------
124        public double getBackOffMultiplier() {
125            return backOffMultiplier;
126        }
127    
128        /**
129         * Sets the multiplier used to increase the delay between redeliveries if
130         * {@link #setUseExponentialBackOff(boolean)} is enabled
131         */
132        public void setBackOffMultiplier(double backOffMultiplier) {
133            this.backOffMultiplier = backOffMultiplier;
134        }
135    
136        public short getCollisionAvoidancePercent() {
137            return (short)Math.round(collisionAvoidanceFactor * 100);
138        }
139    
140        /**
141         * Sets the percentage used for collision avoidence if enabled via
142         * {@link #setUseCollisionAvoidance(boolean)}
143         */
144        public void setCollisionAvoidancePercent(double collisionAvoidancePercent) {
145            this.collisionAvoidanceFactor = collisionAvoidancePercent * 0.01d;
146        }
147    
148        public double getCollisionAvoidanceFactor() {
149            return collisionAvoidanceFactor;
150        }
151    
152        /**
153         * Sets the factor used for collision avoidence if enabled via
154         * {@link #setUseCollisionAvoidance(boolean)}
155         */
156        public void setCollisionAvoidanceFactor(double collisionAvoidanceFactor) {
157            this.collisionAvoidanceFactor = collisionAvoidanceFactor;
158        }
159    
160        public long getInitialRedeliveryDelay() {
161            return initialRedeliveryDelay;
162        }
163    
164        /**
165         * Sets the initial redelivery delay in milliseconds on the first redelivery
166         */
167        public void setInitialRedeliveryDelay(long initialRedeliveryDelay) {
168            this.initialRedeliveryDelay = initialRedeliveryDelay;
169        }
170    
171        public int getMaximumRedeliveries() {
172            return maximumRedeliveries;
173        }
174    
175        /**
176         * Sets the maximum number of times a message exchange will be redelivered
177         */
178        public void setMaximumRedeliveries(int maximumRedeliveries) {
179            this.maximumRedeliveries = maximumRedeliveries;
180        }
181    
182        public long getRedeliveryDelay(long previousDelay) {
183            long redeliveryDelay;
184    
185            if (previousDelay == 0) {
186                redeliveryDelay = initialRedeliveryDelay;
187            } else if (useExponentialBackOff && backOffMultiplier > 1) {
188                redeliveryDelay = Math.round(backOffMultiplier * previousDelay);
189            } else {
190                redeliveryDelay = previousDelay;
191            }
192    
193            if (useCollisionAvoidance) {
194    
195                /*
196                 * First random determines +/-, second random determines how far to
197                 * go in that direction. -cgs
198                 */
199                Random random = getRandomNumberGenerator();
200                double variance = (random.nextBoolean() ? collisionAvoidanceFactor : -collisionAvoidanceFactor)
201                                  * random.nextDouble();
202                redeliveryDelay += redeliveryDelay * variance;
203            }
204    
205            return redeliveryDelay;
206        }
207    
208        public boolean isUseCollisionAvoidance() {
209            return useCollisionAvoidance;
210        }
211    
212        /**
213         * Enables/disables collision avoidence which adds some randomization to the
214         * backoff timings to reduce contention probability
215         */
216        public void setUseCollisionAvoidance(boolean useCollisionAvoidance) {
217            this.useCollisionAvoidance = useCollisionAvoidance;
218        }
219    
220        public boolean isUseExponentialBackOff() {
221            return useExponentialBackOff;
222        }
223    
224        /**
225         * Enables/disables exponential backof using the
226         * {@link #getBackOffMultiplier()} to increase the time between retries
227         */
228        public void setUseExponentialBackOff(boolean useExponentialBackOff) {
229            this.useExponentialBackOff = useExponentialBackOff;
230        }
231    
232        protected static synchronized Random getRandomNumberGenerator() {
233            if (randomNumberGenerator == null) {
234                randomNumberGenerator = new Random();
235            }
236            return randomNumberGenerator;
237        }
238    }