4.2. A “thick-client” banking example

In 4.1, “Example: JSP parcel tracking application,” we saw how dynamic server page technology can be used to expose a database to a voice user using VoiceXML. In that example about half of the VoiceXML was static and half was generated dynamically. In this section we'll consider a different approach where almost all of the VoiceXML is static. This allows the VoiceXML application to be much better separated from the database logic compared to the previous example.

As a motivation for this approach, let's consider the following scenario. We are hired as a voice application consultant to build a voice application for a bank where customers can access their bank accounts. The bank staff is interested in minimizing the impact of outsourced components on their server-code and database as they want to maintain the code that runs on any of their servers.

We could propose the following as a solution: we develop a minimal bit of dynamic VoiceXML to be produced by their server, and then write and maintain the voice application as static VoiceXML whose sole interaction with the server is through those “interface documents.”

Summarizing the requirements, we need to provide them with:

  • a few dynamic VoiceXML pages to provide access to their server. These pages must be very simple and contain no user-interface to speak of, because once deployed we, the voice user interface developer, will not be able to change them;

  • a VoiceXML voice user interface implemented using static VoiceXML that need not run on the bank's servers and whose sole interface with the servers is through those simple dynamic VoiceXML pages.

While this scenario may seem unrealistic, it will serve two ulterior purposes:

  • it forces us to separate business logic and presentation;

  • it gives us an excuse to demonstrate a few sly techniques available to VoiceXML developers for retrieving dynamic information from an HTTP server.

That said, let's plan our attack. We'll want to come up with a representative call flow diagram to understand what this application needs to do. From this we can determine what server interaction is required - specifically, create an abstract API defining inputs and outputs. We can then implement this interface as a set of dynamic VoiceXML pages, and finally implement a first version of the voice user interface.

4.2.1. Analyzing the call flow

This banking application will be pretty simple. It will allow a user to check the balance on any of his or her accounts and to transfer funds between these accounts. Of course the caller will need to authenticate himself before doing anything. This will be done by collecting a banking card number and a PIN at the beginning of the call. Assuming that these match up, the user will be presented with a main menu containing the “Check Balance” and “Transfer Funds” options. This application can be summarized with the call flow diagram shown in Figure 4-3.

Figure 4-3. Call flow diagram for our banking application


4.2.2. Specifying server interaction

A quick inspection of this call flow reveals some obvious server interaction points, including “Retrieve accounts from server” and “Process transaction with server.” Let's think about voice user interface design for a second, though: In the dialog step that asks the caller to choose an account, we would want the speech-recognizer to recognize the names of accounts, such as “checking” or “money-market.” Furthermore, we don't want to update our VoiceXML code every time the bank invents a new type of account. Hence, we would like to have some sort of a dynamic server page that produces a grammar rule based on the account types that the customer actually has.

Let's try to define our server interface a bit more rigorously:

login(bankingCardNumber : string, pin : string) : Session

This interface method takes from the voice application a banking card number and a PIN, and returns a session handle that can be used by subsequent interface calls. In the case where the caller does not enter a valid banking card number or PIN this method should throw an exception.

getAccounts(userid : Session) : {accounts : Accounts[], status : string}

This interface method takes a session handle and returns that caller's bank accounts. It will also return a status flag that could indicate whether this operation passed or failed.

transferFunds(userid : Session, transferAmount : int,fromAccountName : string, toAccountName : string) : { error :string}

This interface method takes the handle of a session, a transfer amount, and the names of the source and destination accounts for the transfer, and attempts to execute the transaction. If successful, this method will return an error of none. Otherwise it will return an error value that describes why the transaction failed.

getMenuGrammar(userid : Session)

This interface method takes a user ID and a session handle and returns a grammar rule containing each of the customers bank-account names.

