cdi
cdi copied to clipboard
Make CDI to replace EJB
A frequent request for CDI is to replace EJB as CDI is becoming more and more popular and widely adopted. CDI covers many EJB functionalities with a much easier framework. However, there are some gaps to be filled. I would like to use this issue to address the gaps where CDI has no counterpart functionalities. This includes:
-
startup
-
Timer I think the useful feature of EJB is startup, so in CDI, we should provide this equivalent feature. As for Timer support, I think it can be supplied by other specs.
Startup is certainly a gap I have run into so would vote for that being addressed in some way. Or, said differently, a scope that enables an object to operate as a "task" and a way to have that task start as soon as the application is initialised, i.e. it's lifecycle of not triggered by or bound to an HTTP request.
startup is easy: @Observes @Initialized(ApplicationScoped.class)
Hi @tandraschko, thank you for that, I didn't know about that approach. Is that an official way of achieving at-startup or it just so happens to work? I'd worry about the points of lifecycle of watching the annotation class and it changing if this is not the spec defined way of achieving at-startup.
I may be worrying needlessly :)
Its an official way and available since 1.2
Awesome! And thank you again!
For timer support, perhaps CDI could introduce some mechanism to "observe" an interval of time?
I propose we define a new annotation@Schedule inspired by @javax.ejb.Schedule that would have many of the same attributes except for persistent (persistent timers is out of scope for CDI)
For example:
@ApplicationScoped
public class TimerBean {
public void timerTask(@Observes @Schedule(minute = "*/5") java.time.Instant calledAt) {
// This gets called every 5 minutes
}
}
What about making that an extension first? It is quite trivial to impl it and IMO it does not belong to CDI which should stay the core IoC.
EE concurrency sounds like an option but truely speaking, if not distributable it is just a matter of creating a scheduledexecutorservice in a cdi bean so guess it should stay distributable to have more added value than just proposing another api than java se one.
So high level, CDI shouldnt aim at replacing EJB because:
- Scope is different ("put it all" for EJB vs "IoC" for CDI with a few historical exceptions/mistakes)
- EJB already reversed part of its responsabilities (jta, scopes, singleton+startup, ...)
- CDI was built with extension api to avoid to redo EJB like specs
So IMHO this issue must be closed and tackled at jakarta ee level (where to dispatch scheduling now since only other missing feature is bean pooling and this is no more needed these days or trivially replaced by a Semaphore when uszd as a throttler).
Just my 2 cents.
Its an official way and available since 1.2
Yes, but I think this approach can be polished a bit =)
Is that an official way of achieving at-startup or it just so happens to work?
Yes, that's official. If you add such an obsever into a bean, an instance of bean will have to be created eagerly to be able to invoke that observer. That works fine for singleton and application scoped bean, other scoped might have issues as the context doesn't need to be active, or in case of dependent bean, a new instance will be created, observer invoked and the instance will then be destroyed again.
@ederks85 Quarkus has defined its own StartupEvent which can be observed like this:
import io.quarkus.runtime.StartupEvent;
@ApplicationScoped
class CoolService {
void startup(@Observes StartupEvent event) {
}
}
Perhaps we could standardize this event? I like this approach more than the current @Observes @Initialized(ApplicationScoped.class) approach
@aguibert the reason Quarkus has this is because ApplicationScoped in JVM mode is started before you get to runtime (in STATIC_INIT). This causes differences in behaviour when running in JVM versus native which may or may not affect what you sim to do with the observer. StartupEvent was introduced to fix that as it is always performed in RUNTIME_INIT phase which is consistent for both modes. See https://quarkus.io/guides/lifecycle#what-is-the-difference-from-initializedapplicationscoped-class-and-destroyedapplicationscoped-class
In other words, from purely CDI perspective @Observes @Initialized(ApplicationScoped.class) and @Observes StartupEvent would be duplicates.
There is one more difference: @Observes @Initialized(ApplicationScoped.class) is not saying anything about the application itself. It's fired when the application context is initialized. Whereas @Observes StartupEvent and @io.quarkus.runtime.Startup (since Quarkus 1.3) is fired/triggered when the application is started, i.e. it may depend on some other services that are required to run before an application is considered started.
Before adding anything we should ensure we don't duplicate something already there and fully specified. There is another built-in way to initialize at startup some code since v1.0: through extensions and AfterValidationDeployment event. Any reason to not enable AfterValidationDeployment event to be observed by any bean instead of limiting it to just extensions? Concretely the spec would be something like that:
"[existing definition]. AfterValidationDeployment can also be observe by CDI beans, the container is responsible to ensure it is fired through all extensions first, and if there is no exception it is fired to all other beans."
-1. I think that this would be even more confusing for users. AfterDeploymentValidation is a container lifecycle event that should only be observed by extensions (per the current wording).
The line between what events are container lifecycle ones and which are standard ones is (IMO) pretty well defined, I would try and avoid blurring it, so also -1.
Besides, you are saying that you don't want to duplicate what's already there, and @Observes @Initialized(ApplicationScoped.class) is already there and is not really different from what you are suggesting here, it's just another payload.
Right so we already have 2 ways to solve that so we don't need a 3rd. For the story @Observes @Initialized(ApplicationScoped.class) was mainly added by consistency (initialized exists for all built-in scopes) so is an acceptable exception in that regards.
So summary is we are good and close this issue?
Maybe we can call out the @Observes @Initialized(ApplicationScoped.class) is equivalent to EJB startup bean. Once we have done the support for Timer, CDI is kind of parity with EJB. Are there any other gaps?
@Emily-Jiang timers don't belong to CDI (see https://github.com/eclipse-ee4j/cdi/issues/424#issuecomment-593152876), this is why I think we are good. Only remaining gap will be pooled beans and CMP but don't think we want to do it there too ;).
I agree with @rmannibucau regarding timers being out of scope for CDI core.
I'm working on prototyping a portable CDI extension that will allow people to do what I described here and will see if I can deliver it in some OSS org.
If we agree that startup beans already have a solution and timers can be done with a portable extension, I think we can close this issue.
The EE Concurrency API has the following issue open for implementing an @Schedule annotation:
https://github.com/eclipse-ee4j/concurrency-api/issues/98
I added my proposal/POC to that issue, and I think EE Concurrency would be the proper spec to house such functionality.
Shameless jump in from a CDI user.
As a developer, I would prefer @Startup to have the meaning of framework/server as a whole is fully set up, not when the CDI component happens to be up. The @Observes method happens to work 99% of the time but is not correct in theory. What if CDI component initializes before another MP/Jakarta/Custom extension component and I try to use that component in the @Startup/@Observes bean?
There could be a dedicated annotation for this and a specified CDI event which the implementor would fire when they are finished with initialization.
Not sure this even belongs in the CDI, just something I observed from the comments above.
@cen1 it is exactly the same with EJB @Startup, you still have lifecycle issues as soon as you rely on multiple startup beans and dependencies can be hidden (inter modules) and not always resolved with depends on so I guess it is just status quo there + in CDI you can always wrap another bean to initialize it later (through an extension) so more flexible than EJB.
I have analyzed this issue in detail some time ago (perhaps somewhat sadly). My belief is that there isn't much to be done in the CDI specification itself. Most of the work resides in other Jakarta EE specifications absorbing EJB features using the CDI programming model as a baseline. Without going into great details, the following is the mapping I had come up with. In my view most of the discussions need to start with the EJB specification project and fan out to respective destination Jakarta EE projects.
| EJB API | Destination | Comments |
|---|---|---|
| @TransactionAttribute, etc | Jakarta Transactions – renamed to @Transactional (already done) | Should be composable via @Stereotype. |
| @RolesAllowed, @RunAs, etc | Jakarta Security | Should be usable by any CDI bean. |
| @Asynchronous, etc | Jakarta Concurrency | Should be composable via @Stereotype. |
| @Schedule, etc | Jakarta Concurrency | Should be composable via @Stereotype. |
| TimerService, etc | Jakarta Concurrency | These services can be injected into any managed bean. |
| @MessageDriven, etc | Jakarta Messaging – renamed to @JmsListener, Jakarta Connectors – renamed to @ResourceListener | Redefine as declarative POJO JMS/JCA listeners on methods, not classes. |
| @Lock, @AccessTimeout, etc | Jakarta Concurrency | Should be composable via @Stereotype. |
| @Pooled, @MaxConcurrency (these are very common but non-standard EJB features) | Jakarta Concurrency | Should be composable via @Stereotype. |
Reza Rahman Jakarta EE Ambassador, Author, Blogger, Speaker
Please note views expressed here are my own as an individual community member and do not reflect the views of my employer.
+1 reza
What about the extended EntityManagers which must be in a stateful EJB?
Stateful entity managers are already handled through scopes/contexts in CDI IMHO.
This looks very interesting and comprehensable, Reze. Good initiative. In short: +1
Edwin
On Sun, 19 Apr 2020 at 11:20, Romain Manni-Bucau [email protected] wrote:
Stateful entity managers are already handled through scopes/contexts in CDI IMHO.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/eclipse-ee4j/cdi/issues/424#issuecomment-616083874, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABAPLV2USPCCUBURSBUNGSDRNK66BANCNFSM4KXBYOOA .
Stateful entity managers are already handled through scopes/contexts in CDI IMHO.
Yes, but is an extended EntityManager allowed in a CDI bean?
Yes, but is an extended EntityManager allowed in a CDI bean?
Well, @PersistenceContext is allowed in EE but more important a stateful entity manager is an entity manager you can reuse accross calls which is exactly the definition of scopes in CDI. So long story shorts, I guess @PersistenceContext will die and be replaced by an @unit or equivalent qualifier to be CDI friendly and driven so i'd just let @persistenceContext die. Now, in terms of feature, CDI does not miss anything in that area.
What about the extended EntityManagers which must be in a stateful EJB?
With all due respect, I have personally not seen that feature in use in the field for many years. It could just be left in EJB, just like EJB remoting, or JPA could further standardize targeting the appropriate CDI scopes. Should be fairly easy to implement.
Reza Rahman Jakarta EE Ambassador, Author, Blogger, Speaker
Please note views expressed here are my own as an individual community member and do not reflect the views of my employer.