What is a CreationalContext
in CDI?
A perfect example of how naming things is the hardest problem in software engineering, really. The only method that it exposes that anyone should really be concerned with, release()
, is used at destruction time, and has nothing to do with creating anything.
Here’s how I would describe it:
A CreationalContext
is a bean helper that automatically stores @Dependent
-scoped contextual instances on behalf of some other bean that has references to them, and ensures that they are cleaned up when that bean goes out of scope.
That’s basically it.
Consider a bean, B
, that has a reference to a @Dependent
-scoped bean, D
. In Java terms, it might have a field like this:
@Inject
private D d;
Now, if the Bean
implementation that created this B
contextual instance has its destroy(T, CreationalContext<T>)
method called, then B
will be destroyed. B
, in other words, will be the first argument.
When B
is destroyed, you want to make sure that its dependent objects are also destroyed. But you also don’t want to burden the programmer with this. That is, you don’t want to make the programmer have to jump through some hoops inside B
‘s logic somewhere to say “oh, when I’m destroyed, make sure to arrange for destruction to happen on my d
field”. That should just happen automatically.
To allow this to happen automatically, CDI locates this logic inside a CreationalContext
(yes, a “creational” context, even though we’re doing destruction. Naming is hard.). At the moment a CreationalContext
is released, it has “in” it:
- A bean that it is helping (usually)
- A set of dependent instances that belong to the bean that it is helping
- For each of those dependent instances some way to tie it to the
Contextual
(theBean
) that created it
When release()
is called, the CreationalContext
iterates over that set of dependent instances and their associated Bean
s and calls destroy()
on each of those Bean
s.
So the programmer didn’t have to do any of this work. She just @Inject
ed a D
into a field and even if D
is some sort of custom object it gets destroyed properly.
(It all follows from this that release()
implementations must be idempotent and pretty much should never be called except from within a destroy()
method of a custom bean. They also must work on only the bean that is being helped, and not on every dependent object “known” to the CreationalContext
.)
OK, that’s all fine, but how did all these objects get “into” the CreationalContext
in the first place?
When B
was created, via a Bean
‘s create(CreationalContext<T>)
method, the container supplied that method with a new, empty CreationalContext
that is associated with the Bean
doing the creating. That is, prior to the create
call, the container called beanManager.createCreationalContext(beanThatIsCreatingBInstances)
, and the resulting CreationalContext
is supplied to beanThatIsCreatingBInstances
‘s create
method as its sole argument.
What does a custom Bean
author here need to do with this CreationalContext
as she implements the create
method? The answer is: ignore it completely. That’s easy.
(More to the point: push(Object)
does not, as you might be tempted to believe, stuff a @Dependent
-scoped object into the CreationalContext
such that release()
will have any effect on it. The two methods are completely orthogonal. Around this point you should start getting suspicious: how does a dependent object get “into” an arbitrary CreationalContext
anyway? An excellent question.)
In the case of managed beans—ordinary CDI beans, with @Inject
annotations and whatnot processed by the container without any special funny business—remember that the container will take care of satisfying the injection points. So in the case of B
with a D
-typed d
field injection point, the container will arrange for a D
-type-producing bean to be invoked and then will automatically arrange for that dependent object to be stuffed into the CreationalContext.
That’s a lot to take in. Let’s try to break it down.
Recall that the container created a brand new CreationalContext
to serve as the bean helper for B
when it is about to call B
‘s create
method.
In order to “make” a B
, the container is going to have to satisfy its D
-typed injection point (the d
field in our example).
To satisfy the D
-typed injection point, the container will need to find out what scope is in effect. It will discover that the scope is @Dependent
(since our example says so; presumably D
is annotated with @Dependent
which allows the container to call BeanManager#getContext(Class<? extends Annotation>)
).
With the right Context
in hand, the container will ask it for an appropriate D
instance. The method that the container uses here is Context#get(Contextual<T>, CreationalContext<T>)
. Here, the container does not create a new CreationalContext
. It passes the CreationalContext
it has made for creating the B
instance. That’s important.
A Context
is responsible for doing basically whatever it wants to return an instance, so long as if a new instance is required it results (ultimately) from the return value of Contextual#create(CreationalContext<T>)
.
The @Dependent
-scoped Context
is obliged to create a new instance with every request, so it will dutifully invoke Contextual#create(CreationalContext<T>)
and get back a new D
instance. (If we pretend for just a moment that D
is made by some kind of custom bean, the custom bean author never had to touch the CreationalContext
when she implemented the create
method. She probably just returned new D()
or something similar.)
OK, so now the Context
has possession of a new D
object. But before it hands it back to the caller, it is going to stuff it in the supplied CreationalContext
as a dependent instance. After all, the @Dependent
-scoped Context
always produces dependent objects that are tied to some “higher-order” bean, and we know that this is what CreationalContext
instances are for: to store such dependent objects together with their referencing bean.
So it’s fine to say all this, but how does the @Dependent
-scoped Context
implementation actually add a dependent object to the CreationalContext
? We’ve already seen the push(Object)
method is not for this purpose.
It does it via proprietary means.
Weld, for example, does it via casting. This can get interesting since a user can supply her own @Dependent
-scoped Context
implementation: in order to add dependent objects herself she must tie her Context
implementation to Weld.
You might think you could have your own @Dependent
-scoped Context
implementation that arranges for a CreationalContext
to be used as a key into a Map
of this kind of state. Then you wouldn’t be bound to a particular implementation of CDI. But of course if someone calls release()
on a CreationalContext
, you would have to somehow arrange to be notified of such a call, and that’s impossible.
So the upshot is that the CDI vendor, who returns CreationalContext
implementations from BeanManager#createCreationalContext(Contextual<T>)
, is the only one who can supply any @Dependent
-scoped Context
implementations, no matter what the specification says.
Returning back to what the end user should “do” with a CreationalContext
: the answer is basically ignore it. If you are writing a custom Bean
implementation, then as the last operation in your delete
implementation you can do this:
if (cc != null) {
cc.release();
}
Otherwise, just leave the thing alone.