1 2 Previous Next 17 Replies Latest reply on Oct 12, 2011 2:09 PM by freemarket.hkatz.iscs-i.com

    Removing or replacing a contextual bean

    dan.j.allen

      One of the problems I'm running into early on with Web Beans (perhaps conceptually) is how to remove or replace a contextual bean. Consider that I have the following producer bean:


      public
      @Stateless
      class AccountProducerBean implements AccountProducer
      {
          @PersistenceContext EntityManager em;
      
          @Current Identity identity;
      
          public
          @Produces
          @Registered
          @SessionScoped
          User getCurrentUser()
          {
              User user = em.find(User.class, identity.getUsername());
              return user != null ? user : new User();
          }
      }



      The first time I access currentUser the bean is created. However, unless I end the session, there is no way for me to get rid of that object from the session (because it's not really in the HttpSession, like it would have been with Seam, but is rather in Web Bean's session bean store. I want to be able to get rid of that object from the session scope and create a new one, perhaps when the user logs out. Yes, I could kill the session, but that doesn't address situations when I am producing some other session-scoped object that I need to eventually get rid of.


      If this isn't possible in the spec (which it appears it isn't) can we get an extension for this?


      Also, why can't producer methods return null? Is that not a valid use case? Or at least allow them to resolve to null when accessed through an EL resolver (where null can mean it just doesn't exist and that part of the page shouldn't be displayed).

        • 1. Re: Removing or replacing a contextual bean
          nickarls

          The remove would be handy, yes. In Seam I sometimes just clear the instance from the context to get a fresh instance from a factory.


          Explicit set would be handy for outjection-like stuff but it was probably considered a no-no to have session scoped beans end up in application and request scopes.


          The spec mentions This restriction allows the container to use a client proxy regarding the null producer methods products...

          • 2. Re: Removing or replacing a contextual bean
            dan.j.allen

            I just can't seem to wrap my head around this. I figured out one way to remove a bean (as opposed to a producer method result):


            Set<Bean<User>> candidates = manager.resolveByType(HotelSearch.class);
            assert candidates.size() == 1;
            candidates.iterator().next().destroy(manager.getInstanceByType(HotelSearch.class));



            However, that is just downright ugly and I don't even think it is allowed.


            Now producer methods can have a corresponding dispose method, which would be called by destroy if the type is the result of a @Producer method. The problem there is that the dispose method just receives the instance it is disposing...so you can act on it (such as close a connection if it's an EntityManager) but you can remove it from the context.


            So I'm having a tremendously difficult time with sticky state.

            • 3. Re: Removing or replacing a contextual bean
              nickarls

              Just add a remove method in AbstractMapContext, noone will notice ;-)

              • 4. Re: Removing or replacing a contextual bean
                gonorrhea

                Dan Allen wrote on May 02, 2009 07:32:


                So I'm having a tremendously difficult time with sticky state.


                That sounds foreboding, coming from the SiA author...


                Sounds like something potentially simple/basic was overlooked in the spec/RI???

                • 5. Re: Removing or replacing a contextual bean
                  dan.j.allen

                  I won't say that. Likely just me just being think-headed. I like to say that may head holds stuff in good, but sometimes it takes a while for it to seep through the membrane ;)

                  • 6. Re: Removing or replacing a contextual bean
                    nickarls

                    I don't think the spec actually forbids removing stuff from context.


                    And even if it would, it's not final yet ;-)

                    • 7. Re: Removing or replacing a contextual bean
                      dan.j.allen

                      I'm pretty sure that the spec is discouraging the explicit removal of a bean from the contexts. I'm personally having a hard time with this. I really don't see what the problem is. Once you remove it, then the next time you request it, the bean is simply resolved/instantiated again.

                      • 8. Re: Removing or replacing a contextual bean
                        dan.j.allen

                        So what I am saying is that there is a distinct difference between destroy and remove. Destroy let's you terminate the state of a bean. Remove gets it out of the context to make room for a new instance to move in.

                        • 9. Re: Removing or replacing a contextual bean
                          dan.j.allen

                          I want to emphasize that where this becomes a problem is on a JSF postback. Let's say you produce a value into the request scope. If JSF happens to retrieve the value while walking the tree before the Invoke Application phase, and your action changes the value that backs the producer, then when you render, you see the old value rather than the new value (a stale value). This was a case in Seam where outjection proved useful because it could update the variable with the new value. Not that we need outjection, but just so you understand what it was doing in this case. There is something more fine-grained than request scope.


                          If I introduce the Web Beans API, I can achieve this functionality using the following call:


                          Context requestContext = manager.getContext(RequestScoped.class);
                          ((AbstractThreadLocalMapContext) requestContext).getBeanStore().remove(manager.resolveByName("results").iterator().next());




                          • 10. Re: Removing or replacing a contextual bean
                            nickarls

                            Actually, BeanStore.remove() isn't really used anywhere and should, as such, be removed ;-)

                            • 11. Re: Removing or replacing a contextual bean
                              gavin.king

                              If you want to remove something from a context before the context ends, then the scope  you defined was obviously incorrect :-)


                              Make it an attribute of an object, not a first-level contextal object.

                              • 12. Re: Removing or replacing a contextual bean
                                dan.j.allen

                                You are making these scopes like Hotel California. I don't agree that just because I want to remove an object from a context that I have chosen the wrong scope. Then longer term scopes, especially, can grow without bounds. There are plenty of cases when you need to get an object out of a scope without killing the whole scope. And it greatly hinders the feasibility of using first-level context objects, which was a pattern Seam really espoused.


                                I want to emphasize two cases here so that we have an architectural problem to discuss.


                                As you know, JSF walks the hell out of the tree on a postback, enough that it's very likely it is going to resolve your bean before the Invoke Application phase. Now, if that bean is created by a request-scoped producer, then you are stuck with that value until the page finishes rendering, even if the intent of the action was to change that value. Let's say the current user is managed by a session-scoped bean, but assigned as a first-level context object by a request-scoped producer. At first, you are a guest. Then you login. Now even request scope is the incorrect scope. The alternative is a dependent-scoped producer, but then the method gets abused by JSF. You could also do #{currentUser.user} but we've been spoiled into just having #{currentUser}. I just want to stick currentUser in the session and clear it when I want a different one in the session.


                                Another case is a conversation-scoped search. The results are produced into conversation-scope. To update them, you have to clear the list and use addAll() to repopulate the result list. That seems like a workaround to me for not being about to clear and output a new result list.


                                Frankly, if you can't remove values from contexts, I anticipate that people are going to fear using these contexts because of their stickiness and go with a more stateless approach.


                                • 13. Re: Removing or replacing a contextual bean
                                  dan.j.allen

                                  As I try to play Devil's advocate on myself, I guess the answer is that you always use manager components...and the manager components can dump their state and reestablish as needed. Then, you never produce anything into a long-term scope...otherwise it is out there in the open unmanaged and no way to clear it. I just happen to not like the idea of having to bid farewell to that approach.

                                  • 14. Re: Removing or replacing a contextual bean
                                    gonorrhea

                                    Along the same lines, what about promoting or demoting context variables into different scopes (e.g. conversation-scoped variable promoted to session-scope, etc.)?


                                    I'm not sure that's currently possible with Seam 2.x...

                                    1 2 Previous Next