weld / weld-vertx

Apache License 2.0
47 stars 16 forks source link

Asynchronously processed wrapper of an injectable reference #31

Closed mkouba closed 7 years ago

mkouba commented 7 years ago

This PR introduces two new constructs:

AsyncReference

CDI bean creation is synchronous. As a result if you inject a bean whose initialization involves potentially blocking operations (e.g. fetch some data from DB) there creation process is blocked until the dependency is ready. AsyncReference is an asynchronously processed wrapper (actually also implements CompletionStage) of an injectable reference which allows to finish the client bean creation before the dependency is ready:

@ApplicationScoped
 class Hello {

     @Inject
     AsyncReference<ServiceWithBlockingInit> serviceRef;

     CompletionStage<String> hello() {
         // This method returns immediately
         return serviceRef.thenApply(service -> "Hello" + service.getName() + "!");
     }
 }

If there is a producer method whose return type is CompletionStage where the result type matches the required type (according to type-safe resolution rules) then produced CompletionStage#whenComplete(java.util.function.BiConsumer) is used to process the reference. Otherwise, a Vertx worker thread is used so that the processing does not block the event loop thread.

AsyncWorker

AsyncWorker allows to wrap a synchronous action as an asynchronous computation. The action is performed either as blocking operation using a Vertx worker thread or as non-blocking operation using the Vertx event-loop thread:

@Dependent
public class Hello {

    @Inject
    AsyncWorker worker;

    @Inject
    Service service;

    CompletionStage<String> hello() {
        return worker.performBlocking(service::getMessageFromDb).thenApply(m -> "Hello " + m + "!");
    }
}

It's also possible to combine these two constructs:

public class HelloCombo {

    @Inject
    AsyncWorker worker;

    @Inject
    AsyncReference<Service> serviceRef;

    CompletionStage<String> hello() {
        return serviceRef.thenCompose(service ->
        // At this point Service is ready
        // But getMessage() is also blocking
        worker.performBlocking(service::getMessage)
                // Finally modify the final message
                .thenApply(m -> "Hello " + m + "!"));
    }

}