View Javadoc

1   /*
2      Copyright 2010 Atlassian
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  package io.atlassian.fugue.retry;
17  
18  import java.util.function.Supplier;
19  
20  import static java.util.Objects.requireNonNull;
21  
22  /**
23   * A Supplier which wraps the apply method of another Supplier and attempts it
24   * up to a fixed number of times. This class can be used when a task is known to
25   * be prone to occasional failure and other workarounds are not known.
26   *
27   * @param <T> The type of the result the Supplier yields upon application
28   * @see RetryFunction for a Function implementation
29   * @see RetryFactory for some factory methods
30   * @see ExceptionHandlers for some predefined handlers
31   */
32  public class RetrySupplier<T> implements Supplier<T> {
33    private final Supplier<T> supplier;
34    private final int tries;
35    private final ExceptionHandler handler;
36    private final Runnable beforeRetry;
37  
38    /**
39     * An instance that does nothing before retrying and ignores exceptions that
40     * occur.
41     *
42     * @param supplier which fetches the result, must not be null
43     * @param tries the number of times to attempt to get a result, must be
44     * positive
45     */
46    public RetrySupplier(Supplier<T> supplier, int tries) {
47      this(supplier, tries, ExceptionHandlers.ignoreExceptionHandler());
48    }
49  
50    /**
51     * An instance that does nothing before retrying.
52     *
53     * @param supplier which fetches the result, must not be null
54     * @param tries the number of times to attempt to get a result, must be
55     * positive
56     * @param handler reacts to exceptions thrown by the supplier, must not be
57     * null
58     */
59    public RetrySupplier(Supplier<T> supplier, int tries, ExceptionHandler handler) {
60      this(supplier, tries, handler, new NoOp());
61    }
62  
63    /**
64     * <p>
65     * Constructor for RetrySupplier.
66     * </p>
67     *
68     * @param supplier which fetches the result, must not be null
69     * @param tries the number of times to attempt to get a result, must be
70     * positive
71     * @param handler reacts to exceptions thrown by the supplier, must not be
72     * null
73     * @param beforeRetry a task which will run at the end of any
74     */
75    public RetrySupplier(Supplier<T> supplier, int tries, ExceptionHandler handler, Runnable beforeRetry) {
76      requireNonNull(supplier);
77      if (tries <= 0) {
78        throw new IllegalArgumentException("Tries must be strictly positive");
79      }
80      requireNonNull(handler);
81  
82      this.beforeRetry = beforeRetry;
83      this.supplier = supplier;
84      this.tries = tries;
85      this.handler = handler;
86    }
87  
88    /**
89     * {@inheritDoc}
90     *
91     * Attempt to get a result <i>tries</i> number of times. Any exceptions thrown
92     * will be ignored until the number of attempts is reached. If the number of
93     * attempts is reached without a successful result, the most recent exception
94     * to be thrown will be rethrown.
95     */
96    @Override public T get() {
97      RuntimeException ex = null;
98      for (int i = 0; i < tries; i++) {
99        try {
100         return supplier.get();
101       } catch (RuntimeException e) {
102         handler.handle(e);
103         ex = e;
104       }
105 
106       if (i + 1 < tries) {
107         beforeRetry.run();
108       }
109     }
110     throw ex;
111   }
112 }