Where to use EJB 3.1 and CDI?
Asked Answered
R

2

137

I am making a Java EE based product in which I'm using GlassFish 3 and EJB 3.1.

My application has session beans, a scheduler and uses web services. I recently came to know about Apache TomEE, which supports Contexts and Dependency Injection (CDI). The GlassFish container also supports CDI.

Can I replace session beans where I do not require any feature that CDI also doesn't already provides? And if then, what are the benefits I can get?

Rudie answered 21/11, 2012 at 7:2 Comment(0)
A
462

Yes, you can freely mix both CDI and EJB and achieve some great results. It sounds like you are using @WebService and @Schedule, which are good reasons for adding EJB to the mix.

There's a lot of confusion out there, so here is some general information on EJB and CDI as they relate to each together.

EJB >= CDI

Note that EJBs are CDI beans and therefore have all the benefits of CDI. The reverse is not true (yet). So definitely don't get into the habit of thinking "EJB vs CDI" as that logic really translates to "EJB+CDI vs CDI", which is an odd equation.

In future versions of Java EE we'll be continuing to align them. What aligning means is allowing people to do what they already can do, just without the @Stateful, @Stateless or @Singleton annotation at the top.

EJB and CDI in Implementation Terms

Ultimately, EJB and CDI share the same fundamental design of being proxied components. When you get a reference to an EJB or CDI bean, it isn't the real bean. Rather the object you are given is a fake (a proxy). When you invoke a method on this fake object, the call goes to the container who will send the call through interceptors, decorators, etc. as well as take care of any transaction or security checks. Once all that is done, the call finally goes to the real object and the result is passed back through the proxy to the caller.

The difference only comes in how the object to be invoked is resolved. By "resolved" we simply mean, where and how the container looks for the real instance to invoke.

In CDI the container looks in a "scope", which will basically be a hashmap that lives for a specific period of time (per request @RequestScoped, per HTTP Session @SessionScoped, per application @ApplicationScoped, JSF Conversation @ConversationScoped, or per your custom scope implementation).

In EJB the container looks also into a hashmap if the bean is of type @Stateful. An @Stateful bean can also use any of the above scope annotations causing it to live and die with all the other beans in the scope. In EJB @Stateful is essentially the "any scoped" bean. The @Stateless is basically an instance pool -- you get an instance from the pool for the duration of one invocation. The @Singleton is essentially @ApplicationScoped

So in a fundamental level, anything you can do with an "EJB" bean you should be able to do with a "CDI" bean. Under the covers it's awfully hard to tell them apart. All the plumbing is the same with the exception of how instances are resolved.

They aren't currently the same in terms of the services the container will offer when doing this proxying, but as I say we're working on it at the Java EE spec level.

Performance note

Disregard any "light" or "heavy" mental images you may have. That's all marketing. They have the same internal design for the most part. CDI instance resolution is perhaps a bit more complex because it is slightly more dynamic and contextual. EJB instance resolution is fairly static, dumb and simple by comparison.

I can tell you from an implementation perspective in TomEE, there's about zero performance difference between invoking an EJB vs invoking a CDI bean.

Default to POJOs, then CDI, then EJB

Of course don't use CDI or EJB when there is no benefit. Throw in CDI when you start to want injection, events, interceptors, decorators, lifecycle tracking and things like that. That's most the time.

Beyond those basics, there are a number of useful container services you only have the option to use if you make your CDI bean also an EJB by adding @Stateful, @Stateless, or @Singleton on it.

Here's a short list of when I break out the EJBs.

Using JAX-WS

Exposing a JAX-WS @WebService. I'm lazy. When the @WebService is also an EJB, you don't have to list it and map it as a servlet in the web.xml file. That's work to me. Plus I get the option to use any of the other functionality mentioned below. So it's a no-brainer for me.

Available to @Stateless and @Singleton only.

Using JAX-RS

Exposing a JAX-RS resource via @Path. I'm still lazy. When the RESTful service is also an EJB, again you get automatic discovery and don't have to add it to a JAX-RS Application subclass or anything like that. Plus I can expose the exact same bean as an @WebService if I want to or use any of the great functionality mentioned below.

Available to @Stateless and @Singleton only.

Startup logic

Load on startup via @Startup. There is currently no equivalent to this in CDI. Somehow we missed adding something like an AfterStartup event in the container lifecycle. Had we done this, you simply could have had an @ApplicationScoped bean that listened for it and that would be effectively the same as an @Singleton with @Startup. It's on the list for CDI 1.1.

Available to @Singleton only.

Working in Parallel

@Asynchronous method invocation. Starting threads is a no-no in any server-side environment. Having too many threads is a serious performance killer. This annotation allows you to parallelize things you do using the container's thread pool. This is awesome.

Available to @Stateful, @Stateless and @Singleton.

Scheduling work

