2

I have implemented rolling updates with JSF 2.3. I'm using Redis (with the Redisson library) to store HTTP sessions, HAProxy as a load balancer with sticky sessions. Additionally, I am using Tomcat 8.5. When I deploy a new version, I take down the previous version of the app, and traffic is redirected to the new Tomcat with the updated app.

The issue arises when I need to update an XHTML file, for example, to add a new column to a data table. In this case, when the session is transferred from one server to another, I encounter this error:

org.primefaces.application.exceptionhandler.PrimeExceptionHandler.logException 2
    java.lang.ArrayIndexOutOfBoundsException: 2
        at javax.faces.component.UIComponentBase.restoreState(UIComponentBase.java:1245)
        at com.sun.faces.application.view.FaceletPartialStateManagementStrategy$2.visit(FaceletPartialStateManagementStrategy.java:376)
        at com.sun.faces.component.visit.FullVisitContext.invokeVisitCallback(FullVisitContext.java:127)
        at javax.faces.component.UIComponent.visitTree(UIComponent.java:1457)
        at javax.faces.component.UIComponent.visitTree(UIComponent.java:1469)
        at javax.faces.component.UIComponent.visitTree(UIComponent.java:1469)
        at javax.faces.component.UIComponent.visitTree(UIComponent.java:1469)
        at javax.faces.component.UIComponent.visitTree(UIComponent.java:1469)
        at javax.faces.component.UIComponent.visitTree(UIComponent.java:1469)
        at javax.faces.component.UIComponent.visitTree(UIComponent.java:1469)
        at javax.faces.component.UIComponent.visitTree(UIComponent.java:1469)
        at javax.faces.component.UIComponent.visitTree(UIComponent.java:1469)
        at com.sun.faces.application.view.FaceletPartialStateManagementStrategy.restoreView(FaceletPartialStateManagementStrategy.java:362)
        at com.sun.faces.application.StateManagerImpl.restoreView(StateManagerImpl.java:113)
        at com.sun.faces.application.view.ViewHandlingStrategy.restoreView(ViewHandlingStrategy.java:99)
        at com.sun.faces.application.view.FaceletViewHandlingStrategy.restoreView(FaceletViewHandlingStrategy.java:272)
        at com.sun.faces.application.view.MultiViewHandler.restoreView(MultiViewHandler.java:133)
        at javax.faces.application.ViewHandlerWrapper.restoreView(ViewHandlerWrapper.java:101)
        at javax.faces.application.ViewHandlerWrapper.restoreView(ViewHandlerWrapper.java:101)
        at org.omnifaces.viewhandler.OmniViewHandler.restoreView(OmniViewHandler.java:117)
        at com.sun.faces.lifecycle.RestoreViewPhase.execute(RestoreViewPhase.java:181)
        at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:76)
        at com.sun.faces.lifecycle.RestoreViewPhase.doPhase(RestoreViewPhase.java:110)
        at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:177)
        at javax.faces.webapp.FacesServlet.executeLifecyle(FacesServlet.java:707)
        at javax.faces.webapp.FacesServlet.service(FacesServlet.java:451)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
        at jsf.filters.CacheFilter.doFilter(CacheFilter.java:27)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
        at com.filters.SecurityFilter.posResult(SecurityFilter.java:104)
        at com.filters.SecurityFilter.doFilter(SecurityFilter.java:86)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
        at org.primefaces.webapp.filter.FileUploadFilter.doFilter(FileUploadFilter.java:89)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
        at org.omnifaces.filter.GzipResponseFilter.doFilter(GzipResponseFilter.java:183)
        at org.omnifaces.filter.HttpFilter.doFilter(HttpFilter.java:108)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:196)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
        at org.redisson.tomcat.UpdateValve.invoke(UpdateValve.java:62)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
        at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:698)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:364)
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:624)
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:831)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1650)
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:750)

I have already tried the solution suggested in this answer (JSF Session Fail over and Partial State Saving), but I had no luck because when I change to PARTIAL_STATE_SAVING=false, I get the following error when restoring (on every page):

06-Oct-2023 16:25:25.953 SEVERE [http-nio-9090-exec-1] com.sun.faces.util.Util.checkIdUniqueness JSF1007: Duplicate component ID j_idt2 found in view.
06-Oct-2023 16:25:25.967 SEVERE [http-nio-9090-exec-1] com.sun.faces.util.Util.checkIdUniqueness +id: j_id1
 type: javax.faces.component.UIViewRoot@103f414
  +id: javax_faces_location_HEAD
   type: com.sun.faces.component.ComponentResourceContainer@23a6f92f
    +id: j_id2
     type: javax.faces.component.UIOutput@23d4beea
    +id: j_id3
     type: javax.faces.component.UIOutput@4638b819
    +id: j_id4
     type: javax.faces.component.UIOutput@101099f6
    +id: j_id5

This is a parcial stack because it basically says that every component on the page is duplicated.

I also tried STATE_SAVING_METHOD on the client and server. I also updated the Mojarra version to 2.3.21.

