1 /*
2 * Copyright 2006-2013 the original author or authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package org.springframework.retry.policy;
18
19 import java.util.Collections;
20 import java.util.Map;
21
22 import org.springframework.classify.BinaryExceptionClassifier;
23 import org.springframework.retry.RetryContext;
24 import org.springframework.retry.RetryPolicy;
25 import org.springframework.retry.context.RetryContextSupport;
26 import org.springframework.util.ClassUtils;
27
28 /**
29 *
30 * Simple retry policy that retries a fixed number of times for a set of named
31 * exceptions (and subclasses). The number of attempts includes the initial try,
32 * so e.g.
33 *
34 * <pre>
35 * retryTemplate = new RetryTemplate(new SimpleRetryPolicy(3));
36 * retryTemplate.execute(callback);
37 * </pre>
38 *
39 * will execute the callback at least once, and as many as 3 times.
40 *
41 * @author Dave Syer
42 * @author Rob Harrop
43 * @author Gary Russell
44 *
45 */
46 @SuppressWarnings("serial")
47 public class SimpleRetryPolicy implements RetryPolicy {
48
49 /**
50 * The default limit to the number of attempts for a new policy.
51 */
52 public final static int DEFAULT_MAX_ATTEMPTS = 3;
53
54 private volatile int maxAttempts;
55
56 private BinaryExceptionClassifier retryableClassifier = new BinaryExceptionClassifier(false);
57
58 /**
59 * Create a {@link SimpleRetryPolicy} with the default number of retry
60 * attempts, retrying all exceptions.
61 */
62 public SimpleRetryPolicy() {
63 this(DEFAULT_MAX_ATTEMPTS, Collections
64 .<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true));
65 }
66
67 /**
68 * Create a {@link SimpleRetryPolicy} with the specified number of retry
69 * attempts, retrying all exceptions.
70 */
71 public SimpleRetryPolicy(int maxAttempts) {
72 this(maxAttempts, Collections
73 .<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true));
74 }
75
76 /**
77 * Create a {@link SimpleRetryPolicy} with the specified number of retry
78 * attempts.
79 *
80 * @param maxAttempts the maximum number of attempts
81 * @param retryableExceptions the map of exceptions that are retryable
82 */
83 public SimpleRetryPolicy(int maxAttempts, Map<Class<? extends Throwable>, Boolean> retryableExceptions) {
84 this(maxAttempts, retryableExceptions, false);
85 }
86
87 /**
88 * Create a {@link SimpleRetryPolicy} with the specified number of retry
89 * attempts. If traverseCauses is true, the exception causes will be traversed until
90 * a match is found.
91 *
92 * @param maxAttempts the maximum number of attempts
93 * @param retryableExceptions the map of exceptions that are retryable based on the
94 * map value (true/false).
95 * @param traverseCauses is this clause traversable
96 */
97 public SimpleRetryPolicy(int maxAttempts, Map<Class<? extends Throwable>, Boolean> retryableExceptions,
98 boolean traverseCauses) {
99 this(maxAttempts, retryableExceptions, traverseCauses, false);
100 }
101
102 /**
103 * Create a {@link SimpleRetryPolicy} with the specified number of retry
104 * attempts. If traverseCauses is true, the exception causes will be traversed until
105 * a match is found. The default value indicates whether to retry or not for exceptions
106 * (or super classes) are not found in the map.
107 *
108 * @param maxAttempts the maximum number of attempts
109 * @param retryableExceptions the map of exceptions that are retryable based on the
110 * map value (true/false).
111 * @param traverseCauses is this clause traversable
112 * @param defaultValue the default action.
113 */
114 public SimpleRetryPolicy(int maxAttempts, Map<Class<? extends Throwable>, Boolean> retryableExceptions,
115 boolean traverseCauses, boolean defaultValue) {
116 super();
117 this.maxAttempts = maxAttempts;
118 this.retryableClassifier = new BinaryExceptionClassifier(retryableExceptions, defaultValue);
119 this.retryableClassifier.setTraverseCauses(traverseCauses);
120 }
121
122 /**
123 * Set the number of attempts before retries are exhausted. Includes the initial
124 * attempt before the retries begin so, generally, will be {@code >= 1}. For example
125 * setting this property to 3 means 3 attempts total (initial + 2 retries).
126 *
127 * @param maxAttempts the maximum number of attempts including the initial attempt.
128 */
129 public void setMaxAttempts(int maxAttempts) {
130 this.maxAttempts = maxAttempts;
131 }
132
133 /**
134 * The maximum number of attempts before failure.
135 *
136 * @return the maximum number of attempts
137 */
138 public int getMaxAttempts() {
139 return this.maxAttempts;
140 }
141
142 /**
143 * Test for retryable operation based on the status.
144 *
145 * @see org.springframework.retry.RetryPolicy#canRetry(org.springframework.retry.RetryContext)
146 *
147 * @return true if the last exception was retryable and the number of
148 * attempts so far is less than the limit.
149 */
150 @Override
151 public boolean canRetry(RetryContext context) {
152 Throwable t = context.getLastThrowable();
153 return (t == null || retryForException(t)) && context.getRetryCount() < maxAttempts;
154 }
155
156 /**
157 * @see org.springframework.retry.RetryPolicy#close(RetryContext)
158 */
159 @Override
160 public void close(RetryContext status) {
161 }
162
163 /**
164 * Update the status with another attempted retry and the latest exception.
165 *
166 * @see RetryPolicy#registerThrowable(RetryContext, Throwable)
167 */
168 @Override
169 public void registerThrowable(RetryContext context, Throwable throwable) {
170 SimpleRetryContext simpleContext = ((SimpleRetryContext) context);
171 simpleContext.registerThrowable(throwable);
172 }
173
174 /**
175 * Get a status object that can be used to track the current operation
176 * according to this policy. Has to be aware of the latest exception and the
177 * number of attempts.
178 *
179 * @see org.springframework.retry.RetryPolicy#open(RetryContext)
180 */
181 @Override
182 public RetryContext open(RetryContext parent) {
183 return new SimpleRetryContext(parent);
184 }
185
186 private static class SimpleRetryContext extends RetryContextSupport {
187 public SimpleRetryContext(RetryContext parent) {
188 super(parent);
189 }
190 }
191
192 /**
193 * Delegates to an exception classifier.
194 *
195 * @param ex
196 * @return true if this exception or its ancestors have been registered as
197 * retryable.
198 */
199 private boolean retryForException(Throwable ex) {
200 return retryableClassifier.classify(ex);
201 }
202
203 @Override
204 public String toString() {
205 return ClassUtils.getShortName(getClass()) + "[maxAttempts=" + maxAttempts + "]";
206 }
207 }
208