在過去五年中,Web 得到了比以往任何時候都長足的發展。一度主要以文本為基礎的軟件 程序媒介 “Web 浏覽器” 現已發展成為一種可供任何具有連通性的設備使用的信息源。最 早列入可訪問 Web 頁面的設備列表的是移動電話,隨後是尋呼機、手持設備、個人電子助理 和其他任何可通過無線連接連入 Web 的設備。在最近幾年,電話也加入了這一陣營,通過普 通電話線路使用 Web 程序的呼聲也越來越高。
這種最新型的應用程序(用戶通過電話訪問在線服務)的更恰當的名稱是電話應用程序( telephone application)。顯而易見,由於電話無法用來 “單擊一個鏈接”,應用程序交 互幾乎全部是通過語音處理的。用戶不是采用單擊鏈接的方式,而是說出 “賬戶信息” 或 使用鍵盤按預先錄制好的指令進行操作。
通過現有(或略加修改的)Web 應用程序為電話提供服務的能力是一種強大的想法,也是 許多 Web 開發人員都渴望探索的領域之一。關於 Web 應用程序與電話應用程序,要了解的 最重要的一件事就是您實際上可以使用相同的技術組合來創建這兩種應用程序。HTML、XHTML 和 XML 是 Web 界面之下最常用的三種底層技術,VoiceXML(或簡稱為 VXML)則是一種密切 相關的技術,它使得電話客戶機可以利用 Web 交互。JavaServer Pages 與 servlet、PHP 腳本以及 Ruby 應用程序均可響應電話請求,就像響應那些進入手持設備或 Web 浏覽器的請 求一樣輕松。在這篇文章中,我們主要關注使用 Java 平台為簡單的 VoiceXML 應用程序提 供服務,但您可將本文介紹的方法同樣地應用到 PHP、Perl 或您選擇的任何編程語言。
VoiceXML、CCXML 還是 CallXML?
構建語音應用程序最常用的標准就是 VoiceXML。絕大多數 VXML 浏覽器都支持 VoiceXML 2.0,本文通篇將使用這個版本的 VXML。VXML 符合 W3C 規范且發展迅速,目前的版本依然 是 v2.1。VXML 3.0 即將推出。
CCXML 是 Call Control XML 的縮寫,也是電話標記方面符合 W3C 規范的最新一員。 CCXML 比大多數 VoiceXML 實現更為高級,提供了對回叫、事件偵聽器和多路及多方會話的 支持。但除非您特別需要這些屬性,否則最好的選擇或許是繼續使用 VoiceXML,VoiceXML 更加穩定,應用也更廣泛。
CallXML 是特定於 Voxeo 的一種平台。CallXML 學習起來非常輕松,並提供了對按鍵電 話(touchtone)輸入的支持(請注意,它並不支持語音識別)。CallXML 的最大缺陷就是特 定於廠商。Voxeo 是一個非常出色的站點,具有無數資源,但被一家廠商鎖定絕對不是個好 主意。此時,大多數開發人員會再次發現,VoiceXML 更適合他們的需求。
VoiceXML 101
在接觸 VoiceXML 世界的 Java 方面之前,您應大致理解 VoiceXML 應用程序的工作原理 。之後,我將為您介紹一個非常簡單的 VoiceXML 應用程序。示例應用程序將使您能夠查看 VXML 文件,同時確保您有權訪問(且可使用)Voxeo 的 call-assignment 服務,這個服務 對本文後面的內容非常關鍵。
一個簡單的 VXML 頁面
VoiceXML 最少要以一個 VXML 文件開始,使用 VoiceXML 風格的 XML 版本告訴電話應用 程序它們應該以及能夠作些什麼。清單 1 給出了一個非常簡單的 VXML 文件。將這個文件保 存到您的本機上(可通過 下載 部分下載完整的示例源代碼,但無論如何,您應該養成自己 處理此類文件的習慣)。
清單 1. 一個非常簡單的 VXML 文件
<?xml version="1.0" encoding="UTF-8"?>
<vxml version="2.1">
<form>
<block>
<prompt>
Things are working correctly! Congratulations.
</prompt>
</block>
</form>
</vxml>
對於 VoiceXML 來說,這非常基礎,如果您對語法的了解還不夠清楚,請查看 參考資料 中列出的其他 VoiceXML 文章。清單 1 中的 VXML 文件只包含一條提示信息,未提供任何交 互功能,在處理 Java 代碼的一節中,您將看到更高級的用法。但目前,使用這個簡單的測 試用例來確保您的環境工作正常。
上傳應用程序
接下來,將您的 VXML 文件放在某個可以訪問的位置。如果您有 ISP,只需將 VXML 文件 上傳到您的 Web 站點,您可能也會希望在 Web 根目錄下為您的 VoiceXML 文件創建一個目 錄,例如 /voicexml 或 /voice。確保這些目錄和文件可通過 Web 訪問(如果您不清楚如何 進行這些操作,請咨詢您的系統管理員或 ISP)。
如果您沒有 ISP,那麼可以在 Voxeo 注冊,以便使用該站點的 File Manager。您應已建 立了一個 Voxeo 賬戶,它附帶 10 MB 的主機空間,因此這是個不錯的免費選擇。(10 MB 可以容納大量 VXML 文件!)
使 VXML 應用程序聯機之後,我們還想確定能夠通過在 Web 浏覽器中輸入 URL 來訪問它 。根據您所使用的具體浏覽器不同,可能會要求您下載 XML 文件,也可能會在您的浏覽器中 以某種形式呈現它。這只是一個測試,確保您的 VXML 可用,因此即使您的計算機沒有開始 跟您交談,也不要太過憂慮。VXML 聯機後,也就作好了將其與一個電話號碼連接的准備。
為您的應用程序分配一個電話號碼
與傳統的 Web 應用程序不同,您無法直接打開 Web 浏覽器然後浏覽您的 VXML 文件,至 少在您希望獲得語音應答時不能這樣做。為了測試基於電話的應用程序,您顯然需要一台電 話,這就意味著一個呼叫號碼。有許多高成本的方法可以將號碼映射到 VoiceXML 應用程序 ,但對於測試、登台(staging)和開發而言,Voxeo 提供了一種出色的免費映射服務。
導航到 Voxeo.com,登錄(使用頁面左上角的字段)。在 Account 菜單中選擇 Application Manager,如 圖 1 所示。
圖 1. 使用 Voxeo Application Manager
選擇 Add Application,然後選擇 VoiceXML 2.0 作為部署平台。
接下來,提供您的 VXML 文件的 URL,另外還有您的應用程序的名稱,您可以按照自己的 偏好任選名稱。圖 2 展示了訪問我的 VXML 文件的設置。從 Application Phone Number 下 拉菜單中選擇 Staging 選項。這將為應用程序分配一個臨時登台電話號碼(temporary staging phone number),以使您可以真正地用您自己的電話呼叫這個號碼。
圖 2. 將一個 VXML 文件映射到一個電話號碼
單擊 Create Application,Voxeo 將為您的應用程序分配一些電話號碼。圖 3 展示了最 終屏幕(略微向下滾動了一點),以及 VXML 文件的所有訪問點。
圖 3. 成功映射!
這一功能值得您花時間去注冊 Voxeo,您現在可以通過長途電話號碼、800 免費電話號碼 和 Skype 訪問您的 VXML 文件,而這些方法還只是其中的一小部分。這非常好,因為您不必 使用 Voxeo 工具去測試應用程序。更好的是,您可以讓您的老板在無需具備 Voxeo 站點賬 戶的情況下完成測試!
測試應用程序
剩下的工作就是呼叫一個 Voxeo 提供的號碼。撥號後,您的 VXML 應用程序應獲取號碼 ,並讓您知道(用一種單調機械的聲音):“Things are working correctly! Congratulations.”
好,就是這樣:在大約五分鐘內,您使您的電話與一個 XML 文件進行了交談。現在就可 以看看 Java 代碼了,並了解如何動態地生成 VXML。
Java 和 VXML
這裡,大多數 Java 開發人員都試圖在自己的 Java Servlet 中手工編碼 VXML,添加數 百行的 out.println() 語句、為輸出的內容類型而操心,通常也會給許多應用程序增加嚴重 且不必要的復雜性。動手處理那些較為復雜的編程任務之前(只要應用得當,它們都是很有 用的),請先通過本節了解一些關於 VoiceXML Servlet 編程的最基本的內容。
創建一個 VXML 文件的原型
首先要開發 VXML 文件。不要打開一個 IDE 並開始編寫 Java 代碼,而是啟動一個文本 編輯器,忍住立即添加 package 和 import 語句的渴望。構建一個簡單的 VXML 文件,就像 本文前面給出的示例那樣。
例如,清單 2 是又一個非常基礎的 VXML 文件。它是一個語音識別 VXML 文件,接入一 個恰當的設備並提供某些關於呼叫選擇的注釋。
清單 2. 另外一個基本的 VXML 文件
<?xml version="1.0" encoding="UTF-8"?>
<vxml version="2.1">
<form id="MainMenu">
<field name="instrument">
<prompt>What is your favorite musical instrument?</prompt>
<!-- Insert an inline grammar -->
<grammar type="text/gsl">
[guitar mandolin dobro (violin fiddle) banjo]
</grammar>
<!-- Handle the case when they give no answer -->
<noinput>
Did you say something? I didn't hear you.
<reprompt />
</noinput>
<!-- Handle the case when no match is found -->
<nomatch>
I suppose that's OK, but it's not on my top five.
Want to try again?
<reprompt />
</nomatch>
</field>
<!-- Handle the various options. -->
<filled namelist="instrument">
<if cond="instrument == 'guitar'">
<prompt>That's right! Hang up and go practice.</prompt>
<elseif cond="instrument == 'mandolin'" />
<prompt>Nice... and only four strings to keep in tune.</prompt>
<elseif cond="instrument == 'dobro'" />
<prompt>Boy, that's no fun to learn, is it?</prompt>
<elseif cond="instrument == 'violin'" />
<prompt>We call that a fiddle, Mr. Fancy Pants.</prompt>
<elseif cond="instrument == 'fiddle'" />
<prompt>Does playing classical music on a
fiddle make it a violin?</prompt>
<elseif cond="instrument == 'banjo'" />
<prompt>Wow, I hope you live alone.</prompt>
</if>
</filled>
</form>
</vxml>
編寫這個 VXML、保存它、將它上傳到 ISP,然後為它分配一個號碼。只有在您完成所有 這些步驟後 —— 確保您的 VXML 正常工作,才是准備好了,可以開始 考慮編寫 Java 代碼 。
如果您直接跳到 Java,那麼很可能會導致輸出中出錯,代碼中也會出錯。結果是要在一 個 Web 框架內嘗試同步調試一個 VXML 文件(XML)和一個 Servlet(Java),這種調試極 其艱難。不要添加所有這些變量(沒有雙關的意思),務必從一個可正常工作的 VXML 文件 入手。然後 准備運行 Java 代碼。
讀入文件
准備好 VXML 可供使用後,您也就為開始編碼作好了最終的准備。首先從一個僅載入 VXML 文件的 Servlet 開始。清單 3 是一個實現此功能的 Servlet —— 載入 清單 2 中開 發的 VXML。這段代碼沒有任何輸出,所以期望值暫時不要太高。
清單 3. 載入一個 VXML 文件
package com.ibm.vxml;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.*;
public class VoiceXMLServlet extends HttpServlet {
private static final String VXML_FILENAME =
"simple-voice_recog.xml";
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
String vxmlDir = getServletContext().getInitParameter("vxml-dir");
BufferedInputStream bis = null;
ServletOutputStream out = null;
try {
// Load the VXML file
File vxml = new File(vxmlDir + "/" + VXML_FILENAME);
FileInputStream fis = new FileInputStream(vxml);
bis = new BufferedInputStream(fis);
// Output the VXML file
int readBytes = 0;
while ((readBytes = bis.read()) != -1) {
// output the VXML
}
} finally {
if (out != null) out.close();
if (bis != null) bis.close();
}
}
}
這段代碼非常直觀。它載入一個 XML 文件 —— 通過 servlet 的配置上下文中的目錄和 一個常量文件名指定,然後遍歷文件內容。您要將文件的路徑硬編碼到 servlet 中,但至少 將目錄名存儲到 Web.xml 文件中是一個非常不錯的主意,此文件位於 servlet 上下文的 WEB-INF/ 目錄下。清單 4 展示了 Web.xml 中的上下文參數。
清單 4. servlet 的上下文參數
<context-param>
<param-name>vxml-dir</param-name>
<param-value>/path-to-your-voicexml-dir/voicexml</param-value>
</context-param>
若編譯 servlet 並嘗試在 Web 浏覽器中載入它,您只會看到一個空白的屏幕,同樣,您 應確保至少會看到這樣的空白屏幕。如果得到錯誤,就需要予以更正。例如,常常會出現文 件訪問問題或 VXML 文件路徑錄入錯誤。一旦得到了空白屏幕,也就准備好實際輸出 VXML 文件了。
從 servlet 中輸出 VXML
首先,您需要訪問一個輸出對象,這樣才能向浏覽器發送內容。這非常簡單:
// Load the VXML file
File vxml = new File(vxmlDir + "/" + VXML_FILENAME);
FileInputStream fis = new FileInputStream(vxml);
bis = new BufferedInputStream(fis);
// Let the browser know that XML is coming
out = res.getOutputStream();
從文件提取內容也非常簡單,只要使用一行代碼即可:
// Output the VXML file
int readBytes = 0;
while ((readBytes = bis.read()) != -1) {
// output the VXML
out.write(readBytes);
}
雖然上述代碼看似已經足夠,但您依然需要告知浏覽器您正在向它發送 XML。切記,浏覽 器用於 HTML,某些浏覽器可能無法順利接收 XML。您可設置內容類型,也可設置內容的長度 ,只要再次使用 HttpServletResponse 對象即可:
// Let the browser know that XML is coming
out = res.getOutputStream();
res.setContentType("text/xml");
res.setContentLength((int)vxml.length());
清單 5 展示了添加到前文介紹的 清單 3 給出的 servlet 中的所有代碼。
清單 5. 完整且准備好載入 VXML 文件的 VoiceXMLServlet
package com.ibm.vxml;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.*;
public class VoiceXMLServlet extends HttpServlet {
private static final String VXML_FILENAME =
"simple-voice_recog.xml";
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
String vxmlDir = getServletContext().getInitParameter("vxml-dir");
BufferedInputStream bis = null;
ServletOutputStream out = null;
try {
// Load the VXML file
File vxml = new File(vxmlDir + "/" + VXML_FILENAME);
FileInputStream fis = new FileInputStream(vxml);
bis = new BufferedInputStream(fis);
// Let the browser know that XML is coming
out = res.getOutputStream();
res.setContentType("text/xml");
res.setContentLength((int)vxml.length());
// Output the VXML file
int readBytes = 0;
while ((readBytes = bis.read()) != -1) {
// output the VXML
out.write(readBytes);
}
} finally {
if (out != null) out.close();
if (bis != null) bis.close();
}
}
}
測試 servlet 載入的 VoiceXML
完成上述更改後編譯您的 servlet,若需要請重啟 servlet 引擎。浏覽 servlet,您應 看到如 圖 4 所示的輸出結果。成功!
圖 4. VoiceXML servlet 輸出 VXML
若您未得到類似輸出,確定您的文件是否位於您希望的位置,並確保沒有任何權限問題。 您還要檢查 servlet 引擎的日志或請求系統管理員的幫助。
現在就准備好將電話號碼映射到您的 servlet 了。重新回到 Voxeo.com 的 Application Mnager,添加一個新應用程序(可能會看到之前您使用過的應用程序)。確保選中 VoiceXML 2.0,然後輸入新應用程序的名稱和 servlet 的 URL。Voxeo 將創建應用程序並為其分配一 個電話號碼。
撥入這個新號碼,您應聽到 清單 2 中的 VXML 給出的提示。祝賀您!您已經編寫好了一 個輸出 VXML 的 Java servlet 的代碼,還在其中掛接了一個電話號碼。
部分可選的附加項
您可能希望向 servlet 代碼中添加一些小附加項。它們都不是必需的,但都會給現有的 版本增加一些健壯性和文檔。
首先,您可能想允許用戶通過 POST 請求訪問 VXML。這可能在用戶單擊表單上的一個按 鈕時發生,該表單將對 VoiceXMLServlet 作出一個 POST 請求。在 servlet 中處理這一操 作非常簡單,只要編寫一個委托已有 doGet() 方法的 doPost() 即可,如下所示:
public void doPost(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
doGet(req, res);
}
另外一個簡單的附加項用於允許浏覽器切實獲知您正在輸出一個 VXML 文件的內容。為此 ,設置 servlet 中的 Content-disposition 響應頭,如下:
// Let the browser know that XML is coming
out = res.getOutputStream();
res.setContentType("text/xml");
res.setContentLength((int)vxml.length());
res.addHeader("Content-Disposition",
"attachment; filename=" + vxml);
現在讀取您的響應的浏覽器(或其他代碼)就可以發現所服務的 VXML 文件了。但務必不 要包含完整的文件路徑,這會造成安全隱患!
動態 VoiceXML
有了輸出 VXML 文件的 servlet 之後,將其轉換成動態輸出 VXML 的 servlet(使用代 碼作為模型或模板)輕而易舉。換句話說,您可以超越簡單地載入靜態的 VXML 文件,開始 通過編程創建 VXML。
當您開始考慮動態 VoiceXML 時,Java 平台就顯示出了自己的優勢。它提供了輕松輸出 XML 的能力,還有與數據庫、目錄服務器、身份驗證存儲和會話的交互。此外,它還能夠證 實,構建動態 VXML 將消除基於語音的系統的部分刻板性。
在這一節中,我將逐步為您介紹創建一個輸出動態 VXML 的 Java servlet 的步驟。
通過 out.println() 輸出 VXML
您已經了解了如何訪問 ServletOutputStream,然後在輸出流中插入字節。但如果從源( 例如一個靜態 VXML 文件)傳輸到輸出流的不僅僅是字節,那麼直接處理字節的方式幾乎無 法管理控制。
如果您希望自行創建 VXML,最好使用 PrintWriter。利用這個類,您可發出整個字符串 ,使之對於創建和輸出動態內容更為有用。這只需要對代碼略加修改,如下所示:
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
String vxmlDir = getServletContext().getInitParameter("vxml-dir");
BufferedInputStream bis = null;
ServletOutputStream out = null;
try {
// Load the VXML file
File vxml = new File(vxmlDir + "/" + VXML_FILENAME);
FileInputStream fis = new FileInputStream(vxml);
bis = new BufferedInputStream(fis);
// Let the browser know that XML is coming
PrintWriter out = res.getOutputStream();
res.setContentType("text/xml");
res.setContentLength((int)vxml.length());
// Output content using PrintWriter
} finally {
if (out != null) out.close();
if (bis != null) bis.close();
}
}
另外,不要忘記導入 java.io.PrintWriter 類:它不會自動成為對您的 servlet 的代碼 基可用。
使用 PrintWriter,您現在可以輸出基於字符串的內容了。例如,清單 6 輸出與 清單 1 相同的 VXML,但是通過 servlet 輸出,並未從靜態文件載入 VXML 內容。
清單 6. 動態輸出 VXML
package com.ibm.vxml; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.*; import javax.servlet.http.*; public class DynamicVoiceXMLServlet extends HttpServlet { public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { BufferedInputStream bis = null; PrintWriter out = null; try { // Let the browser know that XML is coming out = res.getWriter(); res.setContentType("text/xml"); // Output VXML out.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); out.println("<vxml version=\"2.1\">"); out.println(" <form><block><prompt>"); out.println(" Things are working correctly! Congratulations."); out.println(" </prompt></block></form>"); out.println("</vxml>"); } finally { if (out != null) out.close(); if (bis != null) bis.close(); } } public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { doGet(req, res); } }
現在您可編譯這個 servlet、向 Voxeo 注冊它,並通過電話訪問它,與處理 清單 1 的 方法相同。現在我們來看一些示例,從而展示像 Java 這樣的語言的動態編程能力。
添加時間提醒
基於 servlet 的 VXML 輸出的一項最簡單的用途就是添加時間提醒。利用 Java 代碼獲 取當前日期和時間非常輕松,因此這是個不錯的起點。
使用 Calendar 類可輕松獲得一天中的具體時間(實際上,可以獲得與當前日期相關的任 何內容)。清單 7 給出了獲得 Calendar 類新實例的代碼,從而得到一天中的具體時間(以 24 小時的格式返回),然後根據這個時間組合出一條簡單的歡迎詞。
清單 7. 動態輸出 VXML
package com.ibm.vxml; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.PrintWriter; import java.util.Calendar; import javax.servlet.*; import javax.servlet.http.*; public class DynamicVoiceXMLServlet extends HttpServlet { public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { BufferedInputStream bis = null; PrintWriter out = null; try { // Let the browser know that XML is coming out = res.getWriter(); res.setContentType("text/xml"); // Output VXML out.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); out.println("<vxml version=\"2.1\">"); out.println(" <form><block><prompt>"); // Output a greeting based on the time of day Calendar cal = Calendar.getInstance(); int hour = cal.get(Calendar.HOUR_OF_DAY); if (hour < 6) { out.println("You're up early. Good morning."); } else if (hour < 12) { out.println("Good morning. How's your day so far?"); } else if (hour < 18) { out.println("Half the day is done... good afternoon!"); } else{ out.println("Hope you are enjoying your evening."); } out.println(" </prompt></block></form>"); out.println("</vxml>"); } finally { if (out != null) out.close(); if (bis != null) bis.close(); } } public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { doGet(req, res); } }
呼叫者和 VXML 生成者之間的差異
清單 7 也展示了 Voice XML 與動態生成的 VXML 的另外一項特性:呼叫者可能並非 VXML 本身。例如,假設有一名居住在新西蘭的用戶呼叫 清單 7 所示的應用程序。若當時新 西蘭時間是 10:00 PM,但輸出 VXML 的服務器位於科羅拉多州的丹佛,兩者之間問候的消息 可能非常古怪,例如 “您起得真早,早上好!” 這完全不恰當,情況還有可能更糟:如果 您已為每周的特定日子添加好了問候語,那麼實際上總會出現匹配錯誤。
根本問題源於 VXML 和 Java 是在特定服務器所在的位置和時區運行的,但可為世界各地 的呼叫者所用。如果您的 servlet 未考慮到這方面的問題,就會令一些呼叫者倍感困惑。您 有以下幾種選擇:
忽略差異,寄希望於呼叫者了解您的服務器並非在他們所在的時區運行代碼。
明確聲明日期和時間對應於服務器所在位置。例如,下午問候語可以是 “本地現在是下 午。祝您愉快”。
編寫代碼詢問時區或與 GMT 的時差,然後根據這一信息提供問候語。
遺憾的是,這些選擇中沒有任何一種富於吸引力。第一種選擇的所作所為正如所說明的那 樣:基本忽略呼叫者。不言而喻,忽略呼叫者絕非 贏得和維持業務關系的正確方法。第二種 想法 —— 給出本地時間並明確說明這是本地時間 —— 也沒有太大的幫助,因為這種方法 依然傾向於忽略呼叫者,只是在此過程中考慮得略微周全一些。
最後一種選擇乍看上去似乎很有吸引力,可以很容易地編寫出 VXML,允許用戶提供一個 與 GMT 的時差值,然後根據該信息應答。但呼叫者傾向於盡可能迅速地獲得信息,應答提示 越多,惹惱呼叫者的風險就越大,他們可能會不滿地掛斷電話。因此,除非您提供的以日期 或時間為基礎的服務,否則要求呼叫者指明時區就是浪費資源。甚至可能更糟的是,很多呼 叫者並不知道自己所在地與 GMT 的時差,因此您要面臨提供時區、時區縮寫、夏令時……那 將是一個冗長煩瑣的列表。
為什麼要自尋煩惱?
那麼我們為什麼要介紹這種基於日期的 VXML 生成呢?很大程度上是因為它正展示了這些 問題!您需要密切關注您的聽眾,盡力只提供那些與他們有關的信息,而不是與您的服務器 或您所在地有關的信息。
對於基於日期的處理,您要學習的課程就是:或許應該采用一種更好的最終選擇來應對呼 叫者,除非絕對必要,否則應完全避免采用基於日期或時間的事務。如果您預計到將有來自 其他時區的呼叫者,那麼提供與時間相關的功能就是自找麻煩。這一原則同樣適用於任何隨 州、國家或陸地標線的不同可能發生變化的數據。
最後,有很多時候使用 servlet 輸出 VXML 並非良策。如果您只是要從一個靜態文件中 提取 VXML,使用 servlet 帶來的好處極為有限(可能只有一點靈活性而已),但要添加代 碼、編譯、調試、一個 servlet 引擎和其他許許多多的東西,從而使語音應用程序的復雜性 大為增加。在這些簡單的情況下,應繼續使用靜態 VXML 文件。
一些有趣的想法
致此,您已看到,有時 servlet 生成的 VXML 並無 意義。在結束本文之前,考慮以下幾 種情況,在這些時候使用 Java 這樣的語言是絕佳 的電話應用程序解決方案。此處未提供完 整的示例,以後的文章中將予以介紹。
從一個數據庫中載入 VXML
最顯而易見的與 VoiceXML 相關的 Java 應用程序就是:使用數據庫提供動態 VXML 輸出 。這或許也是大部分讀者在選擇閱讀本文時希望了解的內容(但這篇文章中沒有核心示例, 因此您可能未學到足夠多的知識)。無論如何,JDBC 都能使您輕松連接數據庫,然後利用 SQL 查詢的結果填充 VXML。
例如,您要開發一個表,包含 VXM 的全部語法信息,然後將這些語法載入您所輸出的每 個 VXML 文件中。您不必為每一個 VXML 文件編碼語法,而是可以在類似的文件間共享語法 。更好的是,您可在所有 servlet 或一個特定 servlet 的實例中預載入這些語法。從而得 益於將語法存儲在數據庫中,無需浪費成本為每個請求載入語法。
根據用戶憑證載入 VXML
另外一項出色的 Java 功能 —— 特別是在與 servlet、JSP 和基於 Web 的編程相關時 —— 就是在會話中存儲用戶憑證的能力。這為您帶來穩定的身份驗證和授權,以及高度定制 的內容。
例如,考慮一個語音應用程序,從詢問用戶 ID 號和 PIN 開始(與當今的大多數銀行或 金融應用程序類似)。您可根據數據庫(依靠 Java 平台的強大力量)對這些憑證進行驗證 ,然後將呼叫者的 ID 存儲到一個會話變量中。此後,每個處理這名呼叫者的請求的 Java servlet 或 JSP 都可根據這些憑證了解為用戶提供哪些選擇。
盡管許多 VoiceXML 替代產品都提供了類似的功能性,但很少有產品以與其基於 Web 的 應用程序版本之間共享代碼為自豪。換言之,Java 平台允許您在 VoiceXML 應用程序及其基 於 Web 的版本間共享的不僅僅是數據庫,還包括代碼組件。生成 VXML 的 servlet 可使用 相同的身份驗證和授權工具類作為生成 HTML 和 XHTML 的 servlet,應答電話呼叫的 JSP 可與處理 HTTP 請求的 JSP 共享緩存數據庫連接。因而,您將得到一個能夠處理多種類型客 戶機的應用基礎設施,而不必為每種類型的客戶機創建一個完整的應用程序。
結束語
本文蜻蜓點水地介紹了可用 VXML 和 Java 平台實現的功能。介紹了開發 VXML 的過程, 然後為您展示了如何將 Java 技術整合到這一過程之中。介紹過程中給出了很多線索,告訴 您利用 Java 代碼來開發豐富、動態的 VoiceXML 應用程序的所有有趣的方式。
我還說明了 VoiceXML 開發人員在語音應用程序誤用 Java 技術的幾種常見形式。處理日 期和時間時耍小聰明、試圖提供地方性的服務或是忘卻服務器當地時間和呼叫者當地時間之 間的差異無疑會令用戶灰心離去。應將 Java 視為 VoiceXML 的一種工具,不要濫用 Date 和 Calendar 類。
在後續的文章中,我將繼續探討這些主題和更多內容,以本文給出的原則為基礎進行擴展 。如果您希望了解構建豐富的語音應用程序、開發與數據庫交互的電話應用程序、跟蹤用戶 、提供個性化內容的更多內容,請繼續關注本系列文章。另外,訪問 Voxeo.com,獲得一兩 個 servlet,來提供您自己的 VXML。請繼續關注下期文章,了解更多內容。
本文配套源碼