2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.bluetooth.util;
15 import java.util.concurrent.Callable;
16 import java.util.concurrent.CompletableFuture;
17 import java.util.concurrent.CompletionException;
18 import java.util.concurrent.Executor;
19 import java.util.concurrent.ScheduledExecutorService;
20 import java.util.concurrent.TimeUnit;
21 import java.util.function.Supplier;
23 import org.eclipse.jdt.annotation.NonNullByDefault;
26 * This is a utility class that allows adding {@link CompletableFuture} capabilities to a {@link Callable}.
27 * The provided callable will be executed asynchronously and the result will be used
28 * to complete the {@code RetryFuture} instance. As per its namesake, the RetryFuture allows
29 * the callable to reschedule itself by throwing a {@link RetryException}. Any other exception
30 * will simply complete the RetryFuture exceptionally as per {@link CompletableFuture#completeExceptionally(Throwable)}.
32 * @author Connor Petty - Initial contribution
36 public class RetryFuture<T> extends HeritableFuture<T> {
38 private final ScheduledExecutorService scheduler;
40 public RetryFuture(Callable<T> callable, ScheduledExecutorService scheduler, long delay, TimeUnit unit) {
41 this.scheduler = scheduler;
42 setParentFuture(() -> scheduler.schedule(new CallableTask(callable), delay, unit));
45 public RetryFuture(Supplier<CompletableFuture<T>> supplier, ScheduledExecutorService scheduler, long delay,
47 this.scheduler = scheduler;
48 setParentFuture(() -> scheduler.schedule(new ComposeTask(supplier), delay, unit));
52 public Executor defaultExecutor() {
56 private class CallableTask implements Runnable {
58 private final Callable<T> callable;
60 public CallableTask(Callable<T> callable) {
61 this.callable = callable;
67 complete(callable.call());
68 } catch (RetryException e) {
69 setParentFuture(() -> {
71 return scheduler.schedule(this, e.delay, e.unit);
75 } catch (Exception e) {
76 completeExceptionally(e);
81 private class ComposeTask implements Runnable {
83 private final Supplier<CompletableFuture<T>> supplier;
85 public ComposeTask(Supplier<CompletableFuture<T>> supplier) {
86 this.supplier = supplier;
91 CompletableFuture<T> future = supplier.get();
92 setParentFuture(() -> future);
93 future.whenComplete((result, th) -> {
94 if (th instanceof CompletionException) {
97 if (th instanceof RetryException) {
98 RetryException e = (RetryException) th;
99 setParentFuture(() -> {
101 return scheduler.schedule(this, e.delay, e.unit);
105 } else if (th != null) {
106 completeExceptionally(th);
115 * This is a convinience method for calling {@code new RetryFuture<>(callable, scheduler)}
117 * @param <T> the result type of the callable task.
118 * @param callable the task to execute
119 * @param scheduler the scheduler to use
120 * @return a CompletableFuture that will return the result of the callable.
122 public static <T> CompletableFuture<T> callWithRetry(Callable<T> callable, ScheduledExecutorService scheduler) {
123 return new RetryFuture<>(callable, scheduler, 0, TimeUnit.NANOSECONDS);
126 public static <T> CompletableFuture<T> scheduleWithRetry(Callable<T> callable, ScheduledExecutorService scheduler,
127 long delay, TimeUnit unit) {
128 return new RetryFuture<>(callable, scheduler, delay, unit);
132 public static <T> CompletableFuture<T> scheduleWithRetryForExceptions(Callable<T> callable,
133 ScheduledExecutorService scheduler, long initDelay, long retryDelay, TimeUnit unit,
134 Class<? extends Exception>... exceptions) {
135 Callable<T> task = () -> {
137 return callable.call();
138 } catch (RetryException ex) {
140 } catch (Exception ex) {
141 for (Class<? extends Exception> exClass : exceptions) {
142 if (exClass.isInstance(ex)) {
143 throw new RetryException(retryDelay, unit);
149 return new RetryFuture<>(task, scheduler, initDelay, unit);
152 public static <T> CompletableFuture<T> composeWithRetry(Supplier<CompletableFuture<T>> supplier,
153 ScheduledExecutorService scheduler) {
154 return new RetryFuture<>(supplier, scheduler, 0, TimeUnit.NANOSECONDS);
157 public static <T> CompletableFuture<T> composeWithRetry(Supplier<CompletableFuture<T>> supplier,
158 ScheduledExecutorService scheduler, long initDelay, TimeUnit unit) {
159 return new RetryFuture<>(supplier, scheduler, initDelay, unit);