So in the brave new world of JDK 9, you may have heard of its nascent module system.
A tutorial of the Java 9 module system is way beyond the scope of this blog, so see the Project Jigsaw page or the already quite outdated State of the Module System document and try not to accidentally read any more out-of-date documentation than you have to (it’s harder than you think).
Core to the module system is the concept of declaring what modules you (if you’re a module) require, what packages you export, what services you use and what service implementations you provide.
It struck me that CDI has something analogous that approaches such a module system already. This is not revolutionary, but it helped me see a few things clearly. As always this blog is mainly a stream of consciousness, so buyer beware.
First, a CDI “module” is, if you squint hard enough, a bean archive. (A bean archive is, by default, a classpath location with a META-INF/beans.xml
resource in it. There are other ways to define bean archives.)
Next, a bean archive does not require other archives, but does require bean implementations, in the sense that if an injection point “inside” it isn’t satisfied the container won’t come up. So if my archive contains a bean that injects a Frobnicator
, then hopefully some archive somewhere offers up, or produces, a Frobnicator
implementation to satisfy that injection point.
A bean archive doesn’t explicitly export anything, but it does produce certain bean implementations. This is a granular form of exporting.
(Finally, all of this looks and smells like service usage and service furnishing/provisioning, but of course takes context and object lifecycles and decoration and so forth into account so it’s a heck of a lot more powerful.)
OK so fine, there’s a loose analog between these two systems. Just for fun, let’s define some terms that will make hazy sense to CDI developers and Java 9 module system fanatics alike:
- There’s
requires
, let’s say. That encompasses a bean using either@Inject
or, say, an interceptor binding annotation or some other form of indicating a dependency on a bean or a bean-like object (like an interceptor or decorator). So my bean archive canrequire
something of your bean archive. - There’s
produces
, let’s say. That encompasses a bean archive offering up an implementation capable of satisfying an injection point. This could take the form of packaging up a managed bean, or producing something from a producer method, etc. Presumably bean archives that produce one thing satisfy bean archives that require that thing. So your bean archiveproduces
the thing my bean archiverequires
.
If we stopped there, we have enough for basic usage. Imagine a world where I require a Frobnicator
, and you produce a Frobnicator
implementation. If I knew that, I would grab your project, put it on the classpath, and then when the container came up my Frobnicator
injection point would be satisfied. Things would be great.
Now expand that a bit. Let’s say that it’s not just you who offers up a Frobnicator
implementation. Let’s say that woman over there also has a Frobnicator
implementation. How did I find her stuff—and yours for that matter? How did I decide to make my CDI application (a pile of bean archives, exactly one of which creates an SeContainer
) use her stuff instead of yours?
(If you’re like most Java developers, the answer is something like a noxious cocktail of StackOverflow and search.maven.org and stuff you read on Twitter or Reddit the other day. That’s crummy.)
Let’s expand this even more. Suppose your archive contains a JAX-RS javax.ws.rs.core.Application
bean. There is some sense in which (using my previous terminology) you produce
this bean, but there’s another sense in which it is not really usable properly unless the thing that uses it actually deploys it and serves it up in a specification-compliant manner. That sense has to do with deployment, so let’s say that a bean archive can declare that it can deploy
certain bean types, and another bean archive can say that it publishes
certain bean types. A published
bean type, let’s say, is one that needs to be notionally deployed
in an environment that is subordinate to and encapsulated from the overarching CDI container environment (like an HttpServer
launched inside a CDI container; see a prior post of mine on this subject).
What I regard as a strength of CDI—that the Java module system authors regard as a weakness, I suppose, and with whom I strongly disagree, if so—is that CDI’s modular concepts are loosely coupled. My bean archive can inject interfaces that a second regular jar file provides, and your bean archive can implement/produce them, and I don’t need to know about your archive at all until it comes time to choose what implementation of my injection points I want.
What if we could make this kind of discovery and linkage a little easier?
The Java module system has a tool called jlink
. It takes in a (locally present) Java 9 module graph and produces a hairball that in some deliberately unspecified way (they call it a “runtime image”) encapsulates all the modules found in a compact executable format.
What if there were a cdiLink
tool that were kind of like that?
What if this cdiLink
tool could somehow read some indexed data somewhere about your business logic pieces, and about various (say) Maven Central artifacts and identify all of these things as bean archives producing
and requiring
and deploying
and publishing
various bean types?
If you go back and read some of my previous posts on composition-based programming with CDI 2.0, you can see that thanks to the way that CDI discovers bean archives that are locally present you don’t even need to write, say, your own main()
method. Someone else can do that and make a bean archive available on Maven Central (say) with it in there.
If you read some of my previous posts on politely blocking the CDI container, you can see that you don’t even have to write or deploy your own container-or-server-reliant application. Someone else can do that and make it available on Maven Central (say) with it in there.
So now imagine if a bean archive could declare, in some easily indexable manner, that it requires
certain bean types, and produces
other bean types. Imagine further that some repository could be queried for such things by a cdiLink
tool, and a user could then select between various producers interactively.
Then you could take your (extraordinarily minimal!) application (maybe just a JAX-RS endpoint and nothing else!), and cdiLink
it as part of the development process.
cdiLink
might go through the following, one or more times, using my terminology above:
- I noticed you have a JAX-RS
Application
class and its attendant root resource classes. I know [who knows how, haven’t gotten that far yet] that this means you arepublishing
theApplication
bean type. - That means I have to find someone in the universe who
deploys
beans of typeApplication
. - Ah! I have found a Jersey-and-Grizzly-based bean archive Out There In The World™ that claims that it deploys
Application
instances. I have also found a Netty-and-RestEasy-based bean archive that claims it does the same. Which would you like to use? The Jersey-based one? Very well, I’ll use that one. - Next, I have found two basic boilerplate implementations of the standard CDI container startup pattern. One is called Fred, and the other is called Joe. Would you like to use Fred, or Joe? If you don’t care, I’ll pick Fred. OK. Fred it is.
- Finally, of course, which CDI implementation do you want to use? Weld or OpenWebBeans? Weld? OK. (Obviously I’ll include any transitive dependencies it has!)
- Please hold while I assemble your executable hairball [which may just be a classpath; recall that one of the bean archives has a main class in it].
- OK, the hairball is built. Here it is. Just run it.
I think this could be quite powerful, especially when combined with the easy ability to add other bean archives to the classpath (perhaps with interceptors, decorators and alternatives that are more suitable for the final runtime environment the resulting application might find itself in).
Furthermore, the whole tool itself might be implementable as a portable extension.
OK, that’s enough sketchy thoughts for one evening. Thanks for reading.