1 2 3 Previous Next 30 Replies Latest reply on Feb 2, 2007 3:01 PM by brian.stansberry

    Issues with passivation of nested SFSBs

    brian.stansberry

      I'm seeing some issues related to caching and passivation of nested SFSBs (i.e. one SFSB holds a ref to another, i.e. through an @EJB annotation). I'd like to get some input as to what the proper behavior is.

      What I'm seeing is:

      If the nested bean declares an @Remote interface, when the parent bean is being constructed for the nested bean an instance of StatefulBeanContext ends up being cached. This works fine.

      If the nested bean does not declare an @Remote interface, when the parent bean is being constructed, for the nested bean an instance of ProxiedStatefulBeanContext ends up being cached. This leads to problems.

      The problem occurs if the parent bean is later removed. This does not result in a call to remove the child bean from the cache. (I don't know if that's a problem or not; Chapter 11.7 of Bill Burke's book says it should be removed, but I couldn't find a discussion of that in the spec.)

      A replicated ProxiedStatefulBeanContext is useless if its parent is not in the cache. The replication clears the transient reference to the parent bean. If the context is later used, ProxiedStatefulBeanContext.getDelegate() will attempt to look up the parent, which will result in a NoSuchEjbException.

      The problem occurs when the background passivation thread decides to passivate the orphaned ProxiedStatefulBeanContext and calls prePassivate(). You get this:

      javax.ejb.NoSuchEJBException: Could not find Stateful bean: a1g1m-b11vx1-exf6itu4-1-exf6mcqp-1v
       at org.jboss.ejb3.cache.tree.StatefulTreeCache.get(StatefulTreeCache.java:148)
       at org.jboss.ejb3.stateful.StatefulBeanContextReference.getBeanContext(StatefulBeanContextReference.java:72)
       at org.jboss.ejb3.stateful.ProxiedStatefulBeanContext.getDelegate(ProxiedStatefulBeanContext.java:70)
       at org.jboss.ejb3.stateful.ProxiedStatefulBeanContext.getContainer(ProxiedStatefulBeanContext.java:213)
       at org.jboss.ejb3.stateful.StatefulBeanContext.prePassivate(StatefulBeanContext.java:178)
       at org.jboss.ejb3.cache.tree.StatefulTreeCache$ClusteredStatefulCacheListener.nodePassivated(StatefulTreeCache.java:376)
       at org.jboss.cache.notifications.Notifier.notifyNodePassivated(Notifier.java:423)
       ...


      Id a1g1m-b11vx1-exf6itu4-1-exf6mcqp-1v belongs to the parent bean.

      Some solutions I can see are:

      1) If the child bean is meant to be removed along with the parent, fix whatever's preventing that from happening.

      2) Have the StatefulCache impls not cache instances of ProxiedStatefulBeanContext. I don't understand the usage of this class well enough to know whether that's a valid solution or not. I doubt it.

      3) Have the StatefulCache impls catch NoSuchEjbException when they call prePassivate(). Check if the context type is ProxiedStatefulBeanContext; if it is, suppress the exception and remove the useless context from the cache. That's ugly but limited in scope.

      Any comments/suggestions are most appreciated.

        • 1. Re: Issues with passivation of nested SFSBs
          brian.stansberry

          As expected, solution #2 above is not an option; if you don't cache the context the parent bean can't invoke on the child. DOH!

          • 2. Re: Issues with passivation of nested SFSBs
            brian.stansberry

            I implemented solution #3 above for StatefulTreeCache, since normal usage of clustered SFSBs with nested beans will lead to ERRORs in the logs. I think the usage pattern that would lead problems with non-clustered beans is more of an edge case (requires the parent to hand out a ref to the child), so I won't implement it in SimpleStatefulCache without more feedback.

            The callbacks related to activation/passivation are also not handled correctly. See http://jira.jboss.com/jira/browse/EJBTHREE-849.

            The same analysis I applied in EJBTHREE-849 could also apply to the @PreDestroy callback. Currently, o.j.e.test.clusteredsession.ExtendedPersistenceContextUnitTestCase is failing because this callback is not invoked. I'm not sure if the spec requires invoking the @PreDestroy callback in this situation though.

            • 3. Re: Issues with passivation of nested SFSBs
              brian.stansberry

              Tangentially related to this is the issue of ClusteredStatefulCache.replicate(StatefulBeanContext) for a nested SFSB with no remote interface. This method gets invoked on the return side of a clustered SFSB invocation. Again, in this case, the cached context is a proxy. Replicating the proxy here serves little purpose, as it doesn't result in any bean state being replicated.

              Question is, should the *parent context* be replicated in this case? That's what actually holds the child bean's state.

              Replicating the parent is more correct, as it ensures the data is replicated. But it can be inefficient -- i.e. imagine a call on the parent that internally results in calls on 3 child beans. That would result in 4 replications of the parent, when 1 would suffice. It could also cause problems, as 3 of the replications would occur while the parent bean is in the middle of the outer invocation.

              • 4. Re: Issues with passivation of nested SFSBs
                bill.burke

                Brian,

                Nested SFSBs have to share the same lifecycle as their parents. They also have to share the same Extended Persistence Contexts the reference!

                Another thought I had was to actually store the Nested Bean's instance inide of its proxy and propagate it to the Nested Bean's EJB container via the invocation object. If the invocation object had the actual bean instance already set, then the EJB container would not look in the cache.

                I don't remember why I didn't do this....Maybe I didn't think of it at first?

                • 5. Re: Issues with passivation of nested SFSBs
                  brian.stansberry

                   

                  "bill.burke@jboss.com" wrote:

                  Nested SFSBs have to share the same lifecycle as their parents. They also have to share the same Extended Persistence Contexts the reference!


                  OK, good; that's a clear rule that makes deciding how to handle everything else simpler. :)

                  This is broken if the nested SFSB declares @Remote. This test fails:

                  ParentStatefulRemote stateful = (ParentStatefulRemote) ctx.lookup("testParentStateful/remote");
                  
                  assertEquals("Counter: ", 1, stateful.increment());
                  
                  NestedStateful nested = stateful.getNested();
                  
                  stateful.remove();
                  
                  // Confirm nested no longer works following parent remove
                  try
                  {
                   nested.increment();
                   fail("Nested bean still exists following destruction of parent");
                  }
                  catch (Exception good)
                  {
                   // this is what we want -- nested has no independent lifecycle
                  }
                  


                  I'll open a JIRA for this. Hopefully one of you guys can figure it out. I've been digging around trying to understand what's going on. For some reason, if the nested bean declares @Remote, when a StatefulCache calls Pool.get(), it gets back a standard StatefulBeanContext rather ProxiedStatefulBeanContext. This has to be a function of how calls are nested (i.e. the nested bean gets created *before* the parent, thus the parent hasn't been pushed onto the ThreadLocalStack when the nested bean is created.)

                  As for the initial issue of the ProxiedStatefulBeanContext not being removed from the cache when it's parent is, I think I found that -- it was a bug in StatefulTreeCache.

                  Another thought I had was to actually store the Nested Bean's instance inide of its proxy and propagate it to the Nested Bean's EJB container via the invocation object. If the invocation object had the actual bean instance already set, then the EJB container would not look in the cache.

                  I don't remember why I didn't do this....Maybe I didn't think of it at first?


                  Was this thought sparked by my last post re: replicating the proxy? That is, if we store the bean instance in the proxy and replicate it, that solves the issue?

                  If so, I think the reason you didn't do it that way was because you wanted the nested beans to always be serialized as a unit with the parent. Otherwise after deserialization you no longer have shared refs to things like ExtendedPersistenceContext.

                  • 6. Re: Issues with passivation of nested SFSBs
                    brian.stansberry

                    JIRA for broken lifecycle dependency is http://jira.jboss.com/jira/browse/EJBTHREE-854 .

                    • 7. Re: Issues with passivation of nested SFSBs
                      bill.burke

                      @Remote beans are not supposed to be nested. They are separate because they may be located in another server (@Remote).

                      Ok, i got you on the shared refs...I'll put some thought into it.

                      • 8. Re: Issues with passivation of nested SFSBs
                        wolfc

                        I don't get the EJBTHREE-854.

                        The nested.increment() is supposed to work properly because it's just another SLSB. Okay so it was created by another SLSB, so what.

                        Why should any SLSB have a dependant lifecycle?
                        (What goes for @Remote can also go for @Local, if someone returns the SLSB to a servlet for example, which puts it in the HttpSession.)

                        • 9. Re: Issues with passivation of nested SFSBs
                          brian.stansberry

                          OK, if the @Remote case is supposed to work, EJBTHREE-854 should be closed as Rejected. This weekend I originally wrote the unit test the opposite way, to assert that the nested.increment() call worked; got ahead of myself and changed it this morning.

                          Carlo, I agree with you about @Local; that kind of case is what prompted my initial confusion about spec behavior. If the nested bean has a dependent lifecycle, it's very difficult use normal pojo programming with a SFSB.

                          If the nested bean doesn't have a dependent lifecycle, we need to come up with another approach to maintaining shared refs to the XPC.

                          • 10. Re: Issues with passivation of nested SFSBs
                            bill.burke

                            again, nested bean shares lifecycle if it is injected as a @Local reference.

                            • 11. Re: Issues with passivation of nested SFSBs
                              wolfc

                              Again, why share life cycle?

                              What is supposed to happen in the following scenario:

                              SFSB A:

                              @EJB B beanB;
                              getB() { return B };


                              SFSB C:
                              B ref;
                              void setB(A beanA) { ref = beanA.getB(); }
                              void incrementB(B beanB) { ref.increment(); }


                              Client:
                              @EJB A beanA;
                              @EJB C beanC;
                              void iAmDoingSomething() {
                               beanC.setB(beanA);
                               beanA.remove();
                               beanC.incrementB();
                              }


                              I think it should work. So each SFSB has an independent life cycle.
                              (I still don't see the problem why they can't have an independent life cycle.)

                              • 12. Re: Issues with passivation of nested SFSBs
                                brian.stansberry

                                The issue is maintaining shared references to an XPC between the parent and the children across serialization/deserialization (i.e. passivation/activation or replication). This was done by storing the nested bean contexts inside a collection in the parent bean context and only serializing/deserializing the whole thing as a unit. This data structure implies a common lifecycle.

                                • 13. Re: Issues with passivation of nested SFSBs
                                  bill.burke

                                  I swear that the nested @Local SFSB shared the same lifecycle as its parent, but I cannot find it in the spec. I distinctly remember talking about it in the expert group.

                                  IMO, because of managed entities and extended PCs, we should implement the behavior this way, otherwise, it becomes impossible to reallign the object references.

                                  Ineed to reread and rethink the problem based on Brian's clustering problems...

                                  • 14. Re: Issues with passivation of nested SFSBs
                                    bill.burke

                                    Brian....so, if we attach the lifecycle of the child nested bean to that of its parent, will there be no problems?

                                    Another thing we could do is if the parent is removed, we can replace the Proxied with the real thing.

                                    It seems to me though that if you don't tie the lifecycle of the child to the parent, then if this usebase becomes prevalent, there could could quite possibly be orphaned beans.

                                    1 2 3 Previous Next