6 Replies Latest reply on Apr 1, 2010 10:59 PM by jportway.josh.stain.org

    Singleton EJBs

    jportway.josh.stain.org

      Hi,


      I’ve read all that I can find about Singletons and CDI and I’m afraid I’m still very confused. I have some EJB singletons in my app, injected using @Inject into other EJBs (some of them also singletons).


      I’m seeing multiple instances of my singleton classes being instantiated - though mysteriously my app seems to be working ok. ie. even though when I log the constructor of the singleton I see it invoked 4 times I’m still seeing the same state in the Singleton everywhere in the app, so it looks like the same object is actually being injected everywhere (or maybe state is being magically copied around??? - I’m mystified and overawed by the magical proxying and strangeness that must be going on inside weld)


      I know this is also probably a very irritating question, but I’m also still a little unsure whether using a “raw” @Inject is the right thing to do for local EJBs - or am I supposed to use a producer method to declare resources even for local EJBs?


      thanks,
      Josh

        • 1. Re: Singleton EJBs
          jportway.josh.stain.org

          One thing I forgot to mention - if I annotate the Singleton EJBs with @ApplicationScoped then some of them (not all of them) throw errors saying that they are Normal Scoped beans that can’t be proxied. The classes aren’t serializable, but since they’re singletons I don’t think they have to be - and in any case I have other non-serializable classes that don’t throw the same error. My best guess at the difference is that the beans that DO seem to accept the ApplicationScoped annotation are local NO-interface beans, and the ones that throw the error have an explicitly declared local interface.


          I’ve seen several examples of code that use both @Singleton and @ApplicationScoped annotations on the same bean - I’m not sure what difference having both annotations makes, since a singleton EJB is already application scoped in its nature. Can anyone explain?

          • 2. Re: Singleton EJBs
            chasetec
            When you use @Inject to obtain a reference to a bean, in most cases you get a proxy object. I'd guess that the  proxy object is sub-classing your bean and overriding the methods. The EJB-bits (EJBObject & stub) *might* be doing something similar. So you're seeing you constructor called because these subclasses are calling super(). When you leave off @ApplicationScoped your are using the dependent pseudo-scope which doesn't use CDI proxies.

            If you add the @ApplicationScoped annotation to your EJB or producers (if you have them) then you are asking for a CDI proxy to be injected at your @Inject injection points. The @ApplicationScoped proxies maintain reference to the beans in a shared application context. However when your CDI managed bean is actually an EJB then the thing in the application scope is really an EJB stub. This is a huge deal when injection something like a stateful session bean in a request scope for a servlet but it ends up being kind of silly when you've got your case, a stub to a singleton ejb stored in a shared application scope.

            Adding @ApplicationScoped does impact some other things like the life-cycle of your producers and such.

            You might as well leave off the @ApplicationScoped annotation from the singleton bean. Be careful about adding @ApplicationScoped and not @Singleton because then you wouldn't have an EJB and would lose container managed transactions and concurrency.

            As for the error you only see when you add @ApplicationScoped to your bean. Look in the CDI spec for the rules about unproxyable beans. You have to make sure you don't have "classes which don't have a non-private constructor with no parameters" and "classes which are declared final or have final methods". But honestly sometimes I've seen the error go away when I clean and build my project or restart my server. Comment out the content of the class until you get the error to go away.

            Using @Inject is better than using @EJB on the injection points. Whether you need a producer method or not depends on the complexity of your initialization.
            • 3. Re: Singleton EJBs
              jportway.josh.stain.org

              Thanks Matthieu,


              Your reply explains a lot of things I hadn't quite got. I didn't realise that adding ApplicationScoped would be the trigger which would cause a bean to be proxied. If Weld is creating a subclassed proxy, as you say, then it makes sense that I would get a second instantiation  when Weld tried to inject the EJB. Presumably the app container creates the real EJB singleton at sone point, then when I try to inject it Weld is creating a subclassed instance in order to make the proxy. This is an odd situation though, because I still have multiple instances of the singleton class - if the constructor allocated any resources, for instance, they would be allocated multiple times. There should at least be a warning about this somewhere in the docs.


              A simple guide on how to instantiate and inject singleton EJBs with Weld would be very useful. The Weld reference seems to always use @applicationScoped in conjunction with @singleton, but doesn't jeally explain why, or what effect it has. There's also the question of the mysterious javax.inject.singleton annotation and what that's supposed to be for...


              • 4. Re: Singleton EJBs
                jportway.josh.stain.org

                Well, I did a bit more investigation. Firstly, Glassfish itself seems to make 2 instances of the singleton as it starts up the app:
                • the first is created by a method called AbstractSingletonContainer.initializeHome which calls BaseContainer.instantiateOptionalEJBLocalBusinessObjectImpl


                • the second instance is created by SingletonLifeCycleManager.initializeSingleton which xauses AbstractSingletonContainer.createSingletonEJB to be called.


                So - at this point 2 instances have been created, but Weld hasn't done anything. After this the behaviour is different depending whether I annotated with ApplicationScoped or not. when Weld has to inject the Singleton then :


                • if I DID annotate the bean as ApplicationScoped then 2 more instances are created by Weld : the first by weld.util.Proxies.createProxy and the second by weld.bean.SessionBean.create


                • if I DONT annotate with applicationScoped then only 1 instance is created by weld - by SessionBean.create


                Should I just try to relax about all this and just Trust The Magic ?
                My app does seem to be working - I guess it's just important to remember not to allocate resources in the constructor of Singleton beans.


                • 5. Re: Singleton EJBs
                  chasetec
                  Right, an easier thing to do beside dig through the app server/weld code is just to add a line in your constructor that does:
                     System.out.println("I just built a: " + this.getClass().getName());

                  Just use a @PostConstruct method in your bean to take care of all the initialization and you'll be okay.
                  • 6. Re: Singleton EJBs
                    jportway.josh.stain.org

                    God no - I didn't root through the glassfish and Weld code that quickly! I just logged stack traces every time my object was constructed.


                    Yes - postConstruct seems to be the place to do any actual work - either I missed it or this isn't very well explained in the EJB docs though.


                    Thanks again,


                    josh