Introduction

Patiently is a java library that retries tasks on a given schedule until they are successful (they return successfully without throwing an Exception), or until the schedule finished (a time limit has been reached).

It’s predominant use-case, for me, is to simplify testing some threaded tasks and avoid having to mock things I don’t want to. Beyond testing, Patiently can be used as a general polling/retry mechanism.

It’s available on GitHub: https://github.com/maankoe/patiently.

Disclaimer

In no way do I endorse this approach to testing. It is generally considered bad practice to use Thread.sleep(), you’re probably better off mocking the thing you’re testing. Here is Sonarlint’s opinion on it., which also suggests awaitatility as a “compliant solution”. Awaitatility is clearly more mature than Patiently. From what I can see, Patience is also a strong alternative.

Having said that, I enjoyed making it and I’m using it in FluxTail, should you be interested.

Usage

In general, you can do the following for a task that is either a Runnable, Callable<T>, or Supplier<Boolean>:

Patiently.retry(task)
	.every(everyMs)
	.until(untilMs);

where long everyMs specifies the interval between retries (how often the task is executed) and long untilMs specifies the when the task will stop being retried. After untilMs, the task will be executed one final time and the result returned (or Exception thrown).

Alternatively, there is an all-in-one:

Patiently.retry(task, everyMs, untilMs);

Additionally, retry can be imported statically.

Lambdas

Of course, a Runnable, Callable or Supplier can be provided by a lambda in the usual way. The main use-case for patiently (for me) is in retrying an assertion. For example, testing that a slow threaded task completes properly:

Task slowTask = Task();
Executors.newSingleThreadExecutor().execute(slowTask);
Patently.retry(() -> assertThat(slowTask.isComplete()).isTrue())
	.every(100)
	.until(1000);

Future

Some interesting extensions to Patiently would be:

  • Generalise and provide a variety of RetrySchedules.
  • Extend builder DSL to accept different time units other than milliseconds.
  • Provide wider set of PatientX classes (e.g., PatientFunction etc.).

Though unless there is a need (internal or external), it’s unlikely I’ll be doing these in the near future.