Closed maximdim closed 5 years ago
You are correct. In fact the HttpSession
is not really used as a mean to store the dialog state. Instead, it is used to trigger the JavaEE session tracking mechanisms (URL rewritting/JSESSIONID cookie).
The state of the dialog is maintained in the Java call stack. Is it not possible to externalize it. It's a constraint under which you must live when using Rivr.
You can do some partial recovery of the call through the VoiceXmlDialogueContext.setFatalErrorFormFactory()
method. If you create a fatal error form containing a <goto>
to your dialog URI (assuming this URI is handled by a load blanacer), you could restart your dialog on another node.
Here's a sample FatalErrorFormFactory
:
package com.nuecho.rivr.demo.dialogue;
import static com.nuecho.rivr.core.util.DomUtils.*;
import static com.nuecho.rivr.voicexml.rendering.voicexml.VoiceXmlDomUtil.*;
import org.w3c.dom.*;
import com.nuecho.rivr.voicexml.dialogue.*;
import com.nuecho.rivr.voicexml.rendering.voicexml.errorhandling.*;
import com.nuecho.rivr.voicexml.turn.*;
public class RecoveryFatalErrorFormFactory implements FatalErrorFormFactory {
private final String mServletPath;
private final String mContextPath;
public RecoveryFatalErrorFormFactory(VoiceXmlDialogueContext context) {
mServletPath = context.getServletPath();
mContextPath = context.getContextPath();
}
@Override
public void addFatalErrorForm(Element form, VoiceXmlDocumentTurn turn) {
Element blockElement = appendNewElement(form, BLOCK_ELEMENT);
Element gotoElement = appendNewElement(blockElement, GOTO_ELEMENT);
String initialServletUri = mContextPath + mServletPath;
gotoElement.setAttribute(NEXT_ATTRIBUTE, initialServletUri + "?recovery=true");
}
}
In fact, you should also ensure that an error occurring during error handling will not loop indefinitely. So you should probably do something like:
public void addFatalErrorForm(Element form, VoiceXmlDocumentTurn turn) {
Element catchElement = appendNewElement(form, CATCH_ELEMENT);
catchElement.setAttribute(EVENT_ATTRIBUTE, ERROR_EVENT_NAME);
Element promptElement = appendNewElement(catchElement, "prompt");
appendNewText(promptElement, "A fatal error occured. Please try to call back later.");
appendNewElement(catchElement, EXIT_ELEMENT);
Element blockElement = appendNewElement(form, BLOCK_ELEMENT);
Element gotoElement = appendNewElement(blockElement, GOTO_ELEMENT);
String initialServletUri = mContextPath + mServletPath;
gotoElement.setAttribute(NEXT_ATTRIBUTE, initialServletUri + "?recovery=true");
}
Of course, from there, you can go further and pass some elements of the dialog state in the URI and try to resume the dialog with the provided information. If you have a simple callflow, it might be a viable option. It might not provide a totally transparent failure recovery but it's better than hanging up.
Thank you very much for the detailed response. I appreciate your help.
I'm looking at the way to deploy code that uses rivr library in the multi-node environment for high availability purposes - the requirement is for a conversation session to survive a node crash/restart.
From what I can see DialogueServlet has support for Servlet's HttpSession and would save a Session object in HttpSession, which could be made persistent or stored out-of process (via spring-session library, for example). In my tests, when configured properly HttpSession would survive node restart, but if I read code correctly SessionContainer never reads from HttpSession - only from local sessions map (mSessions) so even so HttpSession is present after restart conversation cannot continue because of SessionNotFoundException.
I just want to confirm that what I need is indeed not supported by the current implementation? Thanks.