3 Replies Latest reply on Jun 12, 2009 6:21 AM by felderr

    PortletStateHolder

    felderr

      We are using Jboss Portal 2.7.1, Portletbridge 1.0.0.CR2, Seam 2.1.1 and Richfaces 3.3.1.

      We did some bulk tests to get a feeling how many users can be handled by the portal. During the tests we found a memory leak in the class
      org.jboss.portletbridge.application.PortletStateHolder!

      The class has an inner static class called LRUMap that holds the state of the Portlets using an identifier that consists of mode, sessionid and an uuid. There are 3 methods that cooperate with the LRUMap:

      public void addWindowState(StateId stateId, PortletWindowState state);
      public PortletWindowState getWindowState(StateId stateId);
      private void removeSessionStates(String scopeId);
      


      For each request a new window state is added. The problem is that the method removeSessionStates is never called. It should be called from the method

      public void valueUnbound(HttpSessionBindingEvent event)
      


      which implements the interface HttpSessionBindingListener, HttpSessionActivationListener

      Now I have 3 questions:
      1) Where is the SessionBindingEvent registered?
      2) What does the class PortletStateHolder do? As I can see it's responsible for holding the state between the different phases in JSF? Is that right?
      3) Can I savely remove the states in a filter after each request processing or is it necessary to hold the state within a session?

      Thanks Rene

        • 1. Re: PortletStateHolder
          wesleyhales

          No need to double post. We are looking at this issue for next release. Thanks for all the debugging!

          • 2. Re: PortletStateHolder
            felderr

            Hi Wesley,

            thanks for your quick response! Can you post the Jira or forum link of the double post?

            I need to fix the problem this week as we go live next wednesday. My solution is to use a PortletFilter that removes all entries in the LRU Map:

            public void doFilter(RenderRequest renderRequest, RenderResponse renderResponse, FilterChain chain) throws IOException, PortletException {
             chain.doFilter(renderRequest, rww);
             PortletStateHolder stateHolder = (PortletStateHolder)config.getPortletContext().getAttribute(PortletStateHolder.class.getName());
             stateHolder.cleanLRUMap();
            }
            


            Do you see any drawback when clearing the LRUMap after the Render Phase?

            I'm still not clear why the state is hold across requests?

            • 3. Re: PortletStateHolder
              felderr

              Just implemented the filter and the clearing of the states LRUMap. As I can see all the normal Request/Responses work fine. The Problem occurs when using Ajax based Richfaces requests (eg ExtendedDataTable with filtering and sorting):

              javax.faces.FacesException: No saved portlet window state for an id 4c4fd449-3557-426b-8cd5-f864c24692be:view:2cfaa740-1be3-46a4-bef4-28ece52dcd46
              at org.jboss.portletbridge.context.ServletExternalContextImpl.(ServletExternalContextImpl.java:101)

              So now that I know why the states are cached I have a better solution solving the Memory Leak! The PortletStateHolder needs to hold the state as long as one page is doing Ajax based requests. So if the next page is requested PortletStateHolder does not need the old state any more so it can clearly remove it! So when adding a new WindowState we remove the old WindowStates for this session id:

              private void clearExpiredSessionStates(String sessionId) {
               if (states != null && sessionId != null) {
               for (StateId stateid : states.keySet()) {
               if (sessionId.equals(stateid.getScopeId())) {
               states.remove(stateid);
               }
               }
               }
              }
              public void addWindowState(StateId stateId, PortletWindowState state) {
               clearExpiredSessionStates(stateId.getScopeId());
               states.put(stateId, state);
              }
              


              There's one drawback with this solution if there is more than one window open one of the windows get an error when doing Ajax based requests as we only store one state per session.

              Working on a solution to this. Any ideas?