-
Notifications
You must be signed in to change notification settings - Fork 5
Spring framework integration
There are so many ways you can leverage the Spring framework.
This example will show you how to provide a custom dialogue factory that will create VoiceXmlDialogue
objects
from the Spring bean factory.
The first step is to create a Dialogue as a Java bean:
public class Dialogue implements VoiceXmlDialogue, InitializingBean {
private String mMessage;
public void setMessage(String message) {
mMessage = message;
}
@Override
public VoiceXmlLastTurn run(VoiceXmlFirstTurn firstTurn, VoiceXmlDialogueContext context)
throws Exception {
Message message = new Message("message", new SpeechSynthesis(mMessage));
DialogueUtils.doTurn(message, context);
//end of dialogue
return new Exit("exit");
}
@Override
public void afterPropertiesSet() throws Exception {
if (mMessage == null) throw new BeanInitializationException("Property 'message' is not set.");
}
}
Note that this dialogue has a mandatory message property.
Now in the bean factory configuration file, we can provide some definitions for some dialogue beans:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="x" class="com.nuecho.rivr.cookbook.dialogue.Dialogue" scope="prototype">
<property name="message">
<value>This is a message from bean X.</value>
</property>
</bean>
<bean name="y" class="com.nuecho.rivr.cookbook.dialogue.Dialogue" scope="prototype">
<property name="message">
<value>This is a message from bean Y.</value>
</property>
</bean>
</beans>
We make those beans as prototype, meaning that they are reinstanciated each time they are requested. This is required if the dialogue class is stateful (which is not really the case in our example). A stateless dialogue bean can be shared across many "phone calls" while a stateful dialogue bean can only used by one call.
You need to set the application context in the web.xml:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
We can define a custom dialogue factory that will lookup the dialogue in the bean factory according to
the value of the dialogue
HTTP parameter:
public class DialogueFactory implements VoiceXmlDialogueFactory {
@Override
public VoiceXmlDialogue create(DialogueInitializationInfo<VoiceXmlInputTurn,
VoiceXmlOutputTurn, VoiceXmlDialogueContext> initializationInfo)
throws DialogueFactoryException {
if (!(initializationInfo instanceof WebDialogueInitializationInfo))
throw new DialogueFactoryException("Can only work in web mode.");
WebDialogueInitializationInfo<?, ?, ?> webInitializationInfo =
(WebDialogueInitializationInfo<?, ?, ?>) initializationInfo;
WebApplicationContext applicationContext =
getRequiredWebApplicationContext(webInitializationInfo.getServletContext());
String dialogueBeanName = webInitializationInfo.getHttpServletRequest().getParameter("dialogue");
if (dialogueBeanName == null)
throw new DialogueFactoryException("Missing HTTP parameter 'dialogue'.");
if (!applicationContext.containsBean(dialogueBeanName))
throw new DialogueFactoryException("No such dialogue: '" + dialogueBeanName + "'");
if (!applicationContext.isTypeMatch(dialogueBeanName, VoiceXmlDialogue.class))
throw new DialogueFactoryException("Bad type for dialogue: '"
+ dialogueBeanName
+ "'. Should be a "
+ VoiceXmlDialogue.class.getName());
VoiceXmlDialogue dialogue = applicationContext.getBean(dialogueBeanName, VoiceXmlDialogue.class);
if (dialogue == null)
throw new DialogueFactoryException("No such dialogue: '" + dialogueBeanName + "'");
return dialogue;
}
}
You need to place the name of the dialogue factory class in the web.xml
:
<init-param>
<param-name>com.nuecho.rivr.voicexml.dialogueFactory.class</param-name>
<param-value>com.nuecho.rivr.cookbook.dialogue.DialogueFactory</param-value>
</init-param>
You can try it with the following URI:
- http://localhost:8080/rivr-cookbook/dialogue?dialogue=x
- http://localhost:8080/rivr-cookbook/dialogue?dialogue=y
You can download or browse the complete code for this example at GitHub.This is a complete working application that you can build and run for yourself.
You can also clone the Rivr Cookbook repository and checkout this example:
git clone -b spring [email protected]:nuecho/rivr-cookbook.git
Then, to build and run it:
cd rivr-cookbook
./gradlew jettyRun
The VoiceXML dialogue should be available at http://localhost:8080/rivr-cookbook/dialogue
To stop the application, press Control-C in the console.
Rivr can be extended and customized in different ways. If for some reason, the output and last turns provided by Rivr aren't sufficient, there's always a way out. There are two mechanisms that can be used to extend the fonctionality of Rivr.
The first technique is to customize the VoiceXML generated by a turn. The typical case is to add a
platform-specific attribute on a VoiceXML element. To do that, we set an instance of
VoiceXmlDocumentAdapter
on the
VoiceXmlOutputTurn
or the
VoiceXmlLastTurn
.
The VoiceXmlDocumentAdapter
is an interface with one single method:
void adaptVoiceXmlDocument(Document voiceXmlDocument) throws VoiceXmlDocumentRenderingException
This method allows an implementing class to change the content of the VoiceXML document by modifying
the structure of the org.w3c.dom.Document
. The manipulation of this object is done by using the standard
DOM API.
The second technique is to create a new subclass of VoiceXmlOutputTurn
or
VoiceXmlLastTurn
. When creating this class, the developper has full
control over the generated VoiceXML content.
The following examples show how to realize both of the previous techniques.