Archive

Posts Tagged ‘CDI’

Post CDI 2.0 talk clarification

novembre 26, 2014 Laisser un commentaire

Recently Antoine Sabot Durand, co spec lead of CDI 2.0, went to the ElsassJug to present his (ongoing) work.

After the talk, I was pressed by some to share my views and was at the same time advertised as a « CDI hater », which doesn’t help sane and sound discussions.

I had to leave early (public transportation has its drawbacks) and thus hadn’t planned to speak, yet I did in a hurry and unformalized way which ended being some crappy rant. Thus, here are my apologies to Antoine and a more proper presentation of my thoughts.

Let’s dig in 🙂

CDI 2 : Programmatic binding possible but not out of the box

CDI 2.0 should see the integration of DeltaSpike work into the standard: users will finally be able, in a standardized way, to hook into the binding definition process, when the application starts.

While nice and needed, the chosen way is complex, with plenty of hooks and concepts to master, including a DSL over annotations.

Furthermore, it allows plenty: you could easily break completely your stuff, not even speaking of corner cases’ bugs. So, it’s not for the usual & common coding actions, but something to approach with care and time.

For sure, yet, such a mechanism was deeply needed, due to the early reliance on annotations, conventions & beans.xml for binding definition. Somehow a programmatic approach ended up being needed, who could have guessed?

However, the main use case I see for a dependency injection framework is something close to the following :
if (foo) bind(A.class).to(C.class) else bind(A.class).to(D.class)
This isn’t supported out of the box.

For sure, now, one could implement such mechanism on his own, all the hooks are there. But I don’t want to: it’s complex and comes with extra « rules » which I would like to see implemented as well, like for example to fail if the same binding is defined twice programmatically. This is extra work, bug prone and core to a DI framework: I want to reuse something which is common knowledge of the framework.

So here are my 2 cents on this topic: in CDI 2.0, deprecate alternatives in beans.xml (and everything in there actually), make it all possible through the binding definition process. So binding configuration would be a matter of XML, for the default/simple stuff, completed by programmatic bindings if needed. Then provide a proper way to do programmatic bindings, based on the above hooks but readily accessible to the usual dev for everyday work. Keep the above hooks for funky needs.

Qualifiers are actually way more…

Due to the (previous) lack of programmatic definition of binding, CDI has full blown support for qualifiers. This means annotations to specify which implementation to actually provide. On top, one can stack many qualifiers on the same injection point, plus pass some parameters through the annotations for the implementation provider…

In fact, qualifiers are so common in CDI they provide a way to do an annotation aliasing various qualifiers (a stereotype annotation as it’s called).

Sounds good to you?

Well, not to me. Yes, a qualifier is sometimes needed when the same interface has different implementations for different contexts. But it’s a convenience stuff provided by a DI framework. Indeed, one could actually create a new class/interface and bind it instead, skipping the need for qualifiers. And one should avoid, in client classes, to provide to much things about the injected service. I mean, the whole point of injection is to avoid implementation related details isn’t it?

Yet, in CDI, they seem so common the speaker himself spoke of annotations hell. Furthermore, the ability to provide parameters looks error prone as well. The example given was the following:

@Inject @HttpParam("someParam")
private String someString;

The provider then looks up the « someParam » and returns the matching value.

This has multiple drawbacks IMHO.

For example, the dependency of this class on the RequestContext is hidden, which doesn’t help, both for readability and proper dependency tracking. For example, Antoine rightly said that this could be an issue when no Request where at hand in the current context (for example creating brand new instances of Request for one time use on each call).

But the main issue here, IMHO, is to have runtime behavior depending on annotations, for example what if at runtime I need other params?

One could say « no worries, put them as well in the annotation ». And then you start to alter your design to adapt to CDI. Bad. And limitating: maybe you really want to get some value just once in a while and not every time, so the injection would be unneeded & clutter all the time. Worst, you can’t interact programmatically easily there: there’s no matching programmatic way to access such « injection outcome ». Well, actually, in CDI 2.0 they should provide hooks to get the « injector » and do stuff programmatically, but this is planned for third parties integration, not every day, proper, CDI code.

It really hilights the conceptual issue: it’s not about injection anymore, but interaction with a service. I don’t want annotations for that, it’s the wrong concept. Actually, I fear this code smell to become widespread: to provide services actions through annotations could become widespread and code smells waiting to blow up on their users.

Which leads me to a third issue with CDI, directly coming from this: the event bus.

An event bus… to avoid IMHO

Among the CDI annotations, some come regarding an event bus. With it, one can « fire » and « observe » events, using qualifier annotations to qualify which one(s) we want to observe (on top of the event type itself). Be cautious though, the qualifiers don’t work with event as they work with beans (different type handling mechanism). One can also use « Conditional Observer methods », for example:
public void processQualifiedPayload(@Observes(receive=ALWAYS) Type1 event) {}

The observers are triggered in a synchronous way: all must have finished their observations for the code of the fire to go on. On top, the events are mutable and we were told one could even go as far as modifying them on the go. Finally, these events are local to the current VM instance (or container even, I’m not sure) and… that’s it.

Being interested in event driven architecture, this implementation seems pretty weak to me.

First of all, API wise, conditional observer part is pretty weak: hard coded strings for the win! Or not… A proper method call, maybe with enum, would be ten times better isn’t it? Maybe one could be as crazy as providing some filter function.

More importantly: how do I monitor these events? What if some fire too many of them? What if I want to persist them? What if I want to timeout the observers’ actions?

Sure enough, I was told this stuff was limited and one should use it properly, meaning for none of the above use cases.

On top, asynchronous events should come in CDI 2.0, and maybe as well some way to persist, broadcast or order the observers. All through annotations AFAIK. Annotation hell anyone?

In fact, one real use case of events is for the binding mechanism from DeltaSpike: it’s partly based on events, to allow people to hook at given points in the cycle (they observe these events).

While a valid use case, the semantics at play are completely at odds with asynchronous events, persistence and broadcasting. Wanna broadcast/persist your wiring events anyone?

So here are my 2 cents: remove the event bus from CDI 2.0.

Instead, change it to do a configuration specific DSL used to hook into the startup phase. Then have a new JSR regarding a Java EE event bus, and do proper stuff there, not relying on annotations for all of its public API…

On a broader level, remove/deprecate parameters used by the provider from qualifier annotations, to avoid code smells. Qualifiers should be used to qualify, no more no less.

Conclusion

That’s the main points I wanted to raise after the talk, hopefully done this time in a proper way… Let me know !

On a bigger picture, CDI is usable for sure. It does its job roughly. If I had to use it, I would, taking care of avoiding the above pitfalls. Yet, in its present state, I prefer to avoid it.

Best
Joseph

Publicités
Étiquettes : ,
%d blogueurs aiment cette page :