1. 前言
??? 在一些多功能服務(wù)系統(tǒng)的開發(fā)過程中,我們遇到了這樣的問題:作為體系框架的應(yīng)用程序和基于瀏覽器和WEB頁(yè)面的網(wǎng)絡(luò)服務(wù)很難溝通起來。有什么方法能夠把這兩種傳統(tǒng)意義上大相徑庭的開發(fā)方式高效、穩(wěn)定地集成,以盡可能地發(fā)揮其各自的優(yōu)勢(shì)呢?
2. 問題與思考
??? 運(yùn)行在瀏覽器上的HTML語(yǔ)言有著高效,靈活的優(yōu)勢(shì),在JAVA程序中,通過內(nèi)嵌" title="內(nèi)嵌">內(nèi)嵌開放式的JAVA瀏覽器Mozilla,用WEB頁(yè)面的形式實(shí)現(xiàn)一些用戶操作界面,能夠節(jié)約很多開發(fā)的精力。但瀏覽器的編程接口一般限于導(dǎo)航,對(duì)網(wǎng)頁(yè)內(nèi)部的構(gòu)件和數(shù)據(jù)的訪問支持不夠。當(dāng)主程序" title="主程序">主程序中采集到的數(shù)據(jù)參數(shù)需要經(jīng)由HTTP協(xié)議提交到數(shù)據(jù)遠(yuǎn)程服務(wù)器,并由瀏覽器顯示返回的結(jié)果頁(yè)面時(shí),如何將這些參數(shù)傳遞給瀏覽器的提交頁(yè)面,同時(shí)又使主程序能得到遠(yuǎn)程返回的數(shù)據(jù),便成為了一個(gè)需要解決的問題。
圖1. 問題所在——主程序與瀏覽器頁(yè)面的數(shù)據(jù)傳遞
??? 傳統(tǒng)的基于瀏覽器的Web服務(wù)模式是:瀏覽器與服務(wù)器直接建立連接關(guān)系,服務(wù)器直接返回Web頁(yè)面。如果嘗試打破這種模式,由主程序提交數(shù)據(jù),服務(wù)器以XML文件的形式返回必要的結(jié)果數(shù)據(jù),交由程序處理,那么數(shù)據(jù)共享的問題就得以解決。同時(shí)根據(jù)預(yù)先制定的HTML模板生成本地頁(yè)面文件,再通過瀏覽器向用戶顯示,也能達(dá)到與遠(yuǎn)程頁(yè)面相同的效果。
圖2. 問題的解決——另一種數(shù)據(jù)傳輸模式
?
3.基本原理
??? 由JAVA實(shí)現(xiàn)程序的主框架,內(nèi)嵌Mozilla的WebClient瀏覽器組件。發(fā)生服務(wù)請(qǐng)求時(shí),在程序中利用Http網(wǎng)絡(luò)對(duì)象將參數(shù)以Post的方式提交至遠(yuǎn)程Servlet數(shù)據(jù)服務(wù)器。服務(wù)端" title="服務(wù)端">服務(wù)端處理請(qǐng)求后返回一個(gè)包含結(jié)果數(shù)據(jù)的XML頁(yè)面。客戶端" title="客戶端">客戶端得到XML文件后使用DOM對(duì)象解析出結(jié)果數(shù)據(jù),并根據(jù)預(yù)先設(shè)計(jì)的HTML頁(yè)面模板,通過關(guān)鍵字替換的方式在本地生成結(jié)果頁(yè)面。最后在程序中調(diào)用瀏覽器的SetURL接口指定瀏覽器打開本地頁(yè)面。
4.JAVA中的Http請(qǐng)求
??? Sun的jdk1.3.1中,java.net包提供了一些網(wǎng)絡(luò)訪問功能的對(duì)象。其中網(wǎng)絡(luò)地址對(duì)象URL和連接對(duì)象HttpURLConnection類用于建立一個(gè)遠(yuǎn)程的Http訪問。
???下面是它們的一些主要方法及描述。
URL類:
?
??? 下面是使用這兩個(gè)對(duì)象對(duì)網(wǎng)絡(luò)上的Servlet服務(wù)器進(jìn)行數(shù)據(jù)查詢的例子。
??? 使用查詢腳本的地址描述作為參數(shù)創(chuàng)建一個(gè)URL對(duì)象實(shí)例,調(diào)用URL的openConnection方法打開連接。此時(shí)程序試圖連接遠(yuǎn)端并請(qǐng)求腳本服務(wù),如成功即返回一個(gè)HttpURLConnection連接實(shí)例。
??? 用連接實(shí)例的getOutputStream方法取得它的輸出流,在輸入?yún)?shù)字串" title="字串">字串后,服務(wù)器從后臺(tái)數(shù)據(jù)庫(kù)中查詢出結(jié)果數(shù)據(jù),組成XML字符流并輸出,客戶端由getInputStream得到的輸入流取得并保存XML文件。寫入?yún)?shù)前可以通過setRequestMethod方法設(shè)置提交方法,建議在參數(shù)較復(fù)雜時(shí)使用“POST”方法。根據(jù)HTML的協(xié)議規(guī)范,參數(shù)之間用“&”號(hào)隔開。
??? 以下是主要程序代碼,已通過jdk1.3.1調(diào)試。
//指定遠(yuǎn)程的查詢服務(wù)腳本地址
URL url = new URL("http://192.168.0.176:7001/WebApp/BasicInfQuery");
//打開Http連接
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setDoOutput(true);
//提交參數(shù)pid和name
PrintWriter out = new PrintWriter(connection.getOutputStream());
String pid = "pid="+URLEncoder.encode(“97133225");
String name = "name="+URLEncoder.encode("yang");
out.println(pid+“&”+name);
out.close();
//讀取查詢服務(wù)返回的數(shù)據(jù)
BufferedReader in
= new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
System.out.println(line);
}
in.close();
}
5.XML數(shù)據(jù)結(jié)構(gòu)定義和文件解析
??? 以一個(gè)學(xué)生信息查詢?yōu)槔?,為XML作如下的數(shù)據(jù)結(jié)構(gòu)定義:以“學(xué)生信息”為根,“學(xué)生”為節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)有一個(gè)屬性“性別”,及“姓名”、“年齡”、“電話”三個(gè)子節(jié)點(diǎn)。
??? 客戶端程序在提交查詢后,得到服務(wù)端程序動(dòng)態(tài)生成的XML文件,然后利用JAVA下的DOM對(duì)象將文件中的數(shù)據(jù)解析出來。“文檔對(duì)象模型”DOM是W3C制定的XML數(shù)據(jù)概念描述,它允許開發(fā)者在 XML 結(jié)構(gòu)內(nèi)引用、檢索和更改 XML 結(jié)構(gòu)中的各項(xiàng)。在Sun的jdk1.4.1中包含了XML解析接口javax.xml.parser和DOM對(duì)象,它們對(duì)XML文件的處理提供了很完整的一套接口。
??? DOM在處理XML文件之前,首先將XML文件解析成對(duì)象化的文檔(Document)。javax.xml.parsers中的DocumentBuilder通過parse方法對(duì)一個(gè)XML文件進(jìn)行解析,生成一個(gè)Document對(duì)象。簡(jiǎn)單要代碼如下:
//為解析XML作準(zhǔn)備,創(chuàng)建DocumentBuilderFactory實(shí)例,指定DocumentBuilder
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
//解析指定的XML文件,創(chuàng)建Document對(duì)象
Document doc = db.parse(inFile);
??? 解析后的Document將所有數(shù)據(jù)以父子的節(jié)點(diǎn)層次結(jié)構(gòu)裝入內(nèi)存,這些節(jié)點(diǎn)可以是元素、文本、屬性或其它節(jié)點(diǎn)類型。節(jié)點(diǎn)元素對(duì)象Element提供了對(duì)其元素的值和子節(jié)點(diǎn)的訪問。下面是Element對(duì)象的一些方法定義和描述。
?
方法?描述???
GetAttribute(String name)?取得指定屬性名的屬性值???
GetElementsByTagName(String name)?取得指定節(jié)點(diǎn)名的子節(jié)點(diǎn)隊(duì)列?
getElementsByTagName 返回的NodeList對(duì)象實(shí)質(zhì)是一個(gè)Element的隊(duì)列,其item(n)方法返回其中的第n個(gè)Element對(duì)象。
?以下一段代碼用于讀取Document對(duì)象中樹形節(jié)點(diǎn)的值:
Element root = doc.getDocumentElement();
//取"學(xué)生"元素列表
NodeList students = root.getElementsByTagName("學(xué)生");
for (int i = 0; i < students.getLength(); i++)
{
?//依次取每個(gè)"學(xué)生"元素
?Element student = (Element) students.item(i);
?//取學(xué)生的性別屬性
?String sex = student.getAttribute("性別");
//取"姓名"元素,其他類同
?NodeList names = student.getElementsByTagName("姓名");
?if (names.getLength() == 1) {
??Element e = (Element) names.item(0);
??Text t = (Text) e.getFirstChild();
??String name = t.getNodeValue();
?}
}
注:在Apache提供的XML開發(fā)工具包c(diǎn)rimson的幫助下,DOM對(duì)象也能用于將一個(gè)Document的數(shù)據(jù)寫入XML文件。在我們的數(shù)據(jù)服務(wù)器端,因?yàn)樯蒟ML文件相比較解析文件來的簡(jiǎn)單,我們并沒有使用這一功能,但在一些XML結(jié)構(gòu)較復(fù)雜的情況下,使用一個(gè)清晰的文檔對(duì)象也是很有必要的。相關(guān)的資料可參考Apache網(wǎng)站上crimson的開發(fā)文檔。
6.HTML模板定義和頁(yè)面的動(dòng)態(tài)創(chuàng)建
??? HTML模板是在源碼中添加了一些自定義的標(biāo)記(tag)的HTML文件,這些tag用于在動(dòng)態(tài)創(chuàng)建頁(yè)面時(shí)指示數(shù)據(jù)字串的插入位置。
??? 對(duì)于預(yù)先設(shè)計(jì)的HTML頁(yè)面,以文本方式編輯他們的源碼。在顯示數(shù)據(jù)的位置用自定義的tag作出標(biāo)記,這些tag以一個(gè)在HTML語(yǔ)法中不常出現(xiàn)的特殊符號(hào)作為起始和終止符,如”%”,以它們的數(shù)據(jù)意義來命名。例如姓名的tag定義為”%name%”,性別為”%sex%”,類似。下面是一個(gè)模板中部分代碼的舉例:
?姓名 %name%
?性別 %sex%
?年齡 %age%
??? 在創(chuàng)建頁(yè)面時(shí),程序?qū)⒛0逡晕谋痉绞阶x入一個(gè)String類型變量中,然后依次查找每個(gè)數(shù)據(jù)所對(duì)應(yīng)的tag的位置,用表示數(shù)據(jù)的字串替換它。在Java中實(shí)現(xiàn)字串替換的代碼如下:
? public void ReplaceStr(String sTag, String sData){
??? int index;
??? while((index=sHTML.indexOf(sTag))>=0)
????? sHTML=sHTML.substring(0,index)+sData+sHTML.substring(index+sTag.length(),sHTML.length());
? }
??? 把所有定義的tag替換之后,就創(chuàng)建出一個(gè)表示當(dāng)前操作的結(jié)果數(shù)據(jù)的動(dòng)態(tài)HTML頁(yè)面。通過程序內(nèi)置的瀏覽器在本地打開這個(gè)HTML頁(yè)面,最終實(shí)現(xiàn)了將服務(wù)結(jié)果向用戶輸出。
7.方法的優(yōu)缺點(diǎn)討論
優(yōu)點(diǎn):
??? 成功解決了數(shù)據(jù)共享的問題,因而極大地拓展了瀏覽器在服務(wù)系統(tǒng)開發(fā)中的應(yīng)用領(lǐng)域,從而能夠充分利用開發(fā)資源,高效的開發(fā)出基于網(wǎng)絡(luò)的數(shù)據(jù)服務(wù)。
??? 以XML作為數(shù)據(jù)載體,直接以數(shù)據(jù)的形式返回結(jié)果,可讀性強(qiáng),易于調(diào)試。
??? XML的體積較小,可以減輕服務(wù)器和網(wǎng)絡(luò)的負(fù)擔(dān)。
??? 方法不僅限于JAVA程序,在其他開發(fā)平臺(tái)上也可以通過類似的辦法實(shí)現(xiàn)。
??? 可以通過XSL擴(kuò)展XML在瀏覽器上的顯示功能,省去創(chuàng)建動(dòng)態(tài)網(wǎng)頁(yè)的步驟。
缺點(diǎn):
??? 文本形式的XML存在著不小的安全問題,對(duì)一些機(jī)密的數(shù)據(jù)需要加密后傳輸。
??? 將HTML頁(yè)面存放在客戶端的形式在多臺(tái)客戶機(jī)的情況下會(huì)對(duì)維護(hù)和更新頁(yè)面造成一定的難度,可以考慮在客戶端建立一個(gè)被動(dòng)式的遠(yuǎn)程頁(yè)面更新服務(wù)。
8.結(jié)束語(yǔ)
??? 因?yàn)槠邢?,這里只舉了一個(gè)最簡(jiǎn)單的例子,但我們?nèi)匀荒荏w會(huì)到在應(yīng)用程序的Web服務(wù)中使用瀏覽器所帶來的便捷和強(qiáng)大的功能。這種方法突破了數(shù)據(jù)訪問上的束縛,能夠最大限度的發(fā)揮各自的優(yōu)勢(shì),提高了軟件的整體性能,又節(jié)約了寶貴的開發(fā)時(shí)間和精力。
參考文獻(xiàn)
1.Mark Birbect 等著,裴劍鋒 等譯,XML 高級(jí)編程(第2版),2002
2.Tom Myers Alexander Nakhimovsky 著,王輝 等譯,Java XML編程指南, 2001
3.Bruce Eckel 著,侯捷 譯,Java編程思想(第2版),2002
4.Dan Becker, Explore online XML data with Java programming,IBM developerWorks journal.,August 2002
?