jakartaee / cdi

CDI specification
Apache License 2.0
208 stars 78 forks source link

Make CDI to replace EJB #424

Open Emily-Jiang opened 4 years ago

Emily-Jiang commented 4 years ago

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:

alewis001 commented 4 years ago

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.

tandraschko commented 4 years ago

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

alewis001 commented 4 years ago

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

tandraschko commented 4 years ago

Its an official way and available since 1.2

alewis001 commented 4 years ago

Awesome! And thank you again!

aguibert commented 4 years ago

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
    }
}
rmannibucau commented 4 years ago

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.

ederks85 commented 4 years ago

Its an official way and available since 1.2

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

manovotn commented 4 years ago

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.

aguibert commented 4 years ago

@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

manovotn commented 4 years ago

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

mkouba commented 4 years ago

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.

rmannibucau commented 4 years ago

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

mkouba commented 4 years ago

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

manovotn commented 4 years ago

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.

rmannibucau commented 4 years ago

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?

Emily-Jiang commented 4 years ago

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?

rmannibucau commented 4 years ago

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

aguibert commented 4 years ago

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 commented 4 years ago

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.

cen1 commented 4 years ago

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.

rmannibucau commented 4 years ago

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

m-reza-rahman commented 4 years ago

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.

tandraschko commented 4 years ago

+1 reza

richard-grin commented 4 years ago

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

rmannibucau commented 4 years ago

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

ederks85 commented 4 years ago

This looks very interesting and comprehensable, Reze. Good initiative. In short: +1

Edwin

On Sun, 19 Apr 2020 at 11:20, Romain Manni-Bucau notifications@github.com 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 .

richard-grin commented 4 years ago

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

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

rmannibucau commented 4 years ago

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.

m-reza-rahman commented 4 years ago

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.

rhuan080 commented 4 years ago

+1 @m-reza-rahman

hantsy commented 4 years ago

@m-reza-rahman The table looks good, but I think the programmatic APIs(as alternatives of the declaration strategy with annotations) should be considered at the same time.

Besides EJB, there are a lot of things that should be aligned with CDI.

  1. The future of JSR 330, which is not updated in the latest 2 Java EE/Jakarta EE.
  2. DI in EJB, Jaxrs, JSF, etc.
  3. Refines Managed Bean1.0(part of Java EE 6.0), EJB, Jaxrs Bean, JSF Beans to make these compatible with CDI.
  4. EL and CDI, to make them work seamlessly. In Spring, SpringEL is used everywhere and every powerful, etc. Esp, I like the usages in annotations, such as Security, Cache settings, etc. and JPA QL(eg. use select * from XX where username=#{principal.name} to read the Authentication info in JPQL directly).
rmannibucau commented 4 years ago

@hantsy few comments on your last post:

1- JSR 330 shouldnt be intended to be updated, it was really the most common part of all IoC and guess it will stay like that, CDI could be updated if needed but JSR330 update sounds unlikely or do you see any precise need? 2+3- it is likely done enabling everything to use CDI, everything else is legacy now 4- EL+CDI is done, it is more about other specs and EL ;) probably just some "phrasing" to be sure to put the effort where is it needed. Typically, ELProcessor makes is not hard to impl that now (just bindings to handle).

richard-grin commented 4 years ago

OK. It's important to have the functionality to keep entities in a persistence context between transactions, like with an extended EntityManager. It is not often used but it can be useful, for example in long conversations between a client and a server.

Le 19/04/2020 à 16:57, Romain Manni-Bucau a écrit :

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 https://github.com/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.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/eclipse-ee4j/cdi/issues/424#issuecomment-616154471, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAYFS76NIL7M2EAUVKNSZ6TRNMGODANCNFSM4KXBYOOA.

scottmarlow commented 4 years ago

The idea of adding support for extended persistence context (XPC) in CDI was tracked via CDI-162.

One difference between application managed + XPC, is that the container manages the XPC (e.g. there are lot of behind the scenes tracking/management for XPC inheritance performed for stateful session beans). There is some cost to the tracking/management for XPC inheritance, that we don't have for application managed, however, for the application managed case, the application has to be careful not to leak the persistence context into other (user) requests, as persistence contexts can only be used by one thread at a time (the thread can vary and also be on different JVM instances, but again, never used by more than one thread at a time).

So, IMO, if users want CDI support for XPC, we should discuss the use cases and the JPA container contract, as well as the persistence provider contract as well.

rmannibucau commented 4 years ago

@scottmarlow AFAIK if you produce an entity manager in a scope (let say session or conversation one) then you meet all these requirements:

  1. You have an "inter request" em,
  2. You close it when no more needed (through @Disposes),
  3. Concurrency constraints is trivially manageable either with a @Lock interceptor (as in microprofile) or through the plain old synchronized (fine for this case),

So sounds like the feature is there for me. Do you see something else missing (I ignored the auto join tx which depends the PU style and tx mgt config)?

m-reza-rahman commented 4 years ago

The table looks good, but I think the programmatic APIs (as alternatives of the declaration strategy with annotations) should be considered at the same time.

Sure. This is next level detail that should be discussed in the respective destination projects.

Besides EJB, there are a lot of things that should be aligned with CDI.

Certainly. I suggest following this up with the respective projects with some concrete details. Top of mind Jakarta REST and Batch certainly make sense. The others I think are largely done (e.g. JSF), the value proposition is tenuous (e.g. managed beans) or too difficult to do (e.g. Servlet).

The future of JSR 330, which is not updated in the latest 2 Java EE/Jakarta EE.

Personally I think this is an awkward part of Jakarta EE best left alone.

use select * from XX where username=#{principal.name}

If I am understanding this correctly, I believe I have already proposed this for Jakarta Security. I will make sure to remind Arjan once the Jakarta EE 9 work is under control. It's actually very easy to implement. That said, again, I encourage you to follow up with details in the right project (probably start a discussion and then file an issue).

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.

hantsy commented 4 years ago

@m-reza-rahman I see. These could be discussed under individual issues.

Ladicek commented 1 year ago

Briefly discussed this on the CDI call today. The 1st comment in this issue notes:

  1. @Startup beans could be easily translated to CDI, which now has the Startup event. We could add an annotation (e.g. @Eager) that would translate to a synthetic observer of the Startup event on the annotated managed bean.
  2. Timers could be specified by Jakarta Concurrency (e.g. methods on managed beans that are annotated @Scheduled), and if CDI had invokable methods (#460, #639), Jakarta Concurrency could probably delegate to that for the actual method invocation (getting interceptors for free). I'm not sure if we could easily make @AroundTimeout work, but maybe that isn't terribly critical?

There's a lot more in EJB, but these 2 items are what we touched on today.