Replace Supplier<Boolean> with more legible ApiCall interface.

This commit is contained in:
akwizgran
2022-05-26 12:52:29 +01:00
parent f75d63fc46
commit 8ec998f645
6 changed files with 49 additions and 50 deletions

View File

@@ -1,9 +0,0 @@
package org.briarproject.bramble.api;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
public interface Supplier<T> {
T get();
}

View File

@@ -0,0 +1,21 @@
package org.briarproject.bramble.mailbox;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.mailbox.MailboxApi.TolerableFailureException;
/**
* An interface for calling an API endpoint with the option to retry the call.
*/
interface ApiCall {
/**
* This method makes a synchronous call to an API endpoint and returns
* true if the call should be retried, in which case the method may be
* called again on the same {@link ApiCall} instance after a delay.
*
* @return True if the API call needs to be retried, or false if the API
* call succeeded or {@link TolerableFailureException failed tolerably}.
*/
@IoExecutor
boolean callApi();
}

View File

@@ -1,10 +1,8 @@
package org.briarproject.bramble.mailbox; package org.briarproject.bramble.mailbox;
import org.briarproject.bramble.api.Cancellable; import org.briarproject.bramble.api.Cancellable;
import org.briarproject.bramble.api.Supplier;
import org.briarproject.bramble.api.lifecycle.IoExecutor; import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.mailbox.MailboxApi.TolerableFailureException;
import static java.util.concurrent.TimeUnit.DAYS; import static java.util.concurrent.TimeUnit.DAYS;
import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.MINUTES;
@@ -23,15 +21,12 @@ interface MailboxApiCaller {
long MAX_RETRY_INTERVAL_MS = DAYS.toMillis(1); long MAX_RETRY_INTERVAL_MS = DAYS.toMillis(1);
/** /**
* Asynchronously calls the given supplier, automatically retrying at * Asynchronously calls the given API call on the {@link IoExecutor},
* increasing intervals until the supplier returns false. The returned * automatically retrying at increasing intervals until the API call
* {@link Cancellable} can be used to cancel any future retries. * returns false or retries are cancelled.
* *
* @param supplier A wrapper for an API call. The supplier's * @return A {@link Cancellable} that can be used to cancel any future
* {@link Supplier#get() get()} method will be called on the * retries.
* {@link IoExecutor}. It should return true if the API call needs to be
* retried, or false if the API call succeeded or
* {@link TolerableFailureException failed tolerably}.
*/ */
Cancellable retryWithBackoff(Supplier<Boolean> supplier); Cancellable retryWithBackoff(ApiCall apiCall);
} }

View File

