Enhanced CDI Contexts and Bulkheads With MicroProfile Context Propagation
source link: https://www.tuicool.com/articles/fymuimY
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Enhanced CDI Contexts and Bulkheads With MicroProfile Context Propagation
DZone 's Guide to
Enhanced CDI Contexts and Bulkheads With MicroProfile Context Propagation
Learn more about how MicroProfile Context Propagation can provide enhanced CDI contexts and bulkheads.
Free Resource
Join the DZone community and get the full member experience.
When using CDI with asynchronous execution methods, such as a ManagedExecutorService
, it's traditionally not possible to access all of CDI's scopes that were active in the originating thread. MicroProfile Context Propagation enables us to define and pass thread execution contexts to completion stages where our code can access various CDI contexts despite being executed asynchronously. Additionally, Context Propagation allows us to create managed executor services that can be injected and used inside our beans, for example, to realize bulkheads.
Let's create and use a request-scoped bean that is being used during the handling of a request. With plain CDI, we would not be able to access and lookup the bean within an asynchronous execution.
Have a look at the following code:
@ApplicationScoped @Path("contexts/example") public class ThreadContextExampleResource { @Inject ExampleStore exampleStore; @Inject ThreadContext threadContext; @Resource ManagedExecutorService mes; @Inject Notifier notifier; @PUT public void setExample(String example) { exampleStore.setExample(example); mes.execute(threadContext.contextualRunnable(notifier::notifyAbout)); } }
@RequestScoped public class ExampleStore { private String example; public String getExample() { return example; } public void setExample(String example) { this.example = example; } }
public class Notifier { @Inject ExampleStore exampleStore; public void notifyAbout() { System.out.println("New example: " + exampleStore.getExample()); } }
If a client PUT
s some content to the contexts/example
resource, the method will update the request-scoped ExampleStore
bean and execute the notification asynchronously, using the ManagedExecutorService
. In order to enable the asynchronous execution to lookup the request-scoped store, we are using the ThreadContext
to wrap the runnable with a context captured from the originating thread. This makes sure that the executed runnable can use the corresponding context.
We have to configure and produce a ThreadContext
depending on which type of contexts (e.g. CDI , transaction , security ) we want to propagate:
public class ThreadContextProducer { @Produces ThreadContext threadContext() { return ThreadContext.builder() .propagated(ThreadContext.ALL_REMAINING) .build(); } }
This example will propagate all context types to the wrapped execution. Our bean then injects and uses the produced ThreadContext
.
Defining Bulkheads Using Executors
MicroProfile Context Propagation allows us to create and configure ManagedExecutor
s, a container-managed executor service similar to ManagedExecutorService
. We can create a ManagedExecutor
programmatically, set constraints on the allowed concurrency, and define a context propagation, as well.
Let's define the following asynchronous JAX-RS resources:
@ApplicationScoped @Path("bulkheads") public class BulkheadExampleResource { @Inject ExampleStore exampleStore; @Inject Notifier notifier; @Inject ManagedExecutor writeExecutor; @Inject ManagedExecutor readExecutor; @GET public CompletionStage<String> example() { return readExecutor.supplyAsync(exampleStore::getExample); } @PUT public CompletionStage<Void> setExample(String example) { return writeExecutor.runAsync(() -> { exampleStore.setExample(example); writeExecutor.execute(notifier::notifyAbout); }); } }
We're injecting two dedicated executors that are used to run the corresponding functionalities. The executors are created using a producer:
public class ManagedExecutorProducer { @Produces ManagedExecutor managedExecutor() { return ManagedExecutor.builder() .propagated(ThreadContext.CDI, ThreadContext.APPLICATION) .maxAsync(4) .maxQueued(4) .build(); } public void disposeManagedExecutor(@Disposes ManagedExecutor managedExecutor) { managedExecutor.shutdownNow(); } }
Our executors will have upper bounds of four concurrently executed completion stages, and four tasks in the queue. The contexts of the CDI and application context types will be propagated to the executing threads.
When injecting the executors, be aware of the scope of the injection point; here, we're using an application-scoped resource. Otherwise, we might create more than two executors, which would defeat the purpose of the bulkhead pattern. Since we're using CDI, it's, of course, possible to define additional qualifies if the created executors should be configured differently.
You can try out MicroProfile Context Propagation, for example, using the latest builds of Open Liberty. I've published an example repository on GitHub .
When we're running our applications on Open Liberty, MicroProfile Context Propagation executors are backed by the automatically-tuned global thread pool. You can have a look at the default thread pool metrics provided by Liberty, as shown here .
DOWNLOAD
Topics:
java , microprofile , microprofile context propagation , cdi , application , cdi contexts , bulkheads , code
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK