Details
Description
Using the scopes test, the web.xml needs to be modified first by including the following context-param:
<context-param>
<param-name>org.icefaces.lazyWindowScope</param-name>
<param-value>false</param-value>
</context-param>
To reproduce the issue, follow these steps:
- Go to http://host:port/scopes/window-scope-A.jsf
- Check the server logs for a window scoped bean that has been created, something like:
created >> org.icefaces.demo.scopes.WindowScopedCounter@14323d5
- Go to http://www.google.ca/ (or any other non-scopes application page) and stay on this page for a couple of seconds
- Go to http://host:port/scopes/window-scope-A.jsf again (NOT USING THE BACK BUTTON)
- Check the server logs for disposal of the old window scoped bean and the creation of a new window scoped bean, something like:
destroyed >> org.icefaces.demo.scopes.WindowScopedCounter@14323d5
created >> org.icefaces.demo.scopes.WindowScopedCounter@1abdac9
- Go to http://www.google.ca/ (or any other non-scopes application page) and wait for the session to timeout
- Check the server logs for disposal of the new window scoped bean and incorrectly also another disposal of the old window scoped bean, something like:
destroyed >> org.icefaces.demo.scopes.WindowScopedCounter@1abdac9
destroyed >> org.icefaces.demo.scopes.WindowScopedCounter@14323d5 <===--- 2nd invocation of @PreDestroy on the same window scoped bean
What seems to be happening is as follows:
- When go to Google for the 1st time the old window scoped bean is removed from the windowScopedMaps and put in the disposedWindowScopedMaps using the disactivate() method:
private void disactivate(State state) {
deactivateTimestamp = System.currentTimeMillis();
state.disposedWindowScopedMaps.addLast(state.windowScopedMaps.remove(id));
}
- When returning to the scopes application the new window scoped bean is added to the windowScopedMaps and the old window scoped bean is removed from the disposedWindowScopedMaps using the discardIfExpired() method:
private void discardIfExpired(FacesContext facesContext) {
State state = getState(facesContext);
if (System.currentTimeMillis() > deactivateTimestamp + state.expirationPeriod) {
boolean processingEvents = facesContext.isProcessingEvents();
try {
facesContext.setProcessingEvents(true);
ScopeContext context = new ScopeContext(ScopeName, this);
facesContext.getApplication().publishEvent(facesContext, PreDestroyCustomScopeEvent.class, context);
} finally {
state.disposedWindowScopedMaps.remove(this);
facesContext.setProcessingEvents(processingEvents);
}
}
}
- So far so good, but something kicks off another JSF lifecycle:
at java.lang.Thread.dumpStack(Thread.java:1249)
at org.icefaces.impl.application.WindowScopeManager$ScopeMap.activate(WindowScopeManager.java:452)
at org.icefaces.impl.application.WindowScopeManager$ScopeMap.access$700(WindowScopeManager.java:363)
at org.icefaces.impl.application.WindowScopeManager.determineWindowID(WindowScopeManager.java:234)
at org.icefaces.impl.application.WindowScopeManager$1.beforePhase(WindowScopeManager.java:92)
at com.sun.faces.lifecycle.Phase.handleBeforePhase(Phase.java:228)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:99)
at com.sun.faces.lifecycle.RestoreViewPhase.doPhase(RestoreViewPhase.java:116)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
at org.icefaces.impl.application.WindowScopeManager.disposeViewScopeBeans(WindowScopeManager.java:305)
at org.icefaces.impl.application.WindowScopeManager.disposeWindow(WindowScopeManager.java:291)
at org.icefaces.impl.application.WindowScopeManager.handleResourceRequest(WindowScopeManager.java:152)
at javax.faces.application.ResourceHandlerWrapper.handleResourceRequest(ResourceHandlerWrapper.java:125)
at javax.faces.application.ResourceHandlerWrapper.handleResourceRequest(ResourceHandlerWrapper.java:125)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:407)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:859)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:588)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
at java.lang.Thread.run(Thread.java:662)
- As seen from the stack dump, the activate() method is invoked for the old window scoped bean:
private void activate(State state) {
state.windowScopedMaps.put(id, this);
activateTimestamp = System.currentTimeMillis();
}
- From this point the windowScopedMaps contains both the old and new window scoped beans (though the @PreDestroy of the old window scoped bean has already been invoked).
<context-param>
<param-name>org.icefaces.lazyWindowScope</param-name>
<param-value>false</param-value>
</context-param>
To reproduce the issue, follow these steps:
- Go to http://host:port/scopes/window-scope-A.jsf
- Check the server logs for a window scoped bean that has been created, something like:
created >> org.icefaces.demo.scopes.WindowScopedCounter@14323d5
- Go to http://www.google.ca/ (or any other non-scopes application page) and stay on this page for a couple of seconds
- Go to http://host:port/scopes/window-scope-A.jsf again (NOT USING THE BACK BUTTON)
- Check the server logs for disposal of the old window scoped bean and the creation of a new window scoped bean, something like:
destroyed >> org.icefaces.demo.scopes.WindowScopedCounter@14323d5
created >> org.icefaces.demo.scopes.WindowScopedCounter@1abdac9
- Go to http://www.google.ca/ (or any other non-scopes application page) and wait for the session to timeout
- Check the server logs for disposal of the new window scoped bean and incorrectly also another disposal of the old window scoped bean, something like:
destroyed >> org.icefaces.demo.scopes.WindowScopedCounter@1abdac9
destroyed >> org.icefaces.demo.scopes.WindowScopedCounter@14323d5 <===--- 2nd invocation of @PreDestroy on the same window scoped bean
What seems to be happening is as follows:
- When go to Google for the 1st time the old window scoped bean is removed from the windowScopedMaps and put in the disposedWindowScopedMaps using the disactivate() method:
private void disactivate(State state) {
deactivateTimestamp = System.currentTimeMillis();
state.disposedWindowScopedMaps.addLast(state.windowScopedMaps.remove(id));
}
- When returning to the scopes application the new window scoped bean is added to the windowScopedMaps and the old window scoped bean is removed from the disposedWindowScopedMaps using the discardIfExpired() method:
private void discardIfExpired(FacesContext facesContext) {
State state = getState(facesContext);
if (System.currentTimeMillis() > deactivateTimestamp + state.expirationPeriod) {
boolean processingEvents = facesContext.isProcessingEvents();
try {
facesContext.setProcessingEvents(true);
ScopeContext context = new ScopeContext(ScopeName, this);
facesContext.getApplication().publishEvent(facesContext, PreDestroyCustomScopeEvent.class, context);
} finally {
state.disposedWindowScopedMaps.remove(this);
facesContext.setProcessingEvents(processingEvents);
}
}
}
- So far so good, but something kicks off another JSF lifecycle:
at java.lang.Thread.dumpStack(Thread.java:1249)
at org.icefaces.impl.application.WindowScopeManager$ScopeMap.activate(WindowScopeManager.java:452)
at org.icefaces.impl.application.WindowScopeManager$ScopeMap.access$700(WindowScopeManager.java:363)
at org.icefaces.impl.application.WindowScopeManager.determineWindowID(WindowScopeManager.java:234)
at org.icefaces.impl.application.WindowScopeManager$1.beforePhase(WindowScopeManager.java:92)
at com.sun.faces.lifecycle.Phase.handleBeforePhase(Phase.java:228)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:99)
at com.sun.faces.lifecycle.RestoreViewPhase.doPhase(RestoreViewPhase.java:116)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
at org.icefaces.impl.application.WindowScopeManager.disposeViewScopeBeans(WindowScopeManager.java:305)
at org.icefaces.impl.application.WindowScopeManager.disposeWindow(WindowScopeManager.java:291)
at org.icefaces.impl.application.WindowScopeManager.handleResourceRequest(WindowScopeManager.java:152)
at javax.faces.application.ResourceHandlerWrapper.handleResourceRequest(ResourceHandlerWrapper.java:125)
at javax.faces.application.ResourceHandlerWrapper.handleResourceRequest(ResourceHandlerWrapper.java:125)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:407)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:859)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:588)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
at java.lang.Thread.run(Thread.java:662)
- As seen from the stack dump, the activate() method is invoked for the old window scoped bean:
private void activate(State state) {
state.windowScopedMaps.put(id, this);
activateTimestamp = System.currentTimeMillis();
}
- From this point the windowScopedMaps contains both the old and new window scoped beans (though the @PreDestroy of the old window scoped bean has already been invoked).
Issue Links
- blocks
-
ICE-6826 Add support for @PostConstruct and @PreDestroy annotations for window-scoped beans
- Closed
Changed view scope bean disposal to run after the restore phase is executed to avoid running the additional JSF cycle which causes the reactivation of scope maps while disposing views/window. Refactored also anonymous phase listeners into inner classes. Registered phase listeners declaratively insted of doing it programmatically.