1
\$\begingroup\$

I wrote an application that has a modular structure. Each module communicates with different remote servers. Time for execution of a task in each module varies. So communication should be asynchron.

Plus some restrictions like flexibility, scalability and so on are put.

So i decided to use CompletableFutures to build a composition of functions (monade), that modules execute.

Each module has its own FixedThreadPool und a public function, that create and sends back a CompletableFuture. Something like that:

private final ExecutorService executorService = Executors.newFixedThreadPool(10);

public CompletableFuture<String> function1(final String rawReport, final UUID id) {
     LOG.info("SEND a report for " + id);
     return CompletableFuture.supplyAsync(() -> run(rawReport, id), this.executorService);
}

private String run(final String rawReport, final UUID id) {
     // do something
}

The whole process through all modules is handled by ModuleChainHandler:

 public CompletableFuture<Result> handler(final RunnerJob job) {
        // @formatter:off
        return this.scanExecuterRegistry.function1(job).
                thenCompose(bArr -> this.module2.function2(bArr, job)).
                thenCompose(metrics -> this.module3.function3(metrics, job)).
                handle((vReport, throwable) -> {
                    if (vReport != null) {    
                        this.module4.function4(vReport, job);                        
                        return builder().setResult(vReport).build();
                    } else {                            
                        return builder().setErrorMessage(throwable.getMessage()).build();
                    }
                });
        // @formatter:on
    }

The BIGGEST problem (at the beginning) was to add an error handling. Because you cant throw an exception directly out of a future. I had to create an object with two state (result and error). If a completable future completes exceptionally, will be one action executed and the result object has an error massage and an empty result.

There is no so much real examples in internet except some poor tutorials. So just interesting, if you do it in the same way.

\$\endgroup\$
4
\$\begingroup\$

One thing that i'd like to propose is to replace CompletableFuture with home-brewed builder class that wraps CompletableFuture but will have more 'easy to read' interface. It would be something like this:

class FutureBuilder {

/**
 * Wrapped 'completable future' instance
 */
CompletableFuture future;

...
public FutureBuilder execute(Function<...> task) {...}
public FutureBuilder whenComplete(Consumer<Report> handler) {...}
public FutureBuilder whenFail(BiComsumer<Report, Throwable> handler) {...}
...

/**
 * This method should be called at the end to meet interface requirements
 */
public CompletableFuture build() {
      return future;
}
...
}

So, it may be used in next way:

public CompletableFuture<Result> handler(final RunnerJob job) {
   return new FutureBuilder ()
              .execute(bArr -> this.module2.function2(bArr, job))
              .execute(metrics -> this.module3.function3(metrics, job))
              .whenComplete(r-> uilder().setResult(r).build())
              .whenFail((r, e) -> { 
                   this.module4.function4(r, job);
                   return builder().setErrorMessage(e.getMessage()).build();
              })
              .build(); 
}

I hope that is you are looking for.

\$\endgroup\$
  • \$\begingroup\$ Very interesting move! I will use it for the future. P.S. How i understand whenFail ll consume then only Consumer<Throwable>. And it will be .whenFail( e -> ...) because Report will be null in any case. \$\endgroup\$ – GreedyHat Dec 8 '16 at 12:56
  • \$\begingroup\$ I really like this idea. I like it so much I wrote a library for it. github.com/soabase/soabase-stages - what do you think? If it's useful to folks I can push it to Maven Central. I think it captures the spirit of your FutureBuilder. \$\endgroup\$ – Randgalt Jun 12 '17 at 18:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.