There are a few non-obvious “types” defined here. The first is the Session type which is required because of stateless nature of HTTP. Notice that transferFunds() and getMenuGrammar() do not take banking card number or PIN. We assume this information is embodied in the Session object. When the user is authenticated and the voice application calls the getAccounts() method, we need to pass a session handle down to the VoiceXML interpreter, so all subsequent interface calls can be associated with that initial login.

Another non-trivial “type” is Account. For the purposes of our application, it must embody only an account name and a balance. In this implementation we'll represent monetary amounts in cents.

4.2.3. Implementing dynamic VoiceXML interface

We know abstractly what this interface needs to do. Now we must figure out the nuts and bolts. Let's start by surveying some of the ways in which VoiceXML can interact with an HTTP server that are appropriate to this solution:

the subdialog element

The namelist attribute of the subdialog element allows us to specify a list of VoiceXML variables to post up to the HTTP server. The server can produce a document that returns values back to the calling VoiceXML application.

the script element

The src attribute allows us to defer the body of the script element to another document. This document could be served by a dynamic page. The script element does not provide any way to pass parameters to the HTTP server. For this we must use an HTTP cookie (described below).

the grammar element

As with script, grammar's src attribute allows us to defer the grammar definition to another document. This document could be served by a dynamic page. The grammar element does not provide any way to pass parameters to the HTTP server. For this we must use an HTTP cookie (described below).

HTTP cookies

Since HTTP is stateless, the Web browser community invented HTTP cookies - name/value pairs that can be stored in the browser. A VoiceXML interpreter can be thought of as a voice analog of a Web browser. As such, many of the VoiceXML interpreters support HTTP cookies. Beware, however, that there still are many that do not. For this example, we will assume cookies are supported.

Let's figure out which mode of server interaction is the most appropriate for each of our interface methods. Both login() and transferFunds() need to take parameters other than just the session. The only way to do this is with the submit or subdialog elements. We shall for now take on faith that subdialog is a better option - the logic for this will bear out as we work through the code.

We shall implement our “check balance” and “transfer funds” menus using a dynamic grammar element that will use the getMenuGrammar() interface document. As for getAccounts(), we can use either a subdialog or a script. As we are already using a subdialog for a couple of our interface methods, let's access getAccounts() from a dynamic script.

One remaining implementation issue remains: How do we implement our Session? Even though this is outside the VoiceXML Specification, we can save ourselves some trouble by deploying this application on a VoiceXML interpreter that supports HTTP cookies. We can then return the session in the form of a cookie to the VoiceXML interpreter. Let's name this cookie userid.

We will implement each of our interface documents using JSP. Each interface method will have a corresponding JSP page: login() will be implemented in login.jsp, getAccounts() in getAccounts.jsp, etc.

4.2.4. login.jsp

The login() method will take, as HTTP POST parameters, a banking card number and a PIN. The main effect of this subdialog call will be to set the userid cookie on the VoiceXML interpreter. The actual VoiceXML response need only contain a VoiceXML document with a form that merely returns execution flow back to the calling VoiceXML application. A simple implementation of this interface is shown in Example 4-8.

Example 4-8. A login.jsp implementation
<%@ page import="java.util.*" %>
<%@ page import="java.sql.*" %>
<%@ page import="com.banking.*" %>

<%
String cardNo = request.getParameter("bankingCardNumber");
String pin = request.getParameter("pin");

BankService bserv = new BankService();
int userid = bserv.getUserID(cardNo, pin);
Cookie ck = new Cookie("BankCustomerID", 
                         (new Integer(userid)).toString());
response.addCookie(ck);
%>

<vxml version="2.0" application="BankingApp.jsp">
  <form>
    <block>
      <return/>
    </block>
  </form>
</vxml>

Here we receive the bankingCardNumber and pin HTTP POST parameters, look up the userid value using the BankService class provided by our colleagues at the bank, set the userid cookie in the response, then return a trivial VoiceXML form.

A VoiceXML developer would use this interface simply by calling it as a subdialog, for example:

...
<subdialog src="login.jsp" method="post" 
           namelist="bankingCardNumber pin"/>
