JSF Session Fail over and Partial State Saving
Asked Answered
E

2

13

Running on JSF 2.0.9, Weblogic 10.3.4. We're now running JSF in our production environment but have encountered some issues with Session Replication and fail over. We are using viewscope for our beans and I have ensured they are Serializable/transient and that the transient variables are effectively stateless. However session fail over is not working. I have done extensive testing and managed to get it working by setting the following params in web.xml

    <context-param>
        <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
        <param-value>client</param-value>
    </context-param>


    <context-param>
        <param-name>javax.faces.PARTIAL_STATE_SAVING</param-name>
        <param-value>false</param-value>
    </context-param>

If I set STATE_SAVING_METHOD to server I get a viewexpired exception on failover. If I set to client with PARTIAL_STATE_SAVING to true I get the following error:

java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
    at java.util.ArrayList.RangeCheck(ArrayList.java:547)
    at java.util.ArrayList.get(ArrayList.java:322)
    at javax.faces.component.AttachedObjectListHolder.restoreState(AttachedObjectListHolder.java:165)
    at javax.faces.component.UIComponentBase.restoreState(UIComponentBase.java:1433)
    at com.sun.faces.application.view.StateManagementStrategyImpl$1.visit(StateManagementStrategyImpl.java:265)
    at com.sun.faces.component.visit.FullVisitContext.invokeVisitCallback(FullVisitContext.java:151)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1507)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1521)
    at com.sun.faces.component.visit.VisitUtils.doFullNonIteratingVisit(VisitUtils.java:75)
    at com.sun.faces.application.view.StateManagementStrategyImpl.restoreView(StateManagementStrategyImpl.java:282)
    at com.sun.faces.application.StateManagerImpl.restoreView(StateManagerImpl.java:181)
    at com.sun.faces.application.view.ViewHandlingStrategy.restoreView(ViewHandlingStrategy.java:123)
    at com.sun.faces.application.view.FaceletViewHandlingStrategy.restoreView(FaceletViewHandlingStrategy.java:448)
    at com.sun.faces.application.view.MultiViewHandler.restoreView(MultiViewHandler.java:148)
    at com.sun.faces.lifecycle.RestoreViewPhase.execute(RestoreViewPhase.java:187)
    at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
    at com.sun.faces.lifecycle.RestoreViewPhase.doPhase(RestoreViewPhase.java:111)
    at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:508)
    at weblogic.servlet.internal.StubSecurityHelper$ServletServiceAction.run(StubSecurityHelper.java:227)
    at weblogic.servlet.internal.StubSecurityHelper.invokeServlet(StubSecurityHelper.java:125)
    at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:301)
    at weblogic.servlet.internal.TailFilter.doFilter(TailFilter.java:27)
    at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:57)
    at weblogic.servlet.internal.RequestEventsFilter.doFilter(RequestEventsFilter.java:27)
    at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:57)
    at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.wrapRun(WebAppServletContext.java:3730)
    at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.run(WebAppServletContext.java:3696)
    at weblogic.security.acl.internal.AuthenticatedSubject.doAs(AuthenticatedSubject.java:321)
    at weblogic.security.service.SecurityManager.runAs(SecurityManager.java:120)
    at weblogic.servlet.internal.WebAppServletContext.securedExecute(WebAppServletContext.java:2273)
    at weblogic.servlet.internal.WebAppServletContext.execute(WebAppServletContext.java:2179)
    at weblogic.servlet.internal.ServletRequestImpl.run(ServletRequestImpl.java:1490)
    at weblogic.work.ExecuteThread.execute(ExecuteThread.java:256)
    at weblogic.work.ExecuteThread.run(ExecuteThread.java:221)

So my questions are these:

  • Is STATE_SAVING_METHOD - client & PARTIAL_STATE_SAVING - false the only way I'm going to get failover to work?
  • What is the cost of client/false combination. Is this memory/cpu extensive?
  • Is this a bug and if so is it resolved in 2.1 or 2.2?

