1 /*
2  * Copyright 2010-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
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  * A copy of the License is located at
7  *
8  *  http://aws.amazon.com/apache2.0
9  *
10  * or in the "license" file accompanying this file. This file is distributed
11  * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12  * express or implied. See the License for the specific language governing
13  * permissions and limitations under the License.
14  */

15 package com.amazonaws.retry;
16
17 import com.amazonaws.AmazonClientException;
18 import com.amazonaws.AmazonWebServiceRequest;
19 import com.amazonaws.ClientConfiguration;
20 import com.amazonaws.annotation.Immutable;
21 import com.amazonaws.annotation.SdkInternalApi;
22 import com.amazonaws.retry.internal.RetryModeResolver;
23
24 /**
25  * Retry policy that can be configured on a specific service client using
26  * {@link ClientConfiguration}. This class is immutable, therefore safe to be
27  * shared by multiple clients.
28  * 
29  * @see ClientConfiguration
30  * @see PredefinedRetryPolicies
31  */

32 @Immutable
33 public final class RetryPolicy {
34
35     private static final RetryModeResolver RETRY_MODE_RESOLVER = new RetryModeResolver();
36     /**
37      * Condition on whether a request should be retried. This field
38      * should not be null.
39      */

40     private final RetryCondition retryCondition;
41     
42     /**
43      * Back-off strategy to control the sleep time between retry attempts. This
44      * field should not be null.
45      */

46     private final BackoffStrategy backoffStrategy;
47     
48     /**
49      * Non-negative integer indicating the max retry count.
50      */

51     private final int maxErrorRetry;
52     
53     /**
54      * Whether this retry policy should honor the max error retry set in ClientConfiguration.
55      * @see ClientConfiguration#setMaxErrorRetry(int)
56      */

57     private final boolean honorMaxErrorRetryInClientConfig;
58
59     /**
60      * The retry mode to use
61      */

62     private final RetryMode retryMode;
63
64     /**
65      * Whether it should honor the default max error retry in {@link RetryMode}
66      */

67     private final boolean honorDefaultMaxErrorRetryInRetryMode;
68
69     /**
70      * Constructs a new retry policy. See {@link PredefinedRetryPolicies} for
71      * some pre-defined policy components, and also the default policies used by
72      * SDK.
73      * 
74      * @param retryCondition
75      *            Retry condition on whether a specific request and exception
76      *            should be retried. If null value is specified, the SDK'
77      *            default retry condition is used.
78      * @param backoffStrategy
79      *            Back-off strategy for controlling how long the next retry
80      *            should wait. If null value is specified, the SDK' default
81      *            exponential back-off strategy is used.
82      * @param maxErrorRetry
83      *            Maximum number of retry attempts for failed requests.
84      * @param honorMaxErrorRetryInClientConfig
85      *            Whether this retry policy should honor the max error retry set
86      *            by {@link ClientConfiguration#setMaxErrorRetry(int)}
87      * @see ClientConfiguration
88      * @see PredefinedRetryPolicies
89      */

90     public RetryPolicy(RetryCondition retryCondition,
91                        BackoffStrategy backoffStrategy,
92                        int maxErrorRetry,
93                        boolean honorMaxErrorRetryInClientConfig) {
94         this(retryCondition, backoffStrategy, maxErrorRetry, honorMaxErrorRetryInClientConfig, false);
95     }
96
97     @SdkInternalApi
98     public RetryPolicy(RetryCondition retryCondition,
99                        BackoffStrategy backoffStrategy,
100                        int maxErrorRetry,
101                        boolean honorMaxErrorRetryInClientConfig,
102                        boolean honorDefaultMaxErrorRetryInRetryMode) {
103         this(retryCondition, backoffStrategy, maxErrorRetry, honorMaxErrorRetryInClientConfig, null, honorDefaultMaxErrorRetryInRetryMode);
104     }
105
106     public RetryPolicy(RetryCondition retryCondition,
107                        BackoffStrategy backoffStrategy,
108                        int maxErrorRetry,
109                        boolean honorMaxErrorRetryInClientConfig,
110                        RetryMode retryMode) {
111         this(retryCondition, backoffStrategy, maxErrorRetry, honorMaxErrorRetryInClientConfig, retryMode, false);
112     }
113
114     @SdkInternalApi
115     RetryPolicy(RetryCondition retryCondition,
116                 BackoffStrategy backoffStrategy,
117                 int maxErrorRetry,
118                 boolean honorMaxErrorRetryInClientConfig,
119                 RetryMode retryMode,
120                 boolean honorDefaultMaxErrorRetryInRetryMode) {
121         if (retryCondition == null) {
122             retryCondition = PredefinedRetryPolicies.DEFAULT_RETRY_CONDITION;
123         }
124         if (backoffStrategy == null) {
125             backoffStrategy = PredefinedRetryPolicies.DEFAULT_BACKOFF_STRATEGY;
126         }
127         if (maxErrorRetry < 0) {
128             throw new IllegalArgumentException("Please provide a non-negative value for maxErrorRetry.");
129         }
130
131         this.honorDefaultMaxErrorRetryInRetryMode = honorDefaultMaxErrorRetryInRetryMode;
132         this.retryCondition = retryCondition;
133         this.backoffStrategy = backoffStrategy;
134         this.maxErrorRetry = maxErrorRetry;
135         this.honorMaxErrorRetryInClientConfig = honorMaxErrorRetryInClientConfig;
136         this.retryMode = retryMode != null ? retryMode : RETRY_MODE_RESOLVER.retryMode();
137     }
138
139     /**
140      * Returns the retry condition included in this retry policy.
141      * 
142      * @return The retry condition included in this retry policy.
143      */

144     public RetryCondition getRetryCondition() {
145         return retryCondition;
146     }
147
148     /**
149      * Returns the back-off strategy included in this retry policy.
150      * 
151      * @return The back-off strategy included in this retry policy.
152      */

153     public BackoffStrategy getBackoffStrategy() {
154         return backoffStrategy;
155     }
156
157     /**
158      * Returns the maximum number of retry attempts.
159      * 
160      * @return The maximum number of retry attempts.
161      */

162     public int getMaxErrorRetry() {
163         return maxErrorRetry;
164     }
165     
166     /**
167      * Returns whether this retry policy should honor the max error retry set in
168      * ClientConfiguration.
169      * 
170      * @return Whether this retry policy should honor the max error retry set in
171      *         ClientConfiguration
172      * @see ClientConfiguration#setMaxErrorRetry(int)
173      */

174     public boolean isMaxErrorRetryInClientConfigHonored() {
175         return honorMaxErrorRetryInClientConfig;
176     }
177
178     /**
179      * Returns the {@link RetryMode} to be used.
180      *
181      * @return retryMode
182      */

183     public RetryMode getRetryMode() {
184         return retryMode;
185     }
186
187     /**
188      * @return Whether the default max error in retry mode should be honored.
189      */

190     boolean isDefaultMaxErrorRetryInRetryModeHonored() {
191         return honorDefaultMaxErrorRetryInRetryMode;
192     }
193     
194     /**
195      * The hook for providing custom condition on whether a failed request
196      * should be retried.
197      */

198     public interface RetryCondition {
199         RetryCondition NO_RETRY_CONDITION = new RetryCondition() {
200             @Override
201             public boolean shouldRetry(AmazonWebServiceRequest originalRequest,
202                                        AmazonClientException exception,
203                                        int retriesAttempted) {
204                 return false;
205             }
206         };
207
208         /**
209          * Returns whether a failed request should be retried according to the
210          * given request context. In the following circumstances, the request
211          * will fail directly without consulting this method:
212          * <ul>
213          *   <li> if it has already reached the max retry limit,
214          *   <li> if the request contains non-repeatable content,
215          *   <li> if any RuntimeException or Error is thrown when executing the request.
216          * </ul>
217          * 
218          * @param originalRequest
219          *            The original request object being executed. For
220          *            performance reason, this object is not a defensive copy,
221          *            and caller should not attempt to modify its data.
222          * @param exception
223          *            The exception from the failed request, represented as an
224          *            AmazonClientException object. There are two types of
225          *            exception that will be passed to this method:
226          *            <ul>
227          *            <li>AmazonServiceException (sub-class of
228          *            AmazonClientException) indicating a service error
229          *            <li>AmazonClientException caused by an IOException when
230          *            executing the HTTP request.
231          *            </ul>
232          *            Any other exceptions are regarded as unexpected failures
233          *            and are thrown immediately without any retry. For
234          *            performance reason, this object is not a defensive copy,
235          *            and caller should not attempt to modify its data.
236          * @param retriesAttempted
237          *            The number of times the current request has been
238          *            attempted.
239          * 
240          * @return True if the failed request should be retried.
241          */

242         boolean shouldRetry(AmazonWebServiceRequest originalRequest,
243                                    AmazonClientException exception,
244                                    int retriesAttempted);
245         
246     }
247
248     /**
249      * The hook for providing custom back-off strategy to control the sleep time
250      * between retries.
251      */

252     public interface BackoffStrategy {
253         RetryPolicy.BackoffStrategy NO_DELAY = new BackoffStrategy() {
254             @Override
255             public long delayBeforeNextRetry(AmazonWebServiceRequest originalRequest,
256                                              AmazonClientException exception,
257                                              int retriesAttempted) {
258                 return 0;
259             }
260         };
261
262         /**
263          * Returns the delay (in milliseconds) before next retry attempt.
264          * 
265          * @param originalRequest
266          *            The original request object being executed. For
267          *            performance reason, this object is not a defensive copy,
268          *            and caller should not attempt to modify its data.
269          * @param exception
270          *            The exception from the failed request, represented as an
271          *            AmazonClientException object. There are two types of
272          *            exception that will be passed to this method:
273          *            <ul>
274          *              <li>AmazonServiceException (sub-class of
275          *                  AmazonClientException) indicating a service error
276          *              <li>AmazonClientException caused by an IOException when
277          *                  executing the HTTP request.
278          *            </ul>
279          *            Any other exceptions are regarded as unexpected failures
280          *            and are thrown immediately without any retry. For
281          *            performance reason, this object is not a defensive copy,
282          *            and caller should not attempt to modify its data.
283          * @param retriesAttempted
284          *            The number of times the current request has been attempted
285          *            (not including the next attempt after the delay).
286          * 
287          * @return The delay (in milliseconds) before next retry attempt.
288          */

289         long delayBeforeNextRetry(AmazonWebServiceRequest originalRequest,
290                                          AmazonClientException exception,
291                                          int retriesAttempted);
292     }
293 }
294