Long Running Conversation Timeouts are never occuring
knaas Sep 8, 2006 11:11 AMWe are having issues with conversations being timed out in 1.0.1GA. This may be fixed in the CVS version.
We have set the org.jboss.seam.core.Manager.conversationTimeout to 15 seconds through use of a components.xml.
After pausing on a conversational page for 30 seconds, we attempt to see if the conversation did timeout. When we click the "Save" link on our page, it allows the request to go through without redirecting to the "No Conversation" page.
In order to debug the issue, we set some breakpoints in Manager.setLongRunningConversation, Manager.storeConversation, and Manager.conversationTimeout to
see how things are playing out.
When we click the Save link, the first thing that happens is the conversation is restored.
At this point, the longRunningConversation is set to "true".
Stack trace
Thread [http-0.0.0.0-8080-2] (Suspended (entry into method setLongRunningConversation in Manager)) Manager.setLongRunningConversation(boolean) line: 294 Manager.restoreConversation(String) line: 587 Manager.restoreConversation(Map) line: 521 AbstractSeamPhaseListener.restoreAnyConversationContext(FacesContext) line: 41 SeamPhaseListener.afterPhase(PhaseEvent) line: 63 PhaseListenerManager.informPhaseListenersAfter(PhaseId) line: 89 LifecycleImpl.restoreView(FacesContext, PhaseListenerManager) line: 181 LifecycleImpl.execute(FacesContext) line: 66 FacesServlet.service(ServletRequest, ServletResponse) line: 137 ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 252 ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 173 ExtensionsFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 144 ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 202 ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 173 SeamRedirectFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 30 ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 202 ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 173 ReplyHeaderFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 96 ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 202 ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 173 StandardWrapperValve.invoke(Request, Response) line: 213 StandardContextValve.invoke(Request, Response) line: 178 SecurityAssociationValve.invoke(Request, Response) line: 175 BasicAuthenticator(AuthenticatorBase).invoke(Request, Response) line: 524 JaccContextValve.invoke(Request, Response) line: 74 StandardHostValve.invoke(Request, Response) line: 126 ErrorReportValve.invoke(Request, Response) line: 105 StandardEngineValve.invoke(Request, Response) line: 107 CoyoteAdapter.service(Request, Response) line: 148 Http11Processor.process(InputStream, OutputStream) line: 869 Http11Protocol$JmxHttp11ConnectionHandler(Http11BaseProtocol$Http11ConnectionHandler).processConnection(TcpConnection, Object[]) line: 664 PoolTcpEndpoint.processSocket(Socket, TcpConnection, Object[]) line: 527 MasterSlaveWorkerThread.run() line: 112 ThreadWithAttributes(Thread).run() line: 595
Because the longRunningConversation is "true", the conversation is touched. When the conversation is touched, the ConversationEntry.lastRequestTime is updated to the current time.
public void storeConversation(ContextAdaptor session, Object response) .... if ( isLongRunningConversation() ) { touchConversationStack(); if ( !Seam.isSessionInvalid() ) { forceMutableComponentReplication(); storeLongRunningConversation(response); } } else { discardTemporaryConversation(session, response); }
Stack trace
Thread [http-0.0.0.0-8080-1] (Suspended (entry into method touchConversationStack in Manager)) Manager.touchConversationStack() line: 164 Manager.storeConversation(ContextAdaptor, Object) line: 368 AbstractSeamPhaseListener.storeAnyConversationContext(FacesContext) line: 69 SeamStateManager.saveSerializedView(FacesContext) line: 45 FaceletViewHandler.renderView(FacesContext, UIViewRoot) line: 578 LifecycleImpl.render(FacesContext) line: 384 FacesServlet.service(ServletRequest, ServletResponse) line: 138 ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 252 ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 173 ExtensionsFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 144 ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 202 ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 173 SeamRedirectFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 30 ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 202 ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 173 ReplyHeaderFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 96 ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 202 ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 173 StandardWrapperValve.invoke(Request, Response) line: 213 StandardContextValve.invoke(Request, Response) line: 178 SecurityAssociationValve.invoke(Request, Response) line: 175 BasicAuthenticator(AuthenticatorBase).invoke(Request, Response) line: 524 JaccContextValve.invoke(Request, Response) line: 74 StandardHostValve.invoke(Request, Response) line: 126 ErrorReportValve.invoke(Request, Response) line: 105 StandardEngineValve.invoke(Request, Response) line: 107 CoyoteAdapter.service(Request, Response) line: 148 Http11Processor.process(InputStream, OutputStream) line: 869 Http11Protocol$JmxHttp11ConnectionHandler(Http11BaseProtocol$Http11ConnectionHandler).processConnection(TcpConnection, Object[]) line: 664 PoolTcpEndpoint.processSocket(Socket, TcpConnection, Object[]) line: 527 MasterSlaveWorkerThread.run() line: 112 ThreadWithAttributes(Thread).run() line: 595
Finally, the conversation timeout check occurs. Because the ConversationEntry.lastRequestTime was updated a few milliseconds ago, the delta is never going to be bigger than the conversationEntry.getTimeout(). Note that at this point conversationEntry.getTimeout() is correctly returning "15000".
/** * Clean up timed-out conversations */ public void conversationTimeout(ExternalContext externalContext) { long currentTime = System.currentTimeMillis(); Iterator<Map.Entry<String, ConversationEntry>> entries = getConversationIdEntryMap().entrySet().iterator(); while ( entries.hasNext() ) { Map.Entry<String, ConversationEntry> entry = entries.next(); ConversationEntry conversationEntry = entry.getValue(); long delta = currentTime - conversationEntry.getLastRequestTime(); if ( delta > conversationEntry.getTimeout() ) { String conversationId = entry.getKey(); log.debug("conversation timeout for conversation: " + conversationId); ContextAdaptor session = ContextAdaptor.getSession(externalContext, true); destroyConversation(conversationId, session, entries); } } }
Stack trace
Thread [http-0.0.0.0-8080-1] (Suspended (entry into method conversationTimeout in Manager)) Manager.conversationTimeout(ExternalContext) line: 316 SeamPhaseListener.afterPhase(PhaseEvent) line: 93 PhaseListenerManager.informPhaseListenersAfter(PhaseId) line: 89 LifecycleImpl.render(FacesContext) line: 391 FacesServlet.service(ServletRequest, ServletResponse) line: 138 ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 252 ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 173 ExtensionsFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 144 ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 202 ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 173 SeamRedirectFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 30 ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 202 ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 173 ReplyHeaderFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 96 ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 202 ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 173 StandardWrapperValve.invoke(Request, Response) line: 213 StandardContextValve.invoke(Request, Response) line: 178 SecurityAssociationValve.invoke(Request, Response) line: 175 BasicAuthenticator(AuthenticatorBase).invoke(Request, Response) line: 524 JaccContextValve.invoke(Request, Response) line: 74 StandardHostValve.invoke(Request, Response) line: 126 ErrorReportValve.invoke(Request, Response) line: 105 StandardEngineValve.invoke(Request, Response) line: 107 CoyoteAdapter.service(Request, Response) line: 148 Http11Processor.process(InputStream, OutputStream) line: 869 Http11Protocol$JmxHttp11ConnectionHandler(Http11BaseProtocol$Http11ConnectionHandler).processConnection(TcpConnection, Object[]) line: 664 PoolTcpEndpoint.processSocket(Socket, TcpConnection, Object[]) line: 527 MasterSlaveWorkerThread.run() line: 112 ThreadWithAttributes(Thread).run() line: 595
If you artificially put a 20 second sleep in the Manager.conversationTimeout function, it will correctly detect that the conversation did indeed timeout.
It will then call destroyConversation. However, it fails to redirect to the outcome we specified in our @Conversational annotation. Instead, it
allows our "Save" request to continue as normal and renders the "Saved" page.
Finally, if we click a link on this "Saved" page, it detects that the conversation did indeed timeout, and thus redirects
to the @Conversational Annotations's ifNotBegunOutcome.
So there are multiple issues:
1. The long running conversation will never timeout since the lastRequestTime is always updated before the conversationTimeout check occurs.
2. Even if the conversation timeout check picks up the conversation was timedout, it still allows the method to continue. Note that fixing Issue 1, may
prevent Issue 2 from ever occurring.