...

We assume that there are fields or variables in the VoiceXML application named bankingCardNumber and pin. From a VoiceXML perspective, this subdialog gets called, does nothing, and returns, allowing the calling dialog to continue on its merry way. What has happened under the covers, of course, is the setting of the userid cookie.

4.2.5. getAccounts.jsp

This interface will be called from a script element. Therefore, it must return ECMAScript to be executed on the VoiceXML interpreter. In order to run properly, this document must have access to the session cookie userid, so we need to make sure that we have called login.jsp prior to calling this document.

Recall that the chief concern of this interface is returning a list of accounts. Since our typical use of this list of accounts will be of the form “For account named X, tell me the balance,” we can take advantage of ECMAScript's name-indexed arrays to provide this functionality. For a particular user, getAccounts.jsp might return the ECMAScript code shown in Example 4-9.

Example 4-9. An example of ECMAScript returned by getAccounts.jsp
application.noAccts = 3;
application.status = 'true';
application.accounts = new Array();
application.accounts["checking"] = 1513; 
application.accounts["savings"] = 824580; 
application.accounts["money-market"] = 1042325; 

Note that we're setting variables application.noAccts, application.status, and application.accounts. The correct functioning of this code presupposes the declaration of the variables noAccts, status, and accounts in the application root document. The application. prefix indicates that these are variables defined in the application scope. As such, they are accessible from any document within the same VoiceXML application. (For a review on how VoiceXML application are organized, see 2.10, “Organizing code into VoiceXML applications,”.)

In order to generate this output, getAccounts.jsp must first read the userid cookie as shown in Example 4-10.

Example 4-10. Reading the userid cookie in getAccounts.jsp
<%@ page import="java.util.*" %>
<%@ page import="java.sql.*" %>
<%@ page import="com.banking.*" %>

<%  
String useridstr = "";
Cookie[] cks = request.getCookies();
for(int i=0;i<cks.length;i++){
  if(cks[i].getName().equals("BankCustomerID")){
    useridstr = cks[i].getValue();
    break;
  }
}
int userid = new Integer(useridstr).intValue();
%>

Once the userid is established, we can give it to the BankService object to retrieve the customer's accounts as Java objects.

We then convert this array of Java objects into ECMAScript code that, when run on the VoiceXML interpreter, will provide our voice application with all of the information it needs.

Finally, we must handle exceptions thrown by the BankService by updating the application-scoped VoiceXML variables: application.status and application.reason. These variables also must be defined in the application root document. The code to do this is shown in Example 4-11.

Example 4-11. Retrieving and rendering the customer's accounts in getAccounts.jsp
<% BankService bservice = new BankService();
try{
  Account[] accts = bservice.getAccounts(userid);
%>
application.noAccts = <%=accts.length%>;
application.status = 'true';
application.accounts = new Array();

<%  for(int i=0;i<accts.length;i++){%>

application.accounts["<%=accts[i].name%>"] = <%=accts[i].balance%>; 

<%  } %>
<% }catch(BankingException bex){ %>
application.status = 'false';
application.reason = '<%=bex.message%>';
<% } %>

An advantage to implementing getAccounts.jsp to return ECMAScript is that we can insert script elements in any place where executable elements are allowed, as opposed to subdialogs which must function strictly as form items. We will see the utility of this when we implement the “transfer funds” dialog.

4.2.6. getMenuGrammar.jsp

