There are various utility methods that you combine within the CompletableFuture , and it is really worth exploring all of them.
Let's start with the populateData method. By his name you can deduce that he must receive a data stream from somewhere.
His signature may look like this:
void populateData ( Supplier<? extends Collection<Building> dataSupplier );
Supplier , as the name implies, is simply what provides us with some data.
getDataFromDB() seems appropriate as a Supplier role.
private Set<Building> getDataFromDB()
We want populateData execute asynchronously and return the result of whether the operation completed correctly or not.
So, in the future, populateData may come back and tell us how it went.
Convert the signature to:
CompletableFuture<Result> populateData(Supplier<? extends Collection<Building>> supplier);
Now let's see what the body of the method looks like:
CompletableFuture<Result> populateData(Supplier<? extends Collection<Building>> supplier) { return CompletableFuture // create new completable future from factory method .supplyAsync(supplier) // execute the supplier method (getDataFromDB() in our case) .thenApplyAsync(data -> { // here we can work on the data supplied if (data == null || data.isEmpty()) return new Result(false); // some heavy operations for (Building building : data) { // do something } return new Result(true); // return dummy positive result data }) .handleAsync((result, throwable) -> { // check if there was any exception if (throwable != null) { // check if exception was thrown Log.log(throwable); return new Result(false); } return result; }); }
Now we can call populateData from somewhere and apply another callback to execute when it has finished executing asynchronously.
populateData(TestCallableRetry::getDataFromDB).thenAccept( result -> { if ( ! result.success ) { // things went bad... retry ?? } });
Now it depends on how you want to apply the reuse strategy. . If you just need to retry once, you can just call populateData second time in thenAcceptAsync .
You should also catch exceptions in your provider and convert them to java.util.concurrent.CompletionException , as they are handled smoothly within the CompletableFuture .