@Schedule or ScheduleExpression is basically a cron or Quartz functionality. Also very awesome. Most containers just use Quartz under the covers for this. Most people don't know, however, that scheduling work in Java EE is transactional! If you update a database then schedule some work and one of them fails, both will automatically cleaned up. If the EntityManager persist call fails or there is a problem flushing, there is no need to un-schedule the work. Yay, transactions.

Available to @Stateless and @Singleton only.

Using EntityManagers in a JTA transaction

The above note on transactions of course requires you to use a JTA managed EntityManager. You can use them with plain "CDI", but without the container-managed transactions it can get really monotonous duplicating the UserTransaction commit/rollback logic.

Available to all Java EE components including CDI, JSF @ManagedBean, @WebServlet, @WebListener, @WebFilter, etc. The @TransactionAttribute annotation, however, is available to @Stateful, @Stateless and @Singleton only.

Keeping JTA managed EntityManager

The EXTENDED managed EntityManager allows you to keep an EntityManager open between JTA transactions and not lose the cached data. Good feature for the right time and place. Use responsibly :)

Available to @Stateful only.

Easy synchronization

When you need synchronization, the @Lock(READ) and @Lock(WRITE) annotations are pretty excellent. It allows you to get concurrent access management for free. Skip all the ReentrantReadWriteLock plumbing. In the same bucket is @AccessTimeout, which allows you to say how long a thread should wait to get access to the bean instance before giving up.

Available to @Singleton beans only.

Ammons answered 22/11, 2012 at 1:42 Comment(11)
This is by far the best explanation on that topic I've ever read. It also covers nearly all important aspects of EJB in Real-Life use as well. Great Work!!Kantian
Except is wrong :) so thumb down for that. "CDI-managed beans are contextual and EJB beans are not" Adam Bien at: oracle.com/technetwork/articles/java/… Try creating a @Stateful @SessionScoped pojo, its state: an atomic integer, and one method which getsAndIncrements. From a web service call this method. Try injecting the bean in the web service as CDI (@Inject) and then as EJB(@EJB), make several requests from the same session, noticed the difference? So rather than EJB >= CDI more EJB != CDI.Kaoliang
We've done our best and continue to do our best to address these issues. The CDI 1.0 TCK had only 150 tests that included EJB and 0 tests that included JAX-RS and JAX-WS. The CDI 1.2 TCK does at least have some JAX-RS tests, but not enough IMHO.Ammons
Note also the EJB specification only allows Stateless and Singleton beans to be JAX-RS or JAX-WS web services. Any use of HTTP sessions or Stateful beans with JAX-RS or JAX-WS is non-portable.Ammons
Actually it seems you were right and I was wrong (yeah surprise), I want to return the thumb up but it says"...vote is locked until ..edited", my apologies. Hmmm Adam was wrong too, did i miss smth? It seems like the problem is injecting with @EJB vs @Inject both can be used to inject EJBs developer.jboss.org/thread/179388, and the second considers the scope, for @Stateful EJBs that is. My miss belief was that injection using @Inject only injects the ejb as a CDI, in the CDI container, therefore no concurrency, asynch, transaction management, etc was provided.Kaoliang
Very understandable and Adam is not wrong in strict legal terms, but the distinction is moot. The spec says the EJB instance is not contextual, but then says later the reference (proxy) to the EJB is contextual. A Stateful bean's lifecycle is controlled entirely through the reference (proxy), so when the CDI container is controlling that reference (proxy) the math comes out the same -- Stateful EJBs can effectively be contextual.Ammons
Totally right, thank you sir. From JSR 299:"EJB components may be stateful, but are not by nature contextual."..."Any session bean instance obtained via the dependency injection service is a contextual instance. It is bound to a lifecycle context and is available to other objects that execute in that context. The container automatically creates the instance when it is needed by a client. When the context ends, the container automatically destroys the instance."Kaoliang
I didn't find in JSR 299 how the SLSB's lifecycle is managed when it is injected using @Inject. It will be controled by the CDI container with the scope of the CDI bean or it will be controled by EJB container and the instance will be took from the EJB pool?Justle
Did you write this on your lunch break at TESLA?Poster
This might be a stupid question but - does EJB and CDI share same container or use different one?Hobo
I would be glad if you could write something about Events in CDI: @Observes(during=...)?. Why is it not possible to "register" an event like AFTER_SUCCESS / AFTER_COMPLETION... in EJBs to let code with a defined context be called after a transaction has been finished? - This is a often used use case - it seems to be only possible with CDI?! - I would also appreciate, if you could explain what @Petr Benes asked: Whats about the containers for CDI beans / EJBs, if we mix both? - Thanks in advance for a little update :)Platter
H
2

if you are really not using any of the features of ejb 3.1 the answer is simple. but guess your questiion indicates you suspect there are ejb 3.1 conceptes you are benefiting from without being aware of them. one example might be that the container can keep a pool of slsb ready to be used, so that jms and database connections dont have to injected as part of request

Heteropterous answered 21/11, 2012 at 8:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.