001    /**
002     *
003     * Licensed to the Apache Software Foundation (ASF) under one or more
004     * contributor license agreements.  See the NOTICE file distributed with
005     * this work for additional information regarding copyright ownership.
006     * The ASF licenses this file to You under the Apache License, Version 2.0
007     * (the "License"); you may not use this file except in compliance with
008     * the License.  You may obtain a copy of the License at
009     *
010     * http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    package org.apache.camel.processor;
019    
020    import java.io.Serializable;
021    import java.util.Random;
022    
023    // Code taken from the ActiveMQ codebase
024    
025    /**
026     * The policy used to decide how many times to redeliver and the time between the redeliveries before being sent to a
027     * <a href="http://activemq.apache.org/camel/dead-letter-channel.html">Dead Letter Channel</a>
028     *
029     * @version $Revision: 530858 $
030     */
031    public class RedeliveryPolicy implements Cloneable, Serializable {
032        protected static transient Random randomNumberGenerator;
033        protected int maximumRedeliveries = 6;
034        protected long initialRedeliveryDelay = 1000L;
035        protected double backOffMultiplier = 2;
036        protected boolean useExponentialBackOff = false;
037        // +/-15% for a 30% spread -cgs
038        protected double collisionAvoidanceFactor = 0.15d;
039        protected boolean useCollisionAvoidance = false;
040    
041        public RedeliveryPolicy() {
042        }
043    
044        @Override
045        public String toString() {
046            return "RedeliveryPolicy[maximumRedeliveries=" + maximumRedeliveries + "]";
047        }
048    
049        public RedeliveryPolicy copy() {
050            try {
051                return (RedeliveryPolicy) clone();
052            }
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 redelivered
060         */
061        public boolean shouldRedeliver(int redeliveryCounter) {
062            return redeliveryCounter < getMaximumRedeliveries();
063        }
064    
065        // Builder methods
066        //-------------------------------------------------------------------------
067    
068        /**
069         * Sets the maximum number of times a message exchange will be redelivered
070         */
071        public RedeliveryPolicy maximumRedeliveries(int maximumRedeliveries) {
072            setMaximumRedeliveries(maximumRedeliveries);
073            return this;
074        }
075    
076        /**
077         * Sets the initial redelivery delay in milliseconds on the first redelivery
078         */
079        public RedeliveryPolicy initialRedeliveryDelay(long initialRedeliveryDelay) {
080            setInitialRedeliveryDelay(initialRedeliveryDelay);
081            return this;
082        }
083    
084        /**
085         * Enables collision avoidence which adds some randomization to the backoff timings to reduce contention probability
086         */
087        public RedeliveryPolicy useCollisionAvoidance() {
088            setUseCollisionAvoidance(true);
089            return this;
090        }
091    
092        /**
093         * Enables exponential backof using the {@link #getBackOffMultiplier()} to increase the time between retries
094         */
095        public RedeliveryPolicy useExponentialBackOff() {
096            setUseExponentialBackOff(true);
097            return this;
098        }
099    
100        /**
101         * Enables exponential backoff and sets the multiplier used to increase the delay between redeliveries
102         */
103        public RedeliveryPolicy backOffMultiplier(double backOffMultiplier) {
104            useExponentialBackOff();
105            setBackOffMultiplier(backOffMultiplier);
106            return this;
107        }
108    
109        /**
110         * Enables collision avoidence and sets the percentage used
111         */
112        public RedeliveryPolicy collisionAvoidancePercent(short collisionAvoidancePercent) {
113            useCollisionAvoidance();
114            setCollisionAvoidancePercent(collisionAvoidancePercent);
115            return this;
116        }
117    
118        // Properties
119        //-------------------------------------------------------------------------
120        public double getBackOffMultiplier() {
121            return backOffMultiplier;
122        }
123    
124        /**
125         * Sets the multiplier used to increase the delay between redeliveries if {@link #setUseExponentialBackOff(boolean)} is enabled
126         */
127        public void setBackOffMultiplier(double backOffMultiplier) {
128            this.backOffMultiplier = backOffMultiplier;
129        }
130    
131        public short getCollisionAvoidancePercent() {
132            return (short) Math.round(collisionAvoidanceFactor * 100);
133        }
134    
135        /**
136         * Sets the percentage used for collision avoidence if enabled via {@link #setUseCollisionAvoidance(boolean)}
137         */
138        public void setCollisionAvoidancePercent(short collisionAvoidancePercent) {
139            this.collisionAvoidanceFactor = collisionAvoidancePercent * 0.01d;
140        }
141    
142        public double getCollisionAvoidanceFactor() {
143            return collisionAvoidanceFactor;
144        }
145    
146        /**
147         * Sets the factor used for collision avoidence if enabled via {@link #setUseCollisionAvoidance(boolean)}
148         */
149        public void setCollisionAvoidanceFactor(double collisionAvoidanceFactor) {
150            this.collisionAvoidanceFactor = collisionAvoidanceFactor;
151        }
152    
153        public long getInitialRedeliveryDelay() {
154            return initialRedeliveryDelay;
155        }
156    
157        /**
158         * Sets the initial redelivery delay in milliseconds on the first redelivery
159         */
160        public void setInitialRedeliveryDelay(long initialRedeliveryDelay) {
161            this.initialRedeliveryDelay = initialRedeliveryDelay;
162        }
163    
164        public int getMaximumRedeliveries() {
165            return maximumRedeliveries;
166        }
167    
168        /**
169         * Sets the maximum number of times a message exchange will be redelivered
170         */
171        public void setMaximumRedeliveries(int maximumRedeliveries) {
172            this.maximumRedeliveries = maximumRedeliveries;
173        }
174    
175        public long getRedeliveryDelay(long previousDelay) {
176            long redeliveryDelay;
177    
178            if (previousDelay == 0) {
179                redeliveryDelay = initialRedeliveryDelay;
180            }
181            else if (useExponentialBackOff && backOffMultiplier > 1) {
182                redeliveryDelay = Math.round(backOffMultiplier * previousDelay);
183            }
184            else {
185                redeliveryDelay = previousDelay;
186            }
187    
188            if (useCollisionAvoidance) {
189    
190                /*
191                 * First random determines +/-, second random determines how far to
192                 * go in that direction. -cgs
193                 */
194                Random random = getRandomNumberGenerator();
195                double variance = (random.nextBoolean() ? collisionAvoidanceFactor : -collisionAvoidanceFactor) * random.nextDouble();
196                redeliveryDelay += redeliveryDelay * variance;
197            }
198    
199            return redeliveryDelay;
200        }
201    
202        public boolean isUseCollisionAvoidance() {
203            return useCollisionAvoidance;
204        }
205    
206        /**
207         * Enables/disables collision avoidence which adds some randomization to the backoff timings to reduce contention probability
208         */
209        public void setUseCollisionAvoidance(boolean useCollisionAvoidance) {
210            this.useCollisionAvoidance = useCollisionAvoidance;
211        }
212    
213        public boolean isUseExponentialBackOff() {
214            return useExponentialBackOff;
215        }
216    
217        /**
218         * Enables/disables exponential backof using the {@link #getBackOffMultiplier()} to increase the time between retries
219         */
220        public void setUseExponentialBackOff(boolean useExponentialBackOff) {
221            this.useExponentialBackOff = useExponentialBackOff;
222        }
223    
224        protected static synchronized Random getRandomNumberGenerator() {
225            if (randomNumberGenerator == null) {
226                randomNumberGenerator = new Random();
227            }
228            return randomNumberGenerator;
229        }
230    }