eclipse-ee4j / mojarra

Mojarra, a Jakarta Faces implementation
Other
158 stars 108 forks source link

ArrayIndexOutOfBoundsException during restore state #5269

Closed pizzi80 closed 1 year ago

pizzi80 commented 1 year ago

Describe the bug

If ViewExpiredException is thrown inside an action the restoreState throws an ArrayIndexOutOfBoundsException

To Reproduce

<h:form id="test" >
    <h:commandButton id="expired" action="#{testErrors.throwViewExpiredException}" value="JSF Expired" />
</h:form>
@Named
@RequestScoped
public class TestErrors {

    public void throwViewExpiredException() {
        throw new ViewExpiredException();
    }

}
Caused by: java.lang.ArrayIndexOutOfBoundsException: Index 2 out of bounds for length 2
    at jakarta.faces.component.UIComponentBase.restoreState(UIComponentBase.java:1223)
    at com.sun.faces.application.view.FaceletPartialStateManagementStrategy.lambda$restoreView$1(FaceletPartialStateManagementStrategy.java:360)
    at com.sun.faces.component.visit.FullVisitContext.invokeVisitCallback(FullVisitContext.java:125)
    at jakarta.faces.component.UIComponent.visitTree(UIComponent.java:1266)
    at jakarta.faces.component.UIComponent.visitTree(UIComponent.java:1278)
    at jakarta.faces.component.UIComponent.visitTree(UIComponent.java:1278)
    at com.sun.faces.application.view.FaceletPartialStateManagementStrategy.restoreView(FaceletPartialStateManagementStrategy.java:349)
    at com.sun.faces.application.view.ViewHandlingStrategy.restoreView(ViewHandlingStrategy.java:90)
    at com.sun.faces.application.view.FaceletViewHandlingStrategy.restoreView(FaceletViewHandlingStrategy.java:256)
    at com.sun.faces.application.view.MultiViewHandler.restoreView(MultiViewHandler.java:129)
    at jakarta.faces.application.ViewHandlerWrapper.restoreView(ViewHandlerWrapper.java:96)
    at jakarta.faces.application.ViewHandlerWrapper.restoreView(ViewHandlerWrapper.java:96)
    at org.omnifaces.viewhandler.OmniViewHandler.restoreView(OmniViewHandler.java:128)
    at jakarta.faces.application.ViewHandlerWrapper.restoreView(ViewHandlerWrapper.java:96)
    at com.sun.faces.lifecycle.RestoreViewPhase.execute(RestoreViewPhase.java:161)
    at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:72)
    at com.sun.faces.lifecycle.RestoreViewPhase.doPhase(RestoreViewPhase.java:95)
    at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:159)
    at jakarta.faces.webapp.FacesServlet.executeLifecyle(FacesServlet.java:691)
    ... 29 more

Desktop (please complete the following information):

BalusC commented 1 year ago

Norepro when opening the page and pressing the 'JSF Expired'. I get a 500 error page with

SEVERE: Servlet.service() for servlet [facesServlet] in context with path [/test-tomcat10] threw exception [null] with root cause
jakarta.faces.application.ViewExpiredException
    at com.example.Bean.throwViewExpiredException(Bean.java:16)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.apache.el.parser.AstValue.invoke(AstValue.java:253)
    at org.apache.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:267)
    at org.jboss.weld.module.web.util.el.ForwardingMethodExpression.invoke(ForwardingMethodExpression.java:40)
    at org.jboss.weld.module.web.el.WeldMethodExpression.invoke(WeldMethodExpression.java:50)
    at com.sun.faces.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:70)
    at com.sun.faces.application.ActionListenerImpl.getNavigationOutcome(ActionListenerImpl.java:74)
    at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:62)
    at jakarta.faces.component.UICommand.broadcast(UICommand.java:205)
    at jakarta.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:858)
    at jakarta.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1332)
    at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:56)
    at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:72)
    at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:159)
    at jakarta.faces.webapp.FacesServlet.executeLifecyle(FacesServlet.java:691)
    at jakarta.faces.webapp.FacesServlet.service(FacesServlet.java:449)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:223)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:119)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:690)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:400)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:861)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1739)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
    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.base/java.lang.Thread.run(Thread.java:833)

I'm also wondering how exactly to reproduce it. The restore view is normally invoked BEFORE invoke application.

pizzi80 commented 1 year ago

LOL I'm not able to reproduce this bug anymore :zany_face:

... even if I add PrimeFaces in the mix (which was present in the original project with the bug)

my PR can also be ignored but it's still a more secure way to access the save state object array, eventually I can improve it with a for loop with index instead of call multiple times the static function getElem ...

Or we can leave the algo throws the exception and eventually understand what's going on ...

what do you think about it?

BalusC commented 1 year ago

This kind of exception during restore view is usually thrown when physically same component instance is used by multiple HTTP requests. I.e. in a threadunsafe way. More than often because it or its parent is via binding attribute referenced in a bean with a scope greater than request. That part should then be fixed instead. See also https://stackoverflow.com/a/14917453