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 }