@@ -1,7 +1,6 @@
package org.briarproject.bramble.mailbox; package org.briarproject.bramble.mailbox;
import org.briarproject.bramble.api.Cancellable; import org.briarproject.bramble.api.Cancellable;
import org.briarproject.bramble.api.Supplier;
import org.briarproject.bramble.api.lifecycle.IoExecutor; import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.TaskScheduler; import org.briarproject.bramble.api.system.TaskScheduler;
@@ -31,15 +30,15 @@ class MailboxApiCallerImpl implements MailboxApiCaller {
} }
@Override @Override
public Cancellable retryWithBackoff(Supplier<Boolean> supplier) { public Cancellable retryWithBackoff(ApiCall apiCall) {
Task task = new Task(supplier); Task task = new Task(apiCall);
task.start(); task.start();
return task; return task;
} }
private class Task implements Cancellable { private class Task implements Cancellable {
private final Supplier<Boolean> supplier; private final ApiCall apiCall;
private final Object lock = new Object(); private final Object lock = new Object();
@GuardedBy("lock") @GuardedBy("lock")
@@ -52,8 +51,8 @@ class MailboxApiCallerImpl implements MailboxApiCaller {
@GuardedBy("lock") @GuardedBy("lock")
private long retryIntervalMs = MIN_RETRY_INTERVAL_MS; private long retryIntervalMs = MIN_RETRY_INTERVAL_MS;
private Task(Supplier<Boolean> supplier) { private Task(ApiCall apiCall) {
this.supplier = supplier; this.apiCall = apiCall;
} }
private void start() { private void start() {
@@ -68,8 +67,8 @@ class MailboxApiCallerImpl implements MailboxApiCaller {
synchronized (lock) { synchronized (lock) {
if (cancelled) return; if (cancelled) return;
} }
// The supplier returns true if we should retry // The call returns true if we should retry
if (supplier.get()) { if (apiCall.callApi()) {
synchronized (lock) { synchronized (lock) {
if (cancelled) return; if (cancelled) return;
scheduledTask = taskScheduler.schedule(this::callApi, scheduledTask = taskScheduler.schedule(this::callApi,

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.mailbox; package org.briarproject.bramble.mailbox;
import org.briarproject.bramble.api.Supplier;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.mailbox.MailboxApi.ApiException; import org.briarproject.bramble.mailbox.MailboxApi.ApiException;
import org.briarproject.bramble.mailbox.MailboxApi.TolerableFailureException; import org.briarproject.bramble.mailbox.MailboxApi.TolerableFailureException;
@@ -17,17 +16,17 @@ import static org.briarproject.bramble.util.LogUtils.logException;
* Convenience class for making simple API calls that don't return values. * Convenience class for making simple API calls that don't return values.
*/ */
@NotNullByDefault @NotNullByDefault
public abstract class SimpleApiCall implements Supplier<Boolean> { public abstract class SimpleApiCall implements ApiCall {
private static final Logger LOG = getLogger(SimpleApiCall.class.getName()); private static final Logger LOG = getLogger(SimpleApiCall.class.getName());
abstract void callApi() abstract void tryToCallApi()
throws IOException, ApiException, TolerableFailureException; throws IOException, ApiException, TolerableFailureException;
@Override @Override
public Boolean get() { public boolean callApi() {
try { try {
callApi(); tryToCallApi();
return false; // Succeeded, don't retry return false; // Succeeded, don't retry
} catch (IOException | ApiException e) { } catch (IOException | ApiException e) {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);

View File

@@ -1,7 +1,6 @@
package org.briarproject.bramble.mailbox; package org.briarproject.bramble.mailbox;
import org.briarproject.bramble.api.Cancellable; import org.briarproject.bramble.api.Cancellable;
import org.briarproject.bramble.api.Supplier;
import org.briarproject.bramble.api.system.TaskScheduler; import org.briarproject.bramble.api.system.TaskScheduler;
import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.CaptureArgumentAction; import org.briarproject.bramble.test.CaptureArgumentAction;
@@ -23,8 +22,7 @@ public class MailboxApiCallerImplTest extends BrambleMockTestCase {
private final TaskScheduler taskScheduler = private final TaskScheduler taskScheduler =
context.mock(TaskScheduler.class); context.mock(TaskScheduler.class);
private final Executor ioExecutor = context.mock(Executor.class); private final Executor ioExecutor = context.mock(Executor.class);
private final BooleanSupplier supplier = private final ApiCall apiCall = context.mock(ApiCall.class);
context.mock(BooleanSupplier.class);
private final Cancellable scheduledTask = context.mock(Cancellable.class); private final Cancellable scheduledTask = context.mock(Cancellable.class);
private final MailboxApiCallerImpl caller = private final MailboxApiCallerImpl caller =
@@ -39,12 +37,12 @@ public class MailboxApiCallerImplTest extends BrambleMockTestCase {
will(new CaptureArgumentAction<>(runnable, Runnable.class, 0)); will(new CaptureArgumentAction<>(runnable, Runnable.class, 0));
}}); }});
caller.retryWithBackoff(supplier); caller.retryWithBackoff(apiCall);
// When the task runs, the supplier should be called. The supplier // When the task runs, the API call should be called. The call
// returns false, so no retries should be scheduled // returns false, so no retries should be scheduled
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(supplier).get(); oneOf(apiCall).callApi();
will(returnValue(false)); will(returnValue(false));
}}); }});
@@ -60,12 +58,12 @@ public class MailboxApiCallerImplTest extends BrambleMockTestCase {
will(new CaptureArgumentAction<>(runnable, Runnable.class, 0)); will(new CaptureArgumentAction<>(runnable, Runnable.class, 0));
}}); }});
Cancellable returned = caller.retryWithBackoff(supplier); Cancellable returned = caller.retryWithBackoff(apiCall);
// When the task runs, the supplier should be called. The supplier // When the task runs, the API call should be called. The call
// returns true, so a retry should be scheduled // returns true, so a retry should be scheduled
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(supplier).get(); oneOf(apiCall).callApi();
will(returnValue(true)); will(returnValue(true));
oneOf(taskScheduler).schedule(with(any(Runnable.class)), oneOf(taskScheduler).schedule(with(any(Runnable.class)),
with(ioExecutor), with(MIN_RETRY_INTERVAL_MS), with(ioExecutor), with(MIN_RETRY_INTERVAL_MS),
@@ -90,7 +88,7 @@ public class MailboxApiCallerImplTest extends BrambleMockTestCase {
returned.cancel(); returned.cancel();
// If the scheduled task runs anyway (cancellation came too late), // If the scheduled task runs anyway (cancellation came too late),
// the supplier should not be called and no further tries should be // the API call should not be called and no further tries should be
// scheduled // scheduled
runnable.get().run(); runnable.get().run();
} }
@@ -114,13 +112,13 @@ public class MailboxApiCallerImplTest extends BrambleMockTestCase {
will(new CaptureArgumentAction<>(runnable, Runnable.class, 0)); will(new CaptureArgumentAction<>(runnable, Runnable.class, 0));
}}); }});
caller.retryWithBackoff(supplier); caller.retryWithBackoff(apiCall);
// Each time the task runs, the supplier returns true, so a retry // Each time the task runs, the API call returns true, so a retry
// should be scheduled with a longer interval // should be scheduled with a longer interval
for (long interval : expectedIntervals) { for (long interval : expectedIntervals) {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(supplier).get(); oneOf(apiCall).callApi();
will(returnValue(true)); will(returnValue(true));
oneOf(taskScheduler).schedule(with(any(Runnable.class)), oneOf(taskScheduler).schedule(with(any(Runnable.class)),
with(ioExecutor), with(interval), with(MILLISECONDS)); with(ioExecutor), with(interval), with(MILLISECONDS));
@@ -134,8 +132,4 @@ public class MailboxApiCallerImplTest extends BrambleMockTestCase {
runnable.get().run(); runnable.get().run();
} }
} }
// Reify the generic type to mollify jMock
private interface BooleanSupplier extends Supplier<Boolean> {
}
} }