4

This is mostly about understanding the differences when injecting a stateful EJB (SFSB) with @Inject compared to injecting it with @EJB.

One of the main differences ought to be the contextual awareness when injecting via @Inject. So my assumption was that if I create two @RequestScoped beans and inject in each an SFSB twice (once with @Inject, once with @EJB), the SFSB injected via @Inject would be the same instance in both @RequestScoped beans, whereas the ones injected via @EJB would be different instances.

This assumption seems to be wrong, but I do not understand why. Shouldn't CDI be aware of the fact that both beans are @RequestScoped and therefore inject the same SFSB? Why is this not so, or is my test code somewhat flawed?

This is my SFSB and its interface:

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.Local;
import javax.ejb.PrePassivate;
import javax.ejb.Stateful;

import package.MyStateful;

@Stateful
@Local(MyStateful.class)
public class MyStatefulImpl implements MyStateful {

    @PostConstruct
    private void postConstruct() {
            System.out.println("SFSB postconstruct ref: " + this.toString());
    }

    @PreDestroy
    private void preDestroy() {
            System.out.println("SFSB predestroy ref: " + this.toString());
    }

    @PrePassivate
    private void prePassivate() {
            System.out.println("SFSB prepassivate ref: " + this.toString());
    }

    @Override
    public String myToString() {
            return toString();
    }
}

public interface MyStateful {
    String myToString();
}

And this is one @RequestScoped bean:

import javax.ejb.EJB;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named;

import package.MyStateful;

@Named
@RequestScoped
public class MyFirstRequestScoped {

    @Inject
    MyStateful myStatefulByCDI;

    @EJB
    MyStateful myStatefulByEJB;

    public MyStateful getMyStatefulByCDI() {
            System.out.println("first#myStatefulByCDI proxy ref: " + myStatefulByCDI.toString());
            System.out.println("first#myStatefulByCDI stateful ref: " + myStatefulByCDI.myToString());
            return myStatefulByCDI;
    }

    public MyStateful getMyStatefulByEJB() {
            System.out.println("first#myStatefulByEJB proxy ref: " + myStatefulByEJB.toString());
            System.out.println("first#myStatefulByEJB stateful ref: " + myStatefulByEJB.myToString());
            return myStatefulByEJB;
    }

}

There is another @RequestScoped bean named MySecondRequestScoped with an analoguous implementation.

When these are called from a JSF xhtml page via EL (nothing special, just a <h:outputText value="#{myFirstRequestScoped.myStatefulByCDI}" /> and so on to trigger their creation), this is the console output (WebSphere ApplicationServer 8.5.5.0):

[1/4/14 12:39:11:759 CET] 000000dc SystemOut     O SFSB postconstruct ref: package.MyStatefulImpl@c03fcdee
[1/4/14 12:39:11:761 CET] 000000dc SystemOut     O SFSB postconstruct ref: package.MyStatefulImpl@36b3bb10
[1/4/14 12:39:11:761 CET] 000000dc SystemOut     O first#myStatefulByCDI proxy ref: package.EJSLocal0SFMyStatefulImpl_8d170245@48da7f98(BeanId(Project#ProjectEJB.jar#MyStatefulImpl, 5D0CBA11-0143-4000-E000-6A007F000001))
[1/4/14 12:39:11:762 CET] 000000dc SystemOut     O first#myStatefulByCDI stateful ref: package.MyStatefulImpl@36b3bb10
[1/4/14 12:39:11:768 CET] 000000dc SystemOut     O SFSB postconstruct ref: package.MyStatefulImpl@9b3971c7
[1/4/14 12:39:11:768 CET] 000000dc SystemOut     O SFSB postconstruct ref: package.MyStatefulImpl@456cec27
[1/4/14 12:39:11:769 CET] 000000dc SystemOut     O second#myStatefulByCDI proxy ref: package.EJSLocal0SFMyStatefulImpl_8d170245@48da7fa1(BeanId(Project#ProjectEJB.jar#MyStatefulImpl, 5D0CBA18-0143-4000-E001-6A007F000001))
[1/4/14 12:39:11:769 CET] 000000dc SystemOut     O second#myStatefulByCDI stateful ref: package.MyStatefulImpl@456cec27
[1/4/14 12:39:11:769 CET] 000000dc SystemOut     O first#myStatefulByEJB proxy ref: package.EJSLocal0SFMyStatefulImpl_8d170245@48da7f9b(BeanId(Project#ProjectEJB.jar#MyStatefulImpl, 5D0CBA0E-0143-4000-E000-6A007F000001))
[1/4/14 12:39:11:769 CET] 000000dc SystemOut     O first#myStatefulByEJB stateful ref: package.MyStatefulImpl@c03fcdee
[1/4/14 12:39:11:769 CET] 000000dc SystemOut     O second#myStatefulByEJB proxy ref: package.EJSLocal0SFMyStatefulImpl_8d170245@48da7fa1(BeanId(Project#ProjectEJB.jar#MyStatefulImpl, 5D0CBA18-0143-4000-E000-6A007F000001))
[1/4/14 12:39:11:770 CET] 000000dc SystemOut     O second#myStatefulByEJB stateful ref: package.MyStatefulImpl@9b3971c7
[1/4/14 12:39:11:848 CET] 000000dc SystemOut     O SFSB predestroy ref: package.MyStatefulImpl@36b3bb10
[1/4/14 12:39:11:849 CET] 000000dc SystemOut     O SFSB predestroy ref: package.MyStatefulImpl@456cec27
[1/4/14 12:50:11:765 CET] 00000120 SystemOut     O SFSB prepassivate ref: package.MyStatefulImpl@c03fcdee
[1/4/14 12:50:11:766 CET] 00000120 SystemOut     O SFSB prepassivate ref: package.MyStatefulImpl@9b3971c7