Edit: here is an example of the xhtml page

<div class="layout-main" >
            <h:form id="mainForm" >

                <div class="ui-g" >
                    <div  class="ui-g-12" >
                        <p:commandButton    value="#{msg.test}" styleClass="BtnAccionesLista Fright  Wid100 "
                                            actionListener="#{bbTest.test()}" process="@this" update=":mainForm" partialSubmit="true"  />
                    </div>
                </div>
                <div class="ui-g" >
                    <div  class="ui-g-12" >
                        <h:panelGroup  >
                            <p:outputLabel  value="date_from" />
                            <p:calendar mask="true" value="#{bbTest.dateFrom}"
                                        showButtonPanel="true" showTodayButton="true" showOn="button"   >
                                <p:ajax event="dateSelect" process="@this" update=":mainForm" partialSubmit="true" listener="#{bbTest.test()}" />
                                <p:ajax event="change" process="@this" update=":mainForm" partialSubmit="true" listener="#{bbTest.test()}" />
                            </p:calendar>
                        </h:panelGroup>
                    </div>
                    <div  class="ui-g-12" >
                        <h:panelGroup  >
                            <p:outputLabel  value="date_to" />
                            <p:calendar mask="true" value="#{bbTest.dateTo}"
                                        showButtonPanel="true" showTodayButton="true" showOn="button"   >
                                <p:ajax event="dateSelect" process="@this" update=":mainForm" partialSubmit="true" listener="#{bbTest.test()}" />
                                <p:ajax event="change" process="@this" update=":mainForm" partialSubmit="true" listener="#{bbTest.test()}" />
                            </p:calendar>
                        </h:panelGroup>
                    </div>
                </div>
                <div class="ui-g" >
                    <div  class="ui-g-12" >
                        <p:dataTable    value="#{bbTest.list}" var="item"
                                        paginator="true" paginatorPosition="bottom" sortBy="#{item.field1}" sortOrder="DESCENDING"
                                        scrollable="true"
                                        rows="15" rowsPerPageTemplate="15,25">

                            <p:column headerText="field1"  sortable="false">
                                <h:outputText value="#{item.field1}" title="#{item.field1}"/>
                            </p:column>
                            <p:column headerText="field2"  sortable="false">
                                <h:outputText value="#{item.field2}" title="#{item.field2}"/>
                            </p:column>
                        </p:dataTable>
                    </div>
                </div>


            </h:form>
        </div>
@Named("bbTest")
@ViewScoped
public class BBTest implements Serializable {

    private static final long serialVersionUID = 4543464259557561468L;
    private Date dateFrom;
    private Date dateTo;
    private List<Test> list;


    @PostConstruct
    private void init(){
        dateFrom = DateUtil.firstDay();
        dateTo = DateUtil.lastDay();
        completeList();
    }

    private void completeList(){
        list = new ArrayList<>();
        for(int i=0;i<30;i++){
            Test test = new Test();
            test.setField1("value"+i);
            list.add(test);
        }
    }

    public void test(){
        System.out.println("test");
    }

    public List<Test> getList() {
        return list;
    }

    public void setList(List<Test> list) {
        this.list = list;
    }

    public Date getDateFrom() {
        return dateFrom;
    }

    public void setDateFrom(Date dateFrom) {
        this.dateFrom = dateFrom;
    }

    public Date getDateTo() {
        return dateTo;
    }

    public void setDateTo(Date dateTo) {
        this.dateTo = dateTo;
    }
}

If I remove a column of the datatable on the new tomcat instance, then when the session is moved from one tomcat to another, I get the error.

So, my question is, in cases where I need to deploy a change in an XML file, is it possible to restore the JSF view correctly, or should I create a parallel file for existing sessions? What would be the best approach?

4
  • 1
    This is a sign you're somewhere binding a component to a bean property on a bean which is in a scope greater than request. Can you guarantee that you aren't doing that? See also among others stackoverflow.com/q/14911158
    – BalusC
    Commented Oct 8, 2023 at 12:18
  • @BalusC Yes, I can say for sure that I'm not using binding. Although most of my beans are viewscoped, is this something that cant be achieved using viewscoped?
    – dssof
    Commented Oct 9, 2023 at 12:28
  • 1
    It's not related to beans being view scoped. It's more related that if any view (or session) scoped bean holds a reference to an UIComponent instance as an instance variable then this problem can happen. But the problem is then the instance variable itself and not the bean being in view (or session) scope. Have you tried setting the javax.faces.SERIALIZE_SERVER_STATE context param to true? This should then throw an exception when there's indeed an UIComponent instance referenced as a bean property -- it's namely not serializable.
    – BalusC
    Commented Oct 9, 2023 at 12:39
  • @BalusC I tried to set javax.faces.SERIALIZE_SERVER_STATE = true but didn't get an error of serialization. Also I added an example of the problem. I believe I don't have references to UIComponent in any class, already checked.
    – dssof
    Commented Oct 10, 2023 at 13:01

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.