Details
-
Type: Bug
-
Status: Closed
-
Priority: Major
-
Resolution: Fixed
-
Affects Version/s: 1.8.2
-
Fix Version/s: 1.8.2a, 1.8.2-EE-GA_P02
-
Component/s: Framework
-
Labels:None
-
Environment:ICEFaces FileUpload + Spring Webflow
Description
Using FileUpload in a Spring application fails during the FileUpload itself with an error indicating the software can't restore the view, whatever the view is.
It turns out that ICEFaces state saving is not actually invoked in Spring applications. Looking at the code in Spring, Spring doesn't delegate state saving calls to the delegate instance unless the request is deemed not to be a Spring request, and this is established by checking if there is a RequestContext object ThreadLocal defined, and this is setup in the resume or start method of FlowExecutorImpl.
Here's a stack trace showing the execution path to the point where the RequestContext is established as a ThreadLocal. The entry point to the Spring Webflow applications is through the MainServlet and then goes back into the Spring framework via the SwfLifecycleExecutor.
at org.springframework.webflow.execution.RequestContextHolder.setRequestContext(RequestContextHolder.java:40)
at org.springframework.webflow.engine.impl.FlowExecutionImpl.start(FlowExecutionImpl.java:218)
at org.springframework.webflow.executor.FlowExecutorImpl.launchExecution(FlowExecutorImpl.java:140)
at com.icesoft.faces.webapp.http.core.FlowExecutorUtil.launchExecution(FlowExecutorUtil.java:90)
at com.icesoft.faces.webapp.http.core.SwfLifecycleExecutor.launchExecution(SwfLifecycleExecutor.java:134)
at com.icesoft.faces.webapp.http.core.SwfLifecycleExecutor.apply(SwfLifecycleExecutor.java:57)
at com.icesoft.faces.context.View$2$1.respond(View.java:48)
at com.icesoft.faces.webapp.http.servlet.ServletRequestResponse.respondWith(ServletRequestResponse.java:201)
at com.icesoft.faces.context.View$2.serve(View.java:77)
at com.icesoft.faces.context.View.servePage(View.java:149)
at com.icesoft.faces.webapp.http.core.MultiViewServer.service(MultiViewServer.java:79)
at com.icesoft.faces.webapp.http.common.ServerProxy.service(ServerProxy.java:11)
at com.icesoft.faces.webapp.http.servlet.MainSessionBoundServlet$4.service(MainSessionBoundServlet.java:149)
at com.icesoft.faces.webapp.http.common.standard.PathDispatcherServer.service(PathDispatcherServer.java:24)
at com.icesoft.faces.webapp.http.servlet.BasicAdaptingServlet.service(BasicAdaptingServlet.java:16)
at com.icesoft.faces.webapp.http.servlet.PathDispatcher.service(PathDispatcher.java:23)
at com.icesoft.faces.webapp.http.servlet.SessionDispatcher.service(SessionDispatcher.java:60)
at com.icesoft.faces.webapp.http.servlet.PathDispatcher.service(PathDispatcher.java:23)
at com.icesoft.faces.webapp.http.servlet.MainServlet.service(MainServlet.java:148)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:359)
at org.springframework.security.intercept.web.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:109)
For the sake of interest, views are rendered by the SpringFramework treating the JSF Views as a certain type of renderable.
at com.icesoft.faces.facelets.D2DFaceletViewHandler.renderResponse(D2DFaceletViewHandler.java:284)
at com.icesoft.faces.application.D2DViewHandler.renderView(D2DViewHandler.java:159)
at org.springframework.faces.webflow.FlowViewHandler.renderView(FlowViewHandler.java:92)
at org.springframework.faces.webflow.JsfView.render(JsfView.java:94)
at org.springframework.webflow.engine.ViewState.render(ViewState.java:245)
at org.springframework.webflow.engine.ViewState.resume(ViewState.java:204)
at org.springframework.webflow.engine.Flow.resume(Flow.java:545)
at org.springframework.webflow.engine.impl.FlowExecutionImpl.resume(FlowExecutionImpl.java:262)
at org.springframework.webflow.executor.FlowExecutorImpl.resumeExecution(FlowExecutorImpl.java:163)
at com.icesoft.faces.webapp.http.core.FlowExecutorUtil.resumeExecution(FlowExecutorUtil.java:86)
at com.icesoft.faces.webapp.http.core.SwfLifecycleExecutor.resumeExecution(SwfLifecycleExecutor.java:129)
at com.icesoft.faces.webapp.http.core.SwfLifecycleExecutor.apply(SwfLifecycleExecutor.java:55)
at com.icesoft.faces.webapp.http.core.ReceiveSendUpdates.renderCycle(ReceiveSendUpdates.java:132)
at com.icesoft.faces.webapp.http.core.ReceiveSendUpdates.service(ReceiveSendUpdates.java:74)
at com.icesoft.faces.webapp.http.core.RequestVerifier.service(RequestVerifier.java:31)
at com.icesoft.faces.webapp.http.common.standard.PathDispatcherServer.service(PathDispatcherServer.java:24)
at com.icesoft.faces.webapp.http.servlet.BasicAdaptingServlet.service(BasicAdaptingServlet.java:16)
at com.icesoft.faces.webapp.http.servlet.PathDispatcher.service(PathDispatcher.java:23)
at com.icesoft.faces.webapp.http.servlet.SessionDispatcher.service(SessionDispatcher.java:60)
at com.icesoft.faces.webapp.http.servlet.SessionVerifier.service(SessionVerifier.java:26)
at com.icesoft.faces.webapp.http.servlet.PathDispatcher.service(PathDispatcher.java:23)
at com.icesoft.faces.webapp.http.servlet.MainServlet.service(MainServlet.java:148)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
at com.icesoft.faces.webapp.xmlhttp.BlockingServlet.service(BlockingServlet.java:56)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
When a FileUpload request is processed a lifecycle event is generated by the upload process in order to update the Upload progress page. Since the PathDispatcher matches the path of the FileUploadServlet execution goes down that route and doesn't have the Spring path portions in it, therefore a Spring webflow isn't resumed. Since the Spring Webflow isn't resumed, the StateSaving code executed when running the lifecycle is delegated to our StateSaving implementation which is looking for a viewRoot in the State Maps in the session.
at com.icesoft.faces.webapp.http.core.UploadServer$2.servlet(UploadServer.java:127)
at com.icesoft.faces.webapp.http.servlet.ServletRequestResponse.detectEnvironment(ServletRequestResponse.java:262)
at com.icesoft.faces.webapp.http.core.UploadServer.service(UploadServer.java:69)
at com.icesoft.faces.webapp.http.common.standard.PathDispatcherServer.service(PathDispatcherServer.java:24)
at com.icesoft.faces.webapp.http.servlet.BasicAdaptingServlet.service(BasicAdaptingServlet.java:16)
at com.icesoft.faces.webapp.http.servlet.PathDispatcher.service(PathDispatcher.java:23)
at com.icesoft.faces.webapp.http.servlet.SessionDispatcher.service(SessionDispatcher.java:60)
at com.icesoft.faces.webapp.http.servlet.PathDispatcher.service(PathDispatcher.java:23)
at com.icesoft.faces.webapp.http.servlet.MainServlet.service(MainServlet.java:148)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
at com.icesoft.faces.webapp.xmlhttp.BlockingServlet.service(BlockingServlet.java:56)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:359)
Since we haven't been involved in saving the state at any time in the past, there isn't any copy of the ViewRoot in the Session when this attempt is made, hence the error.
It turns out that ICEFaces state saving is not actually invoked in Spring applications. Looking at the code in Spring, Spring doesn't delegate state saving calls to the delegate instance unless the request is deemed not to be a Spring request, and this is established by checking if there is a RequestContext object ThreadLocal defined, and this is setup in the resume or start method of FlowExecutorImpl.
Here's a stack trace showing the execution path to the point where the RequestContext is established as a ThreadLocal. The entry point to the Spring Webflow applications is through the MainServlet and then goes back into the Spring framework via the SwfLifecycleExecutor.
at org.springframework.webflow.execution.RequestContextHolder.setRequestContext(RequestContextHolder.java:40)
at org.springframework.webflow.engine.impl.FlowExecutionImpl.start(FlowExecutionImpl.java:218)
at org.springframework.webflow.executor.FlowExecutorImpl.launchExecution(FlowExecutorImpl.java:140)
at com.icesoft.faces.webapp.http.core.FlowExecutorUtil.launchExecution(FlowExecutorUtil.java:90)
at com.icesoft.faces.webapp.http.core.SwfLifecycleExecutor.launchExecution(SwfLifecycleExecutor.java:134)
at com.icesoft.faces.webapp.http.core.SwfLifecycleExecutor.apply(SwfLifecycleExecutor.java:57)
at com.icesoft.faces.context.View$2$1.respond(View.java:48)
at com.icesoft.faces.webapp.http.servlet.ServletRequestResponse.respondWith(ServletRequestResponse.java:201)
at com.icesoft.faces.context.View$2.serve(View.java:77)
at com.icesoft.faces.context.View.servePage(View.java:149)
at com.icesoft.faces.webapp.http.core.MultiViewServer.service(MultiViewServer.java:79)
at com.icesoft.faces.webapp.http.common.ServerProxy.service(ServerProxy.java:11)
at com.icesoft.faces.webapp.http.servlet.MainSessionBoundServlet$4.service(MainSessionBoundServlet.java:149)
at com.icesoft.faces.webapp.http.common.standard.PathDispatcherServer.service(PathDispatcherServer.java:24)
at com.icesoft.faces.webapp.http.servlet.BasicAdaptingServlet.service(BasicAdaptingServlet.java:16)
at com.icesoft.faces.webapp.http.servlet.PathDispatcher.service(PathDispatcher.java:23)
at com.icesoft.faces.webapp.http.servlet.SessionDispatcher.service(SessionDispatcher.java:60)
at com.icesoft.faces.webapp.http.servlet.PathDispatcher.service(PathDispatcher.java:23)
at com.icesoft.faces.webapp.http.servlet.MainServlet.service(MainServlet.java:148)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:359)
at org.springframework.security.intercept.web.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:109)
For the sake of interest, views are rendered by the SpringFramework treating the JSF Views as a certain type of renderable.
at com.icesoft.faces.facelets.D2DFaceletViewHandler.renderResponse(D2DFaceletViewHandler.java:284)
at com.icesoft.faces.application.D2DViewHandler.renderView(D2DViewHandler.java:159)
at org.springframework.faces.webflow.FlowViewHandler.renderView(FlowViewHandler.java:92)
at org.springframework.faces.webflow.JsfView.render(JsfView.java:94)
at org.springframework.webflow.engine.ViewState.render(ViewState.java:245)
at org.springframework.webflow.engine.ViewState.resume(ViewState.java:204)
at org.springframework.webflow.engine.Flow.resume(Flow.java:545)
at org.springframework.webflow.engine.impl.FlowExecutionImpl.resume(FlowExecutionImpl.java:262)
at org.springframework.webflow.executor.FlowExecutorImpl.resumeExecution(FlowExecutorImpl.java:163)
at com.icesoft.faces.webapp.http.core.FlowExecutorUtil.resumeExecution(FlowExecutorUtil.java:86)
at com.icesoft.faces.webapp.http.core.SwfLifecycleExecutor.resumeExecution(SwfLifecycleExecutor.java:129)
at com.icesoft.faces.webapp.http.core.SwfLifecycleExecutor.apply(SwfLifecycleExecutor.java:55)
at com.icesoft.faces.webapp.http.core.ReceiveSendUpdates.renderCycle(ReceiveSendUpdates.java:132)
at com.icesoft.faces.webapp.http.core.ReceiveSendUpdates.service(ReceiveSendUpdates.java:74)
at com.icesoft.faces.webapp.http.core.RequestVerifier.service(RequestVerifier.java:31)
at com.icesoft.faces.webapp.http.common.standard.PathDispatcherServer.service(PathDispatcherServer.java:24)
at com.icesoft.faces.webapp.http.servlet.BasicAdaptingServlet.service(BasicAdaptingServlet.java:16)
at com.icesoft.faces.webapp.http.servlet.PathDispatcher.service(PathDispatcher.java:23)
at com.icesoft.faces.webapp.http.servlet.SessionDispatcher.service(SessionDispatcher.java:60)
at com.icesoft.faces.webapp.http.servlet.SessionVerifier.service(SessionVerifier.java:26)
at com.icesoft.faces.webapp.http.servlet.PathDispatcher.service(PathDispatcher.java:23)
at com.icesoft.faces.webapp.http.servlet.MainServlet.service(MainServlet.java:148)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
at com.icesoft.faces.webapp.xmlhttp.BlockingServlet.service(BlockingServlet.java:56)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
When a FileUpload request is processed a lifecycle event is generated by the upload process in order to update the Upload progress page. Since the PathDispatcher matches the path of the FileUploadServlet execution goes down that route and doesn't have the Spring path portions in it, therefore a Spring webflow isn't resumed. Since the Spring Webflow isn't resumed, the StateSaving code executed when running the lifecycle is delegated to our StateSaving implementation which is looking for a viewRoot in the State Maps in the session.
at com.icesoft.faces.webapp.http.core.UploadServer$2.servlet(UploadServer.java:127)
at com.icesoft.faces.webapp.http.servlet.ServletRequestResponse.detectEnvironment(ServletRequestResponse.java:262)
at com.icesoft.faces.webapp.http.core.UploadServer.service(UploadServer.java:69)
at com.icesoft.faces.webapp.http.common.standard.PathDispatcherServer.service(PathDispatcherServer.java:24)
at com.icesoft.faces.webapp.http.servlet.BasicAdaptingServlet.service(BasicAdaptingServlet.java:16)
at com.icesoft.faces.webapp.http.servlet.PathDispatcher.service(PathDispatcher.java:23)
at com.icesoft.faces.webapp.http.servlet.SessionDispatcher.service(SessionDispatcher.java:60)
at com.icesoft.faces.webapp.http.servlet.PathDispatcher.service(PathDispatcher.java:23)
at com.icesoft.faces.webapp.http.servlet.MainServlet.service(MainServlet.java:148)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
at com.icesoft.faces.webapp.xmlhttp.BlockingServlet.service(BlockingServlet.java:56)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:359)
Since we haven't been involved in saving the state at any time in the past, there isn't any copy of the ViewRoot in the Session when this attempt is made, hence the error.
The way that ICEFaces integrates with Spring Webflow is as follows:
Requests to ICEFaces, and even requests that normally are mapped to Spring all go to ICEFaces. ICEfaces has an assortment of Handler classes mapped to handle requests and ICEFaces handles the request. If a Spring request, control is passed to the SwfLifecycleExecutor which either starts a new flow or resumes execution of an old one.
Control then passes to Spring, which executes a JSF lifecycle and then returns control to ICEFaces through the VIewHandler instance which is used to render the page. The Spring state saving instance does not delegate to any other StateSaving class if the request is a Spring mapped request, which is why ICEFaces StateSaving never gets invoked, even though it is configured by default.
The FileUpload mechanism runs a JSF lifecycle to render the progress bar at intervals. These lifecycles don't have the Spring ThreadLocal necessary for Spring to detect a webflow request, so they delegate to ICEFaces state saving. The solution to this is to launch the progress lifecycle via Spring webflow if a Spring environment is detected. This should be ok, no state transition should be executed because there will be no result from the webflow execution.