Thanks in advance.

Eldwin answered 23/8, 2012 at 14:35 Comment(1)
Unfortunately this bug has been around for a long time. It has been pointed out to the developers on multiple occasions as evident from here: github.com/javaserverfaces/mojarra/issues/3245 Basically AttachedObjectListHolder.restoreState is written with based on a false assumption and the developers refuse to fix it. Instead they insist on you writing your code in a different wayLuau
E
14

I finally got this working but not without some extra bits and bobs. Firstly I added the following to the web.xml (yes agressive is spelled incorrectly):

    <context-param>
        <param-name>com.sun.faces.enableAgressiveSessionDirtying</param-name>
        <param-value>true</param-value>
    </context-param>

The client saving is now server and partial-state saving is false still (true simply doesn't work)

Secondly after implementing a HttpSessionAttributeListener I discovered that the com.sun.faces.renderkit.ServerSideStateHelper.LogicalViewMap which holds the state in the session only got added once and never removed/added/replaced again. Therefore although it was being updated in the local session these changes were never replicated to a second jvm. Weblogic docs state that setAttribute must be called on session attributes for replication to work. To fix this I created a phase listener as follows:

public class ViewPhaseListener implements PhaseListener {

    public void afterPhase(PhaseEvent phaseEvent)
    {

    }

    public void beforePhase(PhaseEvent phaseEvent)
    {
        HttpServletRequest request = ((HttpServletRequest) phaseEvent.getFacesContext().getExternalContext().getRequest());
        HttpSession session = request.getSession();

        session.setAttribute("com.sun.faces.renderkit.ServerSideStateHelper.LogicalViewMap", session.getAttribute("com.sun.faces.renderkit.ServerSideStateHelper.LogicalViewMap"));

    }

    public PhaseId getPhaseId()
    {
        return PhaseId.RENDER_RESPONSE;
          //To change body of implemented methods use File | Settings | File Templates.
    }
}

This simply replaces the attribute after each request and ensures it replicates. As an additional point I am limiting the data stored in the views with the following:

<context-param>
        <param-name>com.sun.faces.numberOfViewsInSession</param-name>
        <param-value>3</param-value>
    </context-param>

    <context-param>
        <param-name>com.sun.faces.numberOfLogicalViews</param-name>
        <param-value>1</param-value>
    </context-param>

Hope this helps anyone with the same issues.

Eldwin answered 12/4, 2013 at 9:40 Comment(1)
Awesome, thanks! I did all that and it totally didn't work! :PHumfrid
R
0

The session replication should be handled by the load balancer as the JSF application is only aware of the context on the node that it is deployed to. As such, it shouldn't matter if you set the STATE_SAVING_METHOD to client or server.

It sounds to me like you have an incorrect load balancer configuration. If you're using Apache HTTP Server as your proxy please see the following link for more information on session stickyness:

http://httpd.apache.org/docs/2.2/mod/mod_proxy_balancer.html

I would recommend setting ProxySet stickysession=ROUTEID and see if that resolves the issue.

Reames answered 6/9, 2012 at 17:25 Comment(3)
We have IIS. Session replication is handled by weblogic and not the load balancer. Sticky Sessions is on but this isn't the issue. The issue is the failure of the session to fail over correctly to a new jvm.Eldwin
If you have set sticky session on the weblogic server, the weblogic server will create a cookie with the user session information on that node only. My guess is that this cookie is not passed on to the second node when a failover occur since the weblogic instance is only aware of itself. That's why I said that you should try and turn on sticky sessions on the Load Balancer because the load balancer is able to pass on the cookie to the second node.Reames
Thanks but I don't think I was clear in my initial question. Session replication is happening but just not correctly with the JSF sessions. Only part of our app uses JSF at the mo and non jsf sessions are replicating ok.Eldwin

© 2022 - 2024 — McMap. All rights reserved.