1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.jclouds.http.handlers;
20
21 import static org.jclouds.http.HttpUtils.releasePayload;
22
23 import java.io.IOException;
24
25 import javax.annotation.Resource;
26 import javax.inject.Named;
27 import javax.inject.Singleton;
28
29 import org.jclouds.Constants;
30 import org.jclouds.http.HttpCommand;
31 import org.jclouds.http.HttpResponse;
32 import org.jclouds.http.HttpRetryHandler;
33 import org.jclouds.http.IOExceptionRetryHandler;
34 import org.jclouds.http.TransformingHttpCommand;
35 import org.jclouds.logging.Logger;
36
37 import com.google.common.base.Throwables;
38 import com.google.inject.Inject;
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80 @Singleton
81 public class BackoffLimitedRetryHandler implements HttpRetryHandler, IOExceptionRetryHandler {
82
83 public static BackoffLimitedRetryHandler INSTANCE = new BackoffLimitedRetryHandler();
84
85 @Inject(optional = true)
86 @Named(Constants.PROPERTY_MAX_RETRIES)
87 private int retryCountLimit = 5;
88
89 @Inject(optional = true)
90 @Named(Constants.PROPERTY_RETRY_DELAY_START)
91 private long delayStart = 50L;
92
93 @Resource
94 protected Logger logger = Logger.NULL;
95
96 public boolean shouldRetryRequest(HttpCommand command, IOException error) {
97 return ifReplayableBackoffAndReturnTrue(command);
98 }
99
100 public boolean shouldRetryRequest(HttpCommand command, HttpResponse response) {
101 releasePayload(response);
102 return ifReplayableBackoffAndReturnTrue(command);
103 }
104
105 private boolean ifReplayableBackoffAndReturnTrue(HttpCommand command) {
106 command.incrementFailureCount();
107
108 if (!command.isReplayable()) {
109 logger.warn("Cannot retry after server error, command is not replayable: %1$s", command);
110 return false;
111 } else if (command.getFailureCount() > retryCountLimit) {
112 logger.warn("Cannot retry after server error, command has exceeded retry limit %1$d: %2$s", retryCountLimit,
113 command);
114 return false;
115 } else {
116 imposeBackoffExponentialDelay(command.getFailureCount(), "server error: " + command.toString());
117 return true;
118 }
119 }
120
121 public void imposeBackoffExponentialDelay(int failureCount, String commandDescription) {
122 imposeBackoffExponentialDelay(delayStart, 2, failureCount, retryCountLimit, commandDescription);
123 }
124
125 public void imposeBackoffExponentialDelay(long period, int pow, int failureCount, int max, String commandDescription) {
126 imposeBackoffExponentialDelay(period, period * 10l, pow, failureCount, max, commandDescription);
127 }
128
129 public void imposeBackoffExponentialDelay(long period, long maxPeriod, int pow, int failureCount, int max,
130 String commandDescription) {
131 long delayMs = (long) (period * Math.pow(failureCount, pow));
132 delayMs = delayMs > maxPeriod ? maxPeriod : delayMs;
133 logger.debug("Retry %d/%d: delaying for %d ms: %s", failureCount, max, delayMs, commandDescription);
134 try {
135 Thread.sleep(delayMs);
136 } catch (InterruptedException e) {
137 Throwables.propagate(e);
138 }
139 }
140
141 }