CreationalContext Observations

Here are some random observations concerning CreationalContext, a funky little architecturally polluting blemish on the surface of CDI’s otherwise pretty good set of APIs. (I’ve written before on this little nugget.)

There is no documentation that says what a CreationalContext is. The class javadoc reads, in total:

Provides operations that are used by the Contextual implementation during instance creation and destruction.

So its purpose is exactly that of its two operations, one of which (push()) can be properly implemented as a no-op as we’ll see below. That means its purpose is solely to house the release() method.

To portably create a CreationalContext, you use BeanManager#createCreationalContext(Contextual). For the purposes of destroying dependent objects, which is the interface’s sole documented purpose, the supplied Contextual is never used.

A CreationalContext is architecturally tightly coupled to a Context implementation for the Dependent scope. If you implement one, you have to implement the other because there is no portable way for an arbitrary Context implementing the Dependent scope to indicate to a CreationalContext that a dependent object needs to be tracked for subsequent destruction by the release() method. But in Weld you cannot supply your own instance of a Context for the Dependent scope, because the Weld-supplied one is always active, and there can be at most one active Context for a scope, and there is no way to remove a Context. So therefore you cannot supply your own implementation of CreationalContext in Weld unless you couple it to Weld interfaces and abstract classes…in which case why are you supplying one in the first place?

Weld implements CreationalContext by constructing a tree of them: each one tracks dependent objects added by its child. This means that Weld’s CreationalContext implementation is also tightly coupled to Weld’s implementation of BeanManager: every time a contextual reference is acquired, whether via injection or programmatically, a new “child” CreationalContext is created. This tree structure is not necessarily needed to perform dependent object cleanup (since, for example, OpenWebBeans implements CreationalContext without such a tree structure). The result is that in Weld many CreationalContextImpl objects get created that do nothing.

push() and release() have nothing to do with each other. In fact you can pass the TCK by implementing push() as a no-op. Many developers you talk to think that these methods are related. Almost nobody knows how to use them properly. You are, it turns out, supposed to always (probably within a finally block) call release() as the last thing you do in a custom bean’s destroy() method. Otherwise it is possible that your program will leak memory.

release() means, simply, “destroy dependent objects tracked by this CreationalContext“. Of course it may not be exactly this CreationalContext, because it might be a tree of such CreationalContexts. Or maybe it’s “destroy all dependent objects reachable from the creation of whatever it was that caused this CreationalContext to come into existence”. No specification language indicates whether release() must be idempotent. Obviously it would sure be nice if it were, so CDI implementations tend to make it so.

Remember that according to the specification a CDI implementation can destroy an unreferenced dependent object at any point by any means for any reason, so strictly speaking release() isn’t really a method that should have ended up in the specification (it’s an implementation detail). It’s clearly convenient so maybe that’s why it ended up in here.

The only time you need a CreationalContext is when you know that a contextual instance is going to be created. If you know that a contextual instance already exists, then the CreationalContext will never be used by the Context#get(Contextual, CreationalContext) method.

I often wonder why instead of this strange API there wasn’t a DependentContext interface, extending Context, that would allow you to add and destroy dependent object hierarchies, since we already know that Dependent is a special scope. There’s probably a good reason but I can’t think of what it is at the moment.

Advertisement