How to correctly use RequestContextController
Why starting request scope programmatically
Request context in CDI is misnamed, it is thread context. Concretely it is a context enabling to define that some beans have a lifecycle bound to current thread. It matches servlet request lifecycle in synchronous case but not exactly in asynchronous case where the specification has some exception.
In the context of this post, we will focus to the unmanaged case.
Most of the time, and more and more these days, we use CDI standalone API (SeContainer
) to start an application and embed a web server, a broker, messaging client etc…
However, there are still cases you need to bind bean instances to the thread (MDC like, JPA for example where the entity manager lifecycle will be bound to the thread and not passed from thread to thread requiring a reactive JDBC like driver which is not yet mainstream nor maintained by database vendors).
For these cases, the RequestContextController
enables to start/stop the context programmatically.
RequestContextController
RequestContextController
API is quite simple, it provides an boolean activate()
and void deactivate()
methods which do exactly what their name suggest.
Concretely the spec example is this one:
@Inject
private RequestContextController requestContextController;
public void doRequest(String body) {
// activate request context
requestContextController.activate();
// do work in a request context.
// deactivate the request context
requestContextController.deactivate();
}
Quite explicit right? The issue is it hides the most important part, the enclosing bean.
The RequestContextController
is not a service as we can write in our application, it is a stateful instance storing the state (result from activate
method to simplify).
This means:
-
It is NOT thread safe,
-
You must take care of its reuse.
In other words, injecting it in a bean is generally a bad idea.
Correct RequestContextController usage
To solve it consistently and without any surprises you must ensure any usage of the controller is limited to one "wrapping". But how to get a new instance each time?
There are several options in CDI:
-
Inject a
Provider<RequestContextController>
, -
Use
BeanManager
to lookup the bean and instantiate it programmatically.
First option will just modify previous snippet as follow:
@Inject
private Provider<RequestContextController> requestContextControllerFactory;
public void doRequest(String body) {
final var requestContextController = requestContextControllerFactory.get();
requestContextController.activate();
try {
// do work in a request context.
} finally {
requestContextController.deactivate();
}
}
This new snippet is fully correct and enables to not have surprises.
Nested activate
calls will be ignored (a bit like JTA default propagation) ensuring there is always a request context active in the try
block.
Last option will look like:
@Inject
private BeanManager beanManager;
private Bean<RequestContextController> bean;
@PostConstruct
private void init() { (1)
final Set<Bean<?>> beans = beanManager.getBeans(RequestContextController.class);
bean = (Bean<RequestContextController>) beanManager.resolve(beans);
}
public void doRequest(String body) { (2)
final CreationalContext<RequestContextController> creationalContext = beanManager.createCreationalContext(null);
final var requestContextController = bean.create(creationalContext);
requestContextController.activate();
try {
// do work in a request context.
} finally {
requestContextController.deactivate();
bean.destroy(requestContextController, creationalContext);
}
}
1 | We resolve the controller bean (which is the CDI instance factory), |
2 | We instantiate the controller to get a new instance and use it as before. |
Indeed this last option looks more complicated but it will be a bit faster, avoiding the bean resolution Provider
can imply.
Automatic start with ActivateRequestContext
Last point is that @ActivateRequestContext
defines an interceptor you can use to automatically enable/disable the request context around a method.
It will inherit from the controller semantic (nested calls will not re-enable the context) and works generally more smoothly and is faster than the programmatic factory usage if it matches your usage (which is generally the case).
From the same author:
In the same category: