Reputation: 305
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?
Upvotes: 2
Views: 103