This dynamic document will return a simple GSL (Nuance's Grammar Specification Language) grammar rule capable of understanding all of the customer's bank account names. (See 2.9, “Grammars,” for more information on grammars.) For example, it would be able to understand “Checking” and “Savings” for a customer who has a checking account and a savings account. For simple grammars like this, GSL is very easy to render. We can render the above “Checking” / “Savings” grammar as follows:

Accounts
   [ checking savings]

We'll use the same cookie technique that we used in the getAccounts.jsp document to determine whose accounts we are generating the grammar rule for. This is shown in Example 4-12.

Example 4-12. Retrieving user information based on the userid cookie
<%@ page import="java.util.*" %>;;
<%@ page import="java.sql.*" %>;;
<%@ page import="com.banking.*" %>;;
<%  
String useridstr = "";
Cookie[] cks = request.getCookies();
for(int i=0;i<cks.length;i++){
  if(cks[i].getName().equals("BankCustomerID")){
    useridstr = cks[i].getValue();
    break;
  }
}
int userid = new Integer(useridstr).intValue();
BankService bservice = new BankService();
try{
  Account[] accts = bservice.getAccounts(userid);
%>;;

You might notice the ;; symbols at the end of each JSP code line. This double semi-colon symbol denotes the start of a one-line comment in GSL. There is no matching end-of-comment symbol as this is analogous to the // comment in C++ and Java. Had we not put these at the end of each line, there would instead be a carriage return inserted in the rendered JSP. These carriage returns would confuse the GSL interpreter, but marking these lines as comments solves the problem. Our other option would have been to write all of the JSP code on a single line, but for the sake of readability we chose the commenting route.

The remainder of getMenuGrammar.jsp generates the actual GSL grammar. This code is shown in Example 4-13.

Example 4-13. The GSL generating portion of getMenuGrammar.jsp
Accounts
   [<%for(int i=0;i<accts.length;i++){%> <%=accts[i].name%><% }%>]
<% }catch(BankingException bex){ %>
Accounts
   []
<% } %>

Assuming there were no BankingExceptions thrown, we generate the GSL grammar rule as one might expect. If there was an exception, however, we generate an empty grammar rule. This JSP might generate output like that shown in Example 4-14.

Example 4-14. An example output from getMenuGrammar.jsp
;;
;;
;;
;;
Accounts
   [ checking savings money-market]

This generates a grammar rule called Accounts that can recognize the words “checking,” “savings,” and “money-market.” We assume that the bank account names don't contain any spaces or other confusing characters. If we were not permitted this assumption, we could write a method to “clean” the names thus making them suitable for GSL. We also assume that they will be comprised of common names typically found in a speech-recognition engine's dictionary. Here we see the ;; comment markers explained previously.

4.2.7. transferFunds.jsp

Our last interface document is transferFunds.jsp, which takes the userid cookie, a transfer amount, and the names of the to- and from-accounts. If there are no problems, it executes the transfer. This will return to the VoiceXML interpreter a form that can be called as a subdialog that returns a single variable, error. This variable would contain the error message associated with any transaction failure.

The pattern for these interface documents should be pretty clear by now. We start by collecting the input data from the HTTP POST parameters and the userid cookie. This portion of transferFunds.jsp is shown in Example 4-15.

Example 4-15. Collecting input values in transferFunds.jsp
<%@ page import="java.util.*" %>
<%@ page import="java.sql.*" %>
<%@ page import="com.banking.*" %>
<vxml version="2.0">
  <form>
    <var name="error" expr="'none'"/>

<%
String amountstr = request.getParameter("transferAmount");

String fromAccountName = request.getParameter("fromAccountName");
String toAccountName = request.getParameter("toAccountName");
int transferAmount = (new Float(amountstr)).intValue();
String useridstr = "";
Cookie[] cks = request.getCookies();
for(int i=0;i<cks.length;i++){
  if(cks[i].getName().equals("BankCustomerID")){
    useridstr = cks[i].getValue();
    break;
  }
}
int userid = new Integer(useridstr).intValue();

Next we execute the business logic for a transfer by instantiating a BankService object and calling its transferFunds() method. If this succeeds, we simply produce a VoiceXML document that returns the error variable to the calling VoiceXML dialog. If the operation fails, we produce a VoiceXML document that first sets the error variable to a descriptive message associated with the BankingException and then returns this to the calling dialog. This is shown in Example 4-16.

Example 4-16. Executing the transfer or returning an error to the calling dialog
BankService bservice = new BankService();
try{
  bservice.transferFunds(userid, fromAccountName, 
                         toAccountName, transferAmount);
}catch(BankingException bex){ %>
    <block><assign name="error" expr="'<%=bex.message%>'"/></block>
<%} %>
    <block>
      <return namelist="error"/>
    </block>
  </form>
</vxml>

4.2.8. Implementing a voice user interface

At this point we have all the access to the bank's server we need through four simple dynamic VoiceXML pages. We now need to develop the voice user interface.

Looking back at the dialog call flow diagram in Figure 4-3, we find that we can break our application down into four dialogs:

  • an authentication dialog,

  • a main menu,

  • a “check balances” dialog, and

  • a “transfer funds” dialog.

Before we begin implementing our dialogs, however, we need to think about our VoiceXML application's structure. Recall that the getAccounts.jsp interface document returns ECMAScript that sets application-scope variables. We need to designate one document as our application root document and declare these global variables. Let's call our application root document BankingApp.vxml. These global declarations are shown in Example 4-17.

Example 4-17. Global variable declarations in BankingApp.vxml
<?xml version="1.0" encoding="iso-8859-1"?>
<vxml version="2.0">
  <property name="fetchhint" value="safe"/>
  <var name="accounts"/>
  <var name="status"/>
  <var name="noAccts"/>

You might notice that we set the fetchhint property to safe. While the efficacy of this property varies from one VoiceXML implementation to another (it is, after all, only a fetch hint), it can be crucial for the proper functioning of our dynamic grammars. The getMenuGrammar.jsp document will only return the right results if it is called after login.jsp has set the userid cookie. The default fetching behavior of a VoiceXML interpreter is to try to fetch referred resources, such as audio files, grammars, and scripts, concurrent to the loading and execution of the VoiceXML. This usually improves system performance. In our case, however, the interpreter may retrieve the getMenuGrammar.jsp grammar before login.jsp has been executed. By setting the fetchhint property to safe, we instruct the interpreter to fetch each resource only when it is actually needed. On VoiceXML platforms that don't heed the fetchhint property, we may need to go one step further: To force the point, we can put any VoiceXML grammar elements that require the userid cookie in another file. The prefetching will not typically cross document boundaries.

Next, let's define a handy ECMAScript function for converting a money amount in cents into a phrase spoken exactly as we would like. Specifically, it would convert something like 703 into “7 dollars and 3 cents.” This function is shown in Example 4-18.

Example 4-18. Our function to pronounce money amounts
<script><![CDATA[
function sayAsMoney(tot){
var ans = "";
ans += Math.floor(tot/100) + " dollars and ";
ans += (tot % 100) + " cents."
return ans;
}
]]></script>

Note that this function, being in the application root document, is globally scoped and available from anywhere within our application.

Let's continue with the authentication dialog. We'll put this dialog right in the application root document to ensure the root document is loaded first. This dialog needs to collect the banking card number and PIN from the caller, and then run the login.jsp interface document. This document will then set the userid cookie, so subsequent interface-document calls will know which customer is on this call. The welcome message, authentication, and setting of the userid cookie is shown in Example 4-19.

Example 4-19. Collecting user input in the authentication dialog
  <form id="Authenticate">
    <block>Welcome to the voice banking system.</block>

    <field name="bankingCardNumber" type="digits?length=16">
      <prompt>
        Please say or enter your 16 digit banking card number.
      </prompt>
    </field>

    <field name="pin" type="digits?length=4">
      <prompt>
        Please say or enter your 4 digit pin-code.
      </prompt>
    </field>

    <block>
      <prompt> 
        Please wait while I look up your records.
      </prompt>
    </block>

    <subdialog src="login.jsp" method="post" 
               namelist="bankingCardNumber pin">
      <error>
        <prompt>
          I had a problem authenticating your
          banking card number or PIN.
        </prompt>
      </error>
    </subdialog>

Next, we want to look up this customer's account information as shown in Example 4-20.

Example 4-20. Validating the user in the authentication dialog
    <block>
      <var name="reason"/>
      <script src="getAccounts.jsp"/>

      <if cond="status!='true'">
        <prompt>
          I'm sorry. I can not authenticate you.
          <value expr="reason"/>
          Please try again.
        </prompt>
        <clear/>
        <reprompt/>
      </if>
    </block>

    <block>
      <goto next="#CallerMenu"/>
    </block>
  </form>

Recall that the getAccounts.jsp interface document will set the global variable status to false if there was a problem authenticating the user. If this is the case, we ask the user to try again. If there is no problem, we go on to the CallerMenu dialog.

In this example, we put the CallerMenu dialog in the application root document immediately following the authentication dialog. We could have just as easily put CallerMenu in another file, but as it is fairly small, keeping dialogs centralized is convenient. This dialog followed by the closing tag for the vxml element is shown in Example 4-21.

Example 4-21. The CallerMenu dialog
  <menu id="CallerMenu">
    <prompt>
      To check an account balance, say "check balance". 
      To transfer funds, say "transfer funds".
    </prompt>
    <choice next="TransferFundsDialog.vxml">transfer funds</choice>
    <choice next="CheckBalance.vxml">check balance</choice>
  </menu>
</vxml>

This dialog provides the customer with a menu of options: check an account balance or transfer funds between accounts. We take advantage of the menu element for this functionality.

The CheckBalance dialog asks the user which account is to be checked. It then uses the grammar dynamically generated by getMenuGrammar.jsp to understand the user's answer. The VoiceXML for this dialog is shown in Example 4-22.

Example 4-22. The CheckBalance dialog
<?xml version="1.0" encoding="iso-8859-1"?>
<vxml version="2.0" application="BankingApp.vxml">
  <form id="CheckBalance">
    <field name="acct">
      <prompt>
        Please tell me which account you would like to check.
      </prompt>
      <grammar src="getMenuGrammar.jsp#Accounts" />
      <filled>
        <prompt>You said <value expr="acct"/>.</prompt>
      </filled>
      <nomatch>
        <prompt>Sorry. I don't know of that account.</prompt>
      </nomatch>
    </field>

    <block>
      The account balance in your <value expr="acct"/> account is
      <value expr="sayAsMoney(application.accounts[acct])"/>.
      <goto next="BankingApp.jsp#CallerMenu"/>
    </block>
  </form>

</vxml>

If this customer had a checking account but no savings account, a call scenario might go as shown in Example 4-23. Recall that we use our global ECMAScript function sayAsMoney() to render the phrase “seven dollars and three cents” from 703.

Example 4-23. A “check balance” scenario
IVR     : Please tell me which account you would like to check.
Human   : Savings.
IVR     : Sorry. I don't know of that account.
Human   : Checking.
IVR     : The account balance in your Checking account is seven 
          dollars and three cents.


Notice how the application attribute of the vxml element is set to BankingApp.vxml. This indicates that this document is a leaf document of the BankingApp.vxml application root document. As such it has access to all of the global variables and functions defined in BankingApp.vxml. (See 2.10, “Organizing code into VoiceXML applications,” for more information.)

Our only remaining task is to write the TransferFunds dialog. We create it as a separate document, TransferFundsDialog.vxml. This dialog needs to collect the names of the accounts from which and to which money is being transferred as well as the transfer amount. For this proof-of-concept deployment we restrict the caller to specifying whole-dollar amounts. This portion of the TransferFunds dialog is shown in Example 4-24.

Example 4-24. Collecting account names and transfer amount
<?xml version="1.0" encoding="iso-8859-1"?>
<vxml version="2.0" application="BankingApp.jsp">
  <form id="TransferFunds">
    <block>Transferring funds.</block>
    <field name="fromAccountName">
      <prompt>
        From which account would you like to transfer funds?
      </prompt>
      <grammar src="getMenuGrammar.jsp#Accounts" />
      <filled>
        <prompt>You said <value expr="fromAccountName"/>.</prompt>
      </filled>
      <nomatch>
        <prompt>Sorry. I don't know of that account.</prompt>
      </nomatch>
    </field>

    <field name="toAccountName">
      <prompt>
        To which account would you like to transfer funds?
      </prompt>
      <grammar src="getMenuGrammar.jsp#Accounts" />
      <filled>
        <prompt>You said <value expr="toAccountName"/>.</prompt>
      </filled>
      <nomatch>
        <prompt>Sorry. I don't know of that account.</prompt>
      </nomatch>
    </field>

    <field name="transferAmount" type="number">
      <prompt>
        How many dollars would you like to transfer from your 
        <value expr="fromAccountName"/> to your 
        <value expr="toAccountName"/>?
      </prompt>
      <filled>
        <assign name="transferAmount" expr="transferAmount*100"/>
      </filled>
    </field>

Once we've collected the vital information, rather than leave our customers' fortunes to the fate of the speech recognition software, we read the transaction back to the customer and ask him or her to explicitly confirm the transaction by saying “confirm.” This is shown in Example 4-25.

Example 4-25. Transfer confirmation
    <field name="conf">
      <prompt>
        You have requested a transfer in the amount of
        <value expr="sayAsMoney(transferAmount)"/> from your
        <value expr="fromAccountName"/> account to your
        <value expr="toAccountName"/> account. If this is correct
        say "confirm". Otherwise say "cancel".
      </prompt>
      <grammar>[confirm cancel]</grammar>
      <catch event="nomatch noinput">
        Please say either confirm or cancel.
      </catch>
      <filled>
        <if cond="conf=='confirm'">
          <prompt>OK.</prompt>
         <else/>
          <prompt>Canceling this transaction request.</prompt>
          <goto next="BankingApplication.jsp#CallerMenu"/>
        </if>
      </filled>
    </field>

Once the customer confirms, we call the transferFunds.jsp interface document to consummate the transaction. If an error occurred, we inform the caller of the error and then exit immediately to the CallerMenu dialog. If, however, the transaction went smoothly, we simply tell the customer “Transaction complete,” read out the balances of the affected accounts, and return to the CallerMenu dialog. This VoiceXML is shown in Example 4-26.

Example 4-26. Executing the transfer
    <subdialog name="transferAction" src="transferFunds.jsp" 
            method="post" 
            namelist="transferAmount fromAccountName toAccountName">
      <filled>
        <if cond="transferAction.error != 'none'">
          <prompt>
            Sorry. I couldn't complete this transaction.
            <value expr="error"/>
          </prompt>
          <goto next="BankingApp.vxml#CallerMenu"/>
         <else/>
          <prompt>Transaction complete.</prompt>
        </if>
      </filled>
    </subdialog>

    <block>
      <script src="getAccounts.jsp"/>
      <prompt>
        Your new <value expr="fromAccountName"/> account balance is
        <value expr="application.accounts[fromAccountName]"/>.

        Your new <value expr="toAccountName"/> account balance is
        <value expr="application.accounts[toAccountName]"/>.
      </prompt>
    </block>

    <block>
      <goto next="BankingApp.jsp#CallerMenu"/>
    </block>
  </form>

</vxml>

4.2.9. Conclusions

This banking application demonstrates some different techniques for communicating with a server using VoiceXML and HTTP. By limiting the server interaction code to a handful of dynamic documents that generate very simple VoiceXML and ECMAScript, we can continue to refine our voice use interface without having to touch any of the server code.

While this application provides a basic proof of concept, there are many voice user interface improvements that need to be made before making this into a production application. The most notable addition would be some functionality for exiting the call flow. Another important dialog feature required here is natural sounding confirmation prompts that would allow the user to correct any speech recognition errors. For example, if the user said “Checking” but the speech recognizer heard “Savings,” we would want to give the user a chance to correct this error.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.58.197.25