11 Replies Latest reply on Dec 6, 2013 3:47 AM by mkouba

    Weld : How to make injected objects with Instance<> being garbage-collected

    rde_perigee

      My question will be quite simple : How to avoid JBoss Weld (1.1.5) to keep references to objects produced by Instance ? Typically, the following code :

      class B{}


      class A {

           @Inject

           private Instance<B> instanceB;

       

           public B produce(){ return instanceB.get();

      }

      If I call A#produce(), then the B object won't be garbage-collected before the A object is garbage collected.

      Is there any workaround to make it possible for the B object to be garbage-collected, without the A object been garbage-collected ?

        • 1. Re: Weld : How to make injected objects with Instance<> being garbage-collected
          jharting

          You can call Instance.destroy() but this method is only available since CDI 1.1 (Weld 2.0)

          • 2. Re: Weld : How to make injected objects with Instance<> being garbage-collected
            mkouba

            Or (if possible) change the scope of B to some normal scope. Notice that this behaviour is per the spec (see http://docs.jboss.org/cdi/spec/1.1/cdi-spec.html#dependent_objects and https://issues.jboss.org/browse/WELD-920 for some more information).

            • 3. Re: Weld : How to make injected objects with Instance<> being garbage-collected
              rde_perigee

              @Martin :

              My B instances have default scope, so it won't work for me.

               

              @Jozef :

              I'm currently trying to migrate to CDI 1.1 (Weld 2.1.0.Final)

              • 4. Re: Weld : How to make injected objects with Instance<> being garbage-collected
                rde_perigee

                Reading weld documentation, I changed my mind. It seems the scopes could be  what I was looking for. I need to dig this idea to confirm it can be useful in my case.

                 

                If it does not work, then I'll migrate to CDI 1.1.

                • 5. Re: Weld : How to make injected objects with Instance<> being garbage-collected
                  rde_perigee

                  Ok, what I was looking for is something like @ConversationScoped. But I'm in a Java SE application, so I have no access to context the @ConversationScoped needs : When calling Conversation#begin(), I get :

                  org.jboss.weld.context.ContextNotActiveException: WELD-001303 No active contexts for scope type javax.enterprise.context.RequestScoped

                   

                  It looks like this scope must be used in a JSF context.

                   

                  Is there any mean for me to use this scope in any other way ?

                  • 6. Re: Weld : How to make injected objects with Instance<> being garbage-collected
                    mkouba

                    Yep, the conversation scope is only active during Servlet (CDI 1.1) and JSF (CDI 1.0) requests. You can't use it in Java SE easily.

                    • 7. Re: Weld : How to make injected objects with Instance<> being garbage-collected
                      rde_perigee

                      I've changed my mind. I'm pretty sure implementing a custom scope here is exactly what fits to my situation. That's what I have begun to do.

                       

                      I have my own scope and my own Context. Just have to activate it at the right place. That's what is blocking me right now :

                       

                      @MyScope

                      class B{}


                      class MyContext extends AbstractBoundContext<Map<String, Object>>{

                           private static final DEWeldContext instance = new DEWeldContext();

                        

                           public static DEWeldContext getInstance() {

                                  return instance;

                           }

                       

                           ...

                      }


                      class A {

                       

                           @Inject

                           private Instance<B> instanceB;

                       

                           public B doSomethingWithContext(){

                                          // Activate weld                   

                                          final WeldContainer weld = new Weld().initialize();

                                          ((BeanManagerImpl) weld.getBeanManager()).addContext(MyContext.getInstance());


                                          // Activate context

                                          Map<String, Object> beanStore = new LinkedHashMap<String, Object>();

                                          MyContext.getInstance().associate(beanStore);

                                          MyContext.getInstance().activate();

                       

                                          B b = instanceB.get();

                                          doSomethingElseInScopeOfMyContext();

                                       

                                          // Here, MyContext.getInstance().getBeanStore() is empty ! b is in @Dependent scope context.

                                          MyContext.getInstance().invalidate();

                                          MyContext.getInstance().deactivate();

                                          MyContext.getInstance().dissociate(beanStore);

                           }

                      }

                       

                       

                      Reading my code, do you see something I may do wrong ? The question is : Why is b handled by @Dependent scope context, instead of @MyScope scope context ?

                      • 8. Re: Weld : How to make injected objects with Instance<> being garbage-collected
                        mkouba

                        You have to register your context properly. "((BeanManagerImpl) weld.getBeanManager()).addContext(MyContext.getInstance())" is not a good way to go.

                         

                        Briefly: create a portable extension (see the Weld reference guide) and add AfterBeanDiscovery observer (see also http://docs.jboss.org/cdi/spec/1.1/cdi-spec.html#abd).

                        • 9. Re: Weld : How to make injected objects with Instance<> being garbage-collected
                          rde_perigee

                          I followed your suggestion :

                           

                          public class MyExtension implements Extension {

                           

                            public void afterBeanDiscovery(@Observes AfterBeanDiscovery abd) {

                                 abd.addContext(MyContext.getInstance());

                                 logger.log("Added MyContext")

                             }

                          }

                           

                          and created a file in my resources referencing this extension, as described in the documentation (src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension) containing the fully qualified name of my extension.

                           

                          At the application starting, I can see the log message I put in the afterBeanDiscovery.

                           

                          But my b is still handled by @Dependent scope...

                           

                          Do you have another idea ?

                           

                          Edit : I have a @New annotation on my instanceB. Could it be responsible for such a problem ?

                          • 10. Re: Weld : How to make injected objects with Instance<> being garbage-collected
                            rde_perigee

                            Reading the documentation, I realized @New annotation only injects dependent beans. That' s why my scope never holds reference to injectd beans.

                             

                            I have seen I made a mistake on understanding the purpose of Instance<>. I thought it could be used as "producer" fields (i.e. could inject a new object at each call to get()). But it is not the case. However, that's what I'm trying to do. I need class attributes able to inject new beans at each call, and these beans should be in the scope I defined.

                            • 11. Re: Weld : How to make injected objects with Instance<> being garbage-collected
                              mkouba

                              Well, built-in Instance bean is only used to programatically lookup bean instances (an alternative to @Inject) and new bean instance is not created if not necessary. On the other hand producer (field, method) is a type of a bean - defines how the bean instance is created, and can be bound to any scope.

                               

                              Speaking of your requirement and built-in scopes - only @Dependent beans can be used to produce a new bean instance for each Instance.get(). Other scopes are normal and so there may be only one bean instance per bean per thread (see also 6.3 Normal scopes and pseudo-scopes). Also note that for normal scoped bean a proxy is always created.

                               

                              You can also make @MyScope a pseudo-scope and return a new instance each time Context.get() is invoked.