1 | /* |
2 | * Copyright 2006-2007 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 | * http://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.batch.retry.policy; |
18 | |
19 | import org.springframework.batch.retry.RetryCallback; |
20 | import org.springframework.batch.retry.RetryContext; |
21 | import org.springframework.batch.retry.context.RetryContextSupport; |
22 | import org.springframework.batch.support.BinaryExceptionClassifier; |
23 | |
24 | /** |
25 | * |
26 | * Simple retry policy that retries a fixed number of times for a set of named |
27 | * exceptions (and subclasses). The number of attempts includes the initial try, |
28 | * so e.g. |
29 | * |
30 | * <pre> |
31 | * retryTemplate = new RetryTemplate(new SimpleRetryPolicy(3)); |
32 | * retryTemplate.execute(callback); |
33 | * </pre> |
34 | * |
35 | * will execute the callback at least once, and as many as 3 times. |
36 | * |
37 | * @author Dave Syer |
38 | * @author Rob Harrop |
39 | * |
40 | */ |
41 | public class SimpleRetryPolicy extends AbstractStatelessRetryPolicy { |
42 | |
43 | /** |
44 | * The default limit to the number of attempts for a new policy. |
45 | */ |
46 | public final static int DEFAULT_MAX_ATTEMPTS = 3; |
47 | |
48 | private volatile int maxAttempts; |
49 | |
50 | private BinaryExceptionClassifier retryableClassifier = new BinaryExceptionClassifier(); |
51 | |
52 | private BinaryExceptionClassifier fatalClassifier = new BinaryExceptionClassifier(); |
53 | |
54 | /** |
55 | * Create a {@link SimpleRetryPolicy} with the default number of retry |
56 | * attempts. |
57 | */ |
58 | public SimpleRetryPolicy() { |
59 | this(DEFAULT_MAX_ATTEMPTS); |
60 | } |
61 | |
62 | /** |
63 | * Create a {@link SimpleRetryPolicy} with the specified number of retry |
64 | * attempts, and default exceptions to retry. |
65 | * |
66 | * @param maxAttempts |
67 | */ |
68 | public SimpleRetryPolicy(int maxAttempts) { |
69 | super(); |
70 | setRetryableExceptionClasses(new Class[] { Exception.class }); |
71 | setFatalExceptionClasses(new Class[] { Error.class }); |
72 | this.maxAttempts = maxAttempts; |
73 | } |
74 | |
75 | /** |
76 | * Setter for retry attempts. |
77 | * @param retryAttempts the number of attempts before a retry becomes |
78 | * impossible. |
79 | */ |
80 | public void setMaxAttempts(int retryAttempts) { |
81 | this.maxAttempts = retryAttempts; |
82 | } |
83 | |
84 | /** |
85 | * Test for retryable operation based on the status. |
86 | * @see org.springframework.batch.retry.RetryPolicy#canRetry(org.springframework.batch.retry.RetryContext) |
87 | * |
88 | * @return true if the last exception was retryable and the number of |
89 | * attempts so far is less than the limit. |
90 | */ |
91 | public boolean canRetry(RetryContext context) { |
92 | SimpleRetryContext simpleContext = ((SimpleRetryContext) context); |
93 | Throwable t = simpleContext.getLastThrowable(); |
94 | // N.B. since the contract is defined to include the initial attempt |
95 | // in the count, we have to subtract one from the max attempts in this |
96 | // test |
97 | return (t == null || retryForException(t)) && simpleContext.getRetryCount() < maxAttempts; |
98 | } |
99 | |
100 | /** |
101 | * Set the retryable exceptions. Any exception on the list, or subclasses |
102 | * thereof, will be retryable. Others will be re-thrown without retry. |
103 | * |
104 | * @param retryableExceptionClasses defaults to {@link Exception}. |
105 | */ |
106 | public final void setRetryableExceptionClasses(Class[] retryableExceptionClasses) { |
107 | retryableClassifier.setExceptionClasses(retryableExceptionClasses); |
108 | } |
109 | |
110 | /** |
111 | * Set the fatal exceptions. Any exception on the list, or subclasses |
112 | * thereof, will be re-thrown without retry. This list takes precedence over |
113 | * the retryable list. |
114 | * |
115 | * @param fatalExceptionClasses defaults to {@link Exception}. |
116 | */ |
117 | public final void setFatalExceptionClasses(Class[] fatalExceptionClasses) { |
118 | fatalClassifier.setExceptionClasses(fatalExceptionClasses); |
119 | } |
120 | |
121 | /** |
122 | * @see org.springframework.batch.retry.RetryPolicy#close(RetryContext) |
123 | */ |
124 | public void close(RetryContext status) { |
125 | } |
126 | |
127 | /** |
128 | * Update the status with another attempted retry and the latest exception. |
129 | * |
130 | * @see org.springframework.batch.retry.RetryPolicy#registerThrowable(org.springframework.batch.retry.RetryContext, |
131 | * java.lang.Throwable) |
132 | */ |
133 | public void registerThrowable(RetryContext context, Throwable throwable) { |
134 | SimpleRetryContext simpleContext = ((SimpleRetryContext) context); |
135 | simpleContext.registerThrowable(throwable); |
136 | } |
137 | |
138 | /** |
139 | * Get a status object that can be used to track the current operation |
140 | * according to this policy. Has to be aware of the latest exception and the |
141 | * number of attempts. |
142 | * @see org.springframework.batch.retry.RetryPolicy#open(org.springframework.batch.retry.RetryCallback, |
143 | * RetryContext) |
144 | */ |
145 | public RetryContext open(RetryCallback callback, RetryContext parent) { |
146 | return new SimpleRetryContext(parent); |
147 | } |
148 | |
149 | private static class SimpleRetryContext extends RetryContextSupport { |
150 | public SimpleRetryContext(RetryContext parent) { |
151 | super(parent); |
152 | } |
153 | } |
154 | |
155 | /** |
156 | * Delegates to an exception classifier. |
157 | * |
158 | * @param ex |
159 | * @return true if this exception or its ancestors have been registered as |
160 | * retryable. |
161 | */ |
162 | private boolean retryForException(Throwable ex) { |
163 | return fatalClassifier.isDefault(ex) && !retryableClassifier.isDefault(ex); |
164 | } |
165 | } |