Tuesday, December 25, 2012

Globally configurable wiring alternatives in CDI

The problem

Among the shortcomings of CDI (1.0) as a general purpose DI container is the lack of what Spring 3.+ users know as profiles (not to mention Guice, which is much more expressive due to its Java EDSL approach). The CDI feature which looks closest at a first glance is the concept of alternatives, quickly turns out to be utterly useless: You can annotate your beans directly (or indirectly, via stereotypes) as @Alternatives and then choose among them for one bean-archive (read "jar") only by selecting them in the META-INF/beans.xml file.  So, there is no way to switch between wiring-profiles without repackaging (possibly all) the jars in the deployment-unit. CDI 1.1 improves very slightly on this by allowing a "global" selection in only one of the "beans.xml" which is still far below par.

The implementation-selector pattern

My currently favored work-around consists of the following steps
  • define an enumeration of profiles
  • define a qualifier annotation referring to one of those profiles
  • annotate service alternatives with the above profile-qualifier
  • define a producer selecting the appropriate profile programmatically, publishing it to "@Default" injection points.
In some more detail:

Profile enum and annotation

Let's start with the annotation: The Enum's slightly more interesting, because it sports a reference to an annotation literal which comes in handy later, when we select the right instance:

Annotate your service alternatives

Now we can annotate our service alternatives like
@InProfile(IN_MEMORY_DB) public class SampleSvcForInMemoryDb implements SampleSvc { ...

Define the implementation-selector

This is just a bean containing a producer method like this: Thanks to the @Any annotation, it gets all available instances injected. It then selects the appropriate one for the activeProfile, which is a member variable in this sample, but can, of course be any method-call. Unfortunately, CDI does not allow a useful generification of that pattern, as far as I could see. It's still inferior to what other frameworks offer, but if CDI is a set standard, it's usable.