So it seems that:

  • 4 instances of the SFSB are created; I would have expected this to be just 3. Those injected via @EJB aren't aware of the context, so I would have thought it's ok if they're created for every injection point. But since CDI should be aware of the context (@RequestScoped), I thought CDI would reinject the SFSB already created.
  • The only difference between @Inject and @EJB seems to be here that the life-cycle is automatically managed when injected via CDI - the method annotated @PreDestroy is called for those (36b3bb10 and 456cec27). These injected via @EJB (c03fcdee and 9b3971c7) are later only passivated and don't seem to be destroyed any time later.

The latter seems to be a good reason to use @Inject instead of @EJB, but what I don't understand is what is really meant by the contextual awareness of CDI, when there's a new instance of the SFSB created regardless of the scope?

By the way, this behaves the same when using @SessionScoped beans, even if the second bean is created after following a link to another page (to make sure that the SFSB injected via @Inject definitely exists already). Moreover, the SFSB instances injected via @EJB are created just once for the lifetime of the session, just like the ones injected via @Inject - so these seem to be aware of the context, too, somehow...? When mixing @SessionScoped and @RequestScoped beans, the @SessionScoped bean gets another instance of the SFSB injected than the @RequestScoped bean, which is fine - but which seems not to be a feature of CDI somehow, since this is true for both those instances of the SFSB injected via @Inject as well as for those injected via @EJB.

Edit: Conclusion of the observed behaviour: The only difference between injecting an SFSB via @Inject and @EJB seems to be that in the former case the SFSB is automatically destroyed when the scope is left and in the latter case it's not. Is this correct? This would strike me as odd, since I expected CDI to behave differently...

Any hints about what I'm missing, i.e. misunderstanding when it comes to the "C" in "CDI"? I hope it's not some WebSphere "speciality"...

stef77
  • 1,000
  • 5
  • 19
  • `@EJB` seems to come from a different technology era than CDI. Among other things, it seems to use JNDI for lookup. – Bob Dalgleish Jan 04 '14 at 18:20
  • Sure, `@EJB` has been around for much longer than `CDI`, which is part of Java EE since version 6 (see http://www.oracle.com/technetwork/java/javaee/tech/javaee6technologies-1955512.html). But regrettably, for me that doesn't explain the behaviour I explained above. – stef77 Jan 04 '14 at 18:32

1 Answers1

3

In order for your SFSB to be scoped to the request, you need to give it the @RequestScoped scope. Then you should see the same instance injected. Now since both of these are proxy'd, the easiest way to confirm is to set some value from one bean, and get the value from another bean.

John Ament
  • 11,595
  • 1
  • 36
  • 45
  • Ok, this worked... Checked the toString IDs, so I know it's the same `SFSB`. So it is like **"When injecting an `SFSB` with CDI, in order to get the correct instance of the `SFSB`, the `SFSB` has to have a Scope annotation"?** I find this rather confusing, since when I decide to change my web layer to use another scope (e.g. conversation instead of request since I split up a page and made some process a wizard over multiple pages), I have to change the Scope annotation on my EJB as well? I seem to misunderstand some concept here. How do you use `SFSB`s together with CDI in a best practice way? – stef77 Jan 05 '14 at 19:07
  • Scoping is as appropriate. For the most part, I don't use SFSB, I use stateless in my apps. In those, I don't provide a scope. The stateful nature, if needed, would be in a purely CDI object. – John Ament Jan 06 '14 at 16:36
  • Ay, there's the rub! I saw it the other way round, i thought I'd have an `SFSB` and, as you've put it, if needed, I'd add a scope, i.e. `CDI`. But if I have an already scoped `CDI` bean and add `@Stateful` if I need these capabilities, this sounds more legitimate to me. I wonder if there are many use cases for `SFSB`s, now with `CDI` being around, especially for an application with a web interface, but that's another question. Thank you very much for your answer and explanations! – stef77 Jan 07 '14 at 19:16