10 Replies Latest reply on Aug 13, 2008 4:05 PM by bek816

    RichFaces caching of view roots

    bek816

      (cross posted on the development forum)

      Further to my issue below with duplicate id exceptions (http://jboss.com/index.html?module=bb&op=viewtopic&t=140132), I have some questions regarding RichFaces restoreView and createView methods. My question is this: how does RichFaces decide to call restoreView or createView in the view handler?

      Example: I am on page "A" and I navigate to page "B".

      Without RichFaces: restoreView is called for page "A" and createView for page "B". This pattern is consistent. The page that we're leaving is restored and the page we're going to is created.

      RichFaces behaviour: Like the non-RichFaces behaviour, restoreView is called on page "A" (the page I am leaving), but the behaviour differs for page "B". If this is my first time going to the page, createView is called. If I have already been to the page, restoreView is called.

      The implication that we discovered is that RichFaces seems to be caching old view roots. This explains a few things:

      1) The duplicate id exceptions I was encountering. It always bugged me that I would only encounter the exception the second time the page loaded. If there truly was a duplicate id, it shouldn't render at all.

      2) Increased memory consumption. We started seeing out of heap space exceptions. No wonder - RichFaces is "caching" every view root!

      3) A duplicate id exception when a different section is rendered, for example:

      <h:outputText id="myId" rendered="{my condition}" />
      <h:outputText id="myId" rendered="{not my condition}" />

      In the case above, when the condition switches from true to false, RichFaces would throw a duplicate id exception. At the time I wondered why because only one would ever be in the view root, but now given RichFaces view root "caching", it makes sense!

      Questions:

      1) Why is RichFaces holding onto and restoring old view roots rather than always creating new?
      2) What are the performance and memory impacts? We've already had to bump up our heap size.
      3) How long does RichFaces hold on to the view roots for? When do they "expire"?
      4) Is this a memory leak?
      5) Can this be configured?

      We are using RichFaces 3.1.4

        • 1. Re: RichFaces caching of view roots
          bek816

          Can anyone explain to me how/why RichFaces stores so many view roots? Not only does this require a hefty amount of memory, it affects the way create and restore view are called.

          • 2. Re: RichFaces caching of view roots

             

            No wonder - RichFaces is "caching" every view root!



            RichFaces DOES NOT store (cach) views. It works over the feature of the used JSF implementation.


            3) A duplicate id exception when a different section is rendered, for example:

            <h:outputText id="myId" rendered="{ my condition }" />
            <h:outputText id="myId" rendered="{ not my condition }" />


            JSF stores the component in the component tree regardless the condition of rendered attribute. I.e. both lines must be there. As far as component tree does not allow two components with the same id, your code is incorrect by definition.

            • 3. Re: RichFaces caching of view roots
              bek816

              I'd really like to understand why prior to us adding RichFaces to our frameworks, whatever calls the ViewHandler would always go through CreateView for the page we're navigating to, whereas when we added RichFaces, it always go through RestoreView (assuming we've been to that page at least once). Is there anything we can do to change this so that we have the same behaviour as we did before adding RichFaces to the mix? Or this a "standard feature"?

              Furthermore, is there any way to limit the memory footprint. Maybe "caching" isn't the best term, but whatever you want to call it, there a lot of ViewRoots left in session.

              Thanks.

              • 4. Re: RichFaces caching of view roots

                RichFaces DOES NOT store, cache, left in session (or whatever term you use) ViewRoots.

                • 5. Re: RichFaces caching of view roots
                  bek816

                  Ok, but I'm still struggling to understand the behaviour that I'm seeing. Why, prior to us adding RichFaces, did whatever call the ViewHandler always go through CreateView rather than RestoreView (as it's doing now). This is the root of all of our problems. I wouldn't have encountered any duplicate id exceptions if the view root was being created from scratch each time.

                  • 6. Re: RichFaces caching of view roots

                    If it is possible to create a small war with example that reproduces this behavior, do it. So far, we have no idea what is going on in your concrete environment.

                    • 7. Re: RichFaces caching of view roots
                      bek816

                      Hi SergeySmirnov,

                      Thanks for your reply. We might be able to create war files, but I'll start by explaining the behaviour that I'm seeing. I stepped through the code and located the difference.

                      To recap, we have two environments:

                      1) Application Maintenance (AM): MyFaces 1.1.4, Tomahawk 1.1.2, Ajax4JSF, JSPs, etc.
                      2) Developments: The same as above, but RichFaces 3.1.4 instead of A4J.

                      I started by putting a breakpoint in org.apache.myfaces.lifecycle.LifecylcleImpl in the restore view method:

                      UIViewRoot viewRoot = viewHandler.restoreView(facesContext, viewId);
                      if (viewRoot == null)
                      {
                       viewRoot = viewHandler.createView(facesContext, viewId);
                       viewRoot.setViewId(viewId);
                       facesContext.renderResponse();
                       //viewCreated = true;
                      }
                      


                      Note that this breakpoint on UIViewRoot viewRoot will be hit twice: first for the page that we're leaving and again for the page that we're going to. I am interested in the page we're going to (assuming that I have already been to this page at least once).

                      For the page that we're going to, it goes into the AjaxViewHandler.restoreView. From there, it bounces around a bit, but ends up in the JspViewHandlerImpl:

                      public UIViewRoot restoreView(FacesContext facesContext, String viewId)
                       {
                       Application application = facesContext.getApplication();
                       ViewHandler applicationViewHandler = application.getViewHandler();
                       String renderKitId = applicationViewHandler.calculateRenderKitId(facesContext);
                       UIViewRoot viewRoot = application.getStateManager().restoreView(facesContext,
                       viewId,
                       renderKitId);
                       return viewRoot;
                       }
                      


                      Note the call to the StateManager. In our AM environment, the StateManager is a JspStateManagerImpl, but in our Dev environment, the StateManager is a AjaxStateManager.

                      Here is where the two environments differ. Note the AjaxStateManager RestoreView code:
                      public UIViewRoot restoreView(FacesContext context, String viewId,
                       String renderKitId) {
                       UIViewRoot viewRoot = null;
                       ResponseStateManager responseStateManager = getRenderKit(context,
                       renderKitId).getResponseStateManager();
                       TreeStrutureNode treeStructure = null;
                       Object[] state = null;
                       if (isSavingStateInClient(context)) {
                       ....
                       } else {
                       Object[] serializedView = restoreStateFromSession(context, viewId,
                       renderKitId);
                       if (null != serializedView) {
                       treeStructure = (TreeStrutureNode) serializedView[0];
                       state = (Object[]) serializedView[1];
                       }
                       }
                       if (null != treeStructure) {
                       viewRoot = (UIViewRoot) treeStructure.restore(componentLoader);
                       if (null != viewRoot && null != state) {
                       viewRoot.processRestoreState(context, state[0]);
                       restoreAdditionalState(context, state[1]);
                       }
                       }
                       return viewRoot;
                      
                       }


                      In this case (we have server state saving enabled), it successfully restores the view from session and ultimately returns a view root to the lifecycle.

                      In our AM environment, we're seeing different behaviour:

                      From the Lifecycle it goes to AjaxViewHandler.restoreView, to JspViewHandlerImpl, to JspStateManagerImpl.restoreView. Note that the JspStateManagerImpl is used in place of the AjaxStateManager (which does not exist in A4J).

                      The code for the JspStateManagerImpl is as follows:

                      public UIViewRoot restoreView(FacesContext facescontext, String viewId, String renderKitId)
                       {
                       UIViewRoot uiViewRoot = restoreTreeStructure(facescontext, viewId, renderKitId);
                       if (uiViewRoot != null)
                       {
                       uiViewRoot.setViewId(viewId);
                       restoreComponentState(facescontext, uiViewRoot, renderKitId);
                       String restoredViewId = uiViewRoot.getViewId();
                       if (restoredViewId == null || !(restoredViewId.equals(viewId)))
                       {
                       if (log.isTraceEnabled()) log.trace("Exiting restoreView - restored view is null.");
                       return null;
                       }
                       }
                      
                       return uiViewRoot;
                       }


                      Inside RestoreTreeStructure, the following code is called:

                      SerializedView serializedView = getSerializedViewFromServletSession(facesContext, viewId);
                      


                      For the page that I'm going to, this always returns null. It's never retrieving the serialized view from session. This has an effect going back to the lifecycle:

                      UIViewRoot viewRoot = viewHandler.restoreView(facesContext, viewId);
                      if (viewRoot == null)
                      {
                       viewRoot = viewHandler.createView(facesContext, viewId);
                       viewRoot.setViewId(viewId);
                       facesContext.renderResponse();
                       //viewCreated = true;
                      }
                      


                      Because the JspStateManager is never retrieving view roots, it's always creating them. This is in contrast to RichFaces where the ViewRoot is not null.

                      This is what we're seeing. In our AM environment (no RichFaces), we're always creating new view roots on the pages we're navigating to. With RichFaces in our Dev environment they're retrieved from session and restored. Can you shed any light on this behaviour?

                      Thanks.

                      • 8. Re: RichFaces caching of view roots

                        Still no rational explanation why it work not like it should in JSF. Needed code to be debugged

                        • 9. Re: RichFaces caching of view roots
                          bek816

                          Maybe you can help me shed some light on what the expected behaviour should be? I posted the two different samples above, one where view roots on pages we're navigating to are always retrieved from session and one where they are not. Which is correct?

                          • 10. Re: RichFaces caching of view roots
                            bek816

                            What I'm trying to say is that the two state managers behave differently. In one case, the view root is always restored and in the other the view root is always created. What is the "normal" behaviour? What should a state manager be doing?