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 }