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 e) {
98 setParentFuture(() -> {
100 return scheduler.schedule(this, e.delay, e.unit);
104 } else if (th != null) {
105 completeExceptionally(th);
114 * This is a convinience method for calling {@code new RetryFuture<>(callable, scheduler)}
116 * @param <T> the result type of the callable task.
117 * @param callable the task to execute
118 * @param scheduler the scheduler to use
119 * @return a CompletableFuture that will return the result of the callable.
121 public static <T> CompletableFuture<T> callWithRetry(Callable<T> callable, ScheduledExecutorService scheduler) {
122 return new RetryFuture<>(callable, scheduler, 0, TimeUnit.NANOSECONDS);
125 public static <T> CompletableFuture<T> scheduleWithRetry(Callable<T> callable, ScheduledExecutorService scheduler,
126 long delay, TimeUnit unit) {
127 return new RetryFuture<>(callable, scheduler, delay, unit);
131 public static <T> CompletableFuture<T> scheduleWithRetryForExceptions(Callable<T> callable,
132 ScheduledExecutorService scheduler, long initDelay, long retryDelay, TimeUnit unit,
133 Class<? extends Exception>... exceptions) {
134 Callable<T> task = () -> {
136 return callable.call();
137 } catch (RetryException ex) {
139 } catch (Exception ex) {
140 for (Class<? extends Exception> exClass : exceptions) {
141 if (exClass.isInstance(ex)) {
142 throw new RetryException(retryDelay, unit);
148 return new RetryFuture<>(task, scheduler, initDelay, unit);
151 public static <T> CompletableFuture<T> composeWithRetry(Supplier<CompletableFuture<T>> supplier,
152 ScheduledExecutorService scheduler) {
153 return new RetryFuture<>(supplier, scheduler, 0, TimeUnit.NANOSECONDS);
156 public static <T> CompletableFuture<T> composeWithRetry(Supplier<CompletableFuture<T>> supplier,
157 ScheduledExecutorService scheduler, long initDelay, TimeUnit unit) {
158 return new RetryFuture<>(supplier, scheduler, initDelay, unit);