cdi icon indicating copy to clipboard operation
cdi copied to clipboard

Make CDI to replace EJB

Open Emily-Jiang opened this issue 5 years ago • 42 comments

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.

Emily-Jiang avatar Feb 18 '20 10:02 Emily-Jiang

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.

alewis001 avatar Mar 01 '20 05:03 alewis001

startup is easy: @Observes @Initialized(ApplicationScoped.class)

tandraschko avatar Mar 01 '20 10:03 tandraschko

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 :)

alewis001 avatar Mar 01 '20 12:03 alewis001

Its an official way and available since 1.2

tandraschko avatar Mar 01 '20 12:03 tandraschko

Awesome! And thank you again!

alewis001 avatar Mar 01 '20 12:03 alewis001

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
    }
}

aguibert avatar Mar 01 '20 19:03 aguibert

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:

  1. Scope is different ("put it all" for EJB vs "IoC" for CDI with a few historical exceptions/mistakes)
  2. EJB already reversed part of its responsabilities (jta, scopes, singleton+startup, ...)
  3. 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.

rmannibucau avatar Mar 01 '20 22:03 rmannibucau

Its an official way and available since 1.2

Yes, but I think this approach can be polished a bit =)

ederks85 avatar Mar 02 '20 07:03 ederks85

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.

manovotn avatar Mar 02 '20 14:03 manovotn

@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 avatar Mar 02 '20 15:03 aguibert

@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.

manovotn avatar Mar 02 '20 16:03 manovotn

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.

mkouba avatar Mar 02 '20 16:03 mkouba

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."

rmannibucau avatar Mar 02 '20 16:03 rmannibucau

-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).

mkouba avatar Mar 02 '20 16:03 mkouba

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.

manovotn avatar Mar 02 '20 16:03 manovotn

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?

rmannibucau avatar Mar 02 '20 18:03 rmannibucau

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 avatar Mar 02 '20 18:03 Emily-Jiang

@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 ;).

rmannibucau avatar Mar 02 '20 18:03 rmannibucau

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.

aguibert avatar Mar 02 '20 18:03 aguibert

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.

aguibert avatar Mar 03 '20 14:03 aguibert

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 avatar Apr 17 '20 12:04 cen1

@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.

rmannibucau avatar Apr 17 '20 12:04 rmannibucau

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.

m-reza-rahman avatar Apr 18 '20 23:04 m-reza-rahman

+1 reza

tandraschko avatar Apr 18 '20 23:04 tandraschko

What about the extended EntityManagers which must be in a stateful EJB?

richard-grin avatar Apr 19 '20 08:04 richard-grin

Stateful entity managers are already handled through scopes/contexts in CDI IMHO.

rmannibucau avatar Apr 19 '20 09:04 rmannibucau

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 .

ederks85 avatar Apr 19 '20 09:04 ederks85

Stateful entity managers are already handled through scopes/contexts in CDI IMHO.

Yes, but is an extended EntityManager allowed in a CDI bean?

richard-grin avatar Apr 19 '20 11:04 richard-grin

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.

rmannibucau avatar Apr 19 '20 14:04 rmannibucau

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.

m-reza-rahman avatar Apr 19 '20 15:04 m-reza-rahman