1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.spf4j.failsafe;
17
18 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
19 import java.util.Arrays;
20 import java.util.concurrent.Callable;
21 import java.util.function.Function;
22 import java.util.function.Supplier;
23 import javax.annotation.Nonnull;
24 import javax.annotation.Nullable;
25 import javax.annotation.concurrent.ThreadSafe;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
28 import org.spf4j.log.Level;
29 import org.spf4j.log.SLf4jXLogAdapter;
30 import org.spf4j.log.XLog;
31
32
33
34
35 @SuppressFBWarnings("AI_ANNOTATION_ISSUES_NEEDS_NULLABLE")
36 @ThreadSafe
37 final class DefaultRetryPredicate<T> implements RetryPredicate<T, Callable<T>> {
38
39 private static final Level RETRY_LOG_LEVEL =
40 Level.valueOf(System.getProperty("spf4j.failsafe.retryLogLevel", "WARN"));
41
42 private static final PartialResultRetryPredicate[] NO_RP = new PartialResultRetryPredicate[0];
43
44 private static final PartialExceptionRetryPredicate[] NO_EP = new PartialExceptionRetryPredicate[0];
45
46 private static final Logger LOG = LoggerFactory.getLogger(DefaultRetryPredicate.class);
47
48 private final Function<Object, RetryDelaySupplier> defaultBackoffSupplier;
49
50 private final PartialResultRetryPredicate<T, Callable<T>>[] resultPredicates;
51
52 private final PartialExceptionRetryPredicate<T, Callable<T>>[] exceptionPredicates;
53
54 private final XLog log;
55
56 @SuppressFBWarnings("LO_SUSPECT_LOG_PARAMETER")
57 DefaultRetryPredicate(@Nullable final Logger log, final long startNanos, final long deadlineNanos,
58 final Supplier<Function<Object, RetryDelaySupplier>> defaultBackoffSupplierSupplier,
59 final TimedSupplier<PartialResultRetryPredicate<T, Callable<T>>>[] resultPredicates,
60 final TimedSupplier<PartialExceptionRetryPredicate<T, Callable<T>>>... exceptionPredicates) {
61 this.log = new SLf4jXLogAdapter(log == null ? LOG : log);
62 this.defaultBackoffSupplier = defaultBackoffSupplierSupplier.get();
63 int rpl = resultPredicates.length;
64 if (rpl > 0) {
65 this.resultPredicates = new PartialResultRetryPredicate[rpl];
66 for (int i = 0; i < rpl; i++) {
67 this.resultPredicates[i] = resultPredicates[i].get(startNanos, deadlineNanos);
68 }
69 } else {
70 this.resultPredicates = NO_RP;
71 }
72 int epl = exceptionPredicates.length;
73 if (epl > 0) {
74 this.exceptionPredicates = new PartialExceptionRetryPredicate[epl];
75 for (int i = 0; i < epl; i++) {
76 this.exceptionPredicates[i] = exceptionPredicates[i].get(startNanos, deadlineNanos);
77 }
78 } else {
79 this.exceptionPredicates = NO_EP;
80 }
81 }
82
83 @Override
84 @Nonnull
85 public RetryDecision<T, Callable<T>> getDecision(final T value, final Callable<T> what) {
86
87 for (PartialResultRetryPredicate<T, Callable<T>> predicate : resultPredicates) {
88 RetryDecision<T, Callable<T>> decision = predicate.getDecision(value, what);
89 if (decision != null) {
90 if (decision.getDecisionType() == RetryDecision.Type.Retry) {
91 Callable<?> newCallable = decision.getNewCallable();
92 if (decision.getDelayNanos() < 0) {
93 RetryDelaySupplier backoff = defaultBackoffSupplier.apply(value);
94 decision = (RetryDecision) RetryDecision.retry(backoff.nextDelay(), newCallable);
95 }
96 log.log(null, RETRY_LOG_LEVEL, "Result {}, retrying {} with {}", value, newCallable, decision);
97 }
98 return decision;
99 }
100 }
101 return RetryDecision.abortReturn(value);
102 }
103
104 @Override
105 @Nonnull
106 public RetryDecision<T, Callable<T>> getExceptionDecision(final Throwable value, final Callable<T> what) {
107 for (PartialExceptionRetryPredicate<T, Callable<T>> predicate : exceptionPredicates) {
108 RetryDecision<T, Callable<T>> decision = predicate.getExceptionDecision(value, what);
109 if (decision != null) {
110 if (decision.getDecisionType() == RetryDecision.Type.Retry) {
111 Callable<?> newCallable = decision.getNewCallable();
112 if (decision.getDelayNanos() < 0) {
113 RetryDelaySupplier backoff = defaultBackoffSupplier.apply(value);
114 decision = (RetryDecision) RetryDecision.retry(backoff.nextDelay(), newCallable);
115 }
116 log.log(null, RETRY_LOG_LEVEL,
117 "Result {}, retrying {} with {}", value.getClass().getName(), newCallable, decision, value);
118 }
119 return decision;
120 }
121 }
122 return RetryDecision.abortThrow(value);
123
124 }
125
126 @Override
127 public String toString() {
128 return "DefaultRetryPredicate{" + "defaultBackoffSupplier=" + defaultBackoffSupplier
129 + ", resultPredicates=" + Arrays.toString(resultPredicates)
130 + ", exceptionPredicates=" + Arrays.toString(exceptionPredicates) + '}';
131 }
132
133 }