概覽:
xml和jsp是這些日子中最熱的東西。本文介紹如何聯合這兩種技術來建設動態網站。你還可以同時看一下dom,xpath,xsl,和其它java-xml技術的範例程式碼。
我們在此假設你已經瞭解javaserver pages(jsp)和extensible markup language (xml)。但也許你對該如何綜合使用它們仍然有些迷惑。
jsp的應用很容易,你可以用它設計網頁,使之看起來似乎和html一樣。唯一的不同是jsp是動態執行的。例如,它們可以處理表單form和讀寫資料庫。
xml的應用的說明則比較困難。似乎所有的產品都支援它,每個人也好象都以各種不同目的在使用它。
在本文中,你可以看到如何使用一種相當先進的方式用xml來設計一個系統。許多網站有巨量資料收集並以一種很標準或很不標準的方式來顯示它們。我將設計一個系統,它使用xml檔案在web伺服器上進行儲存,並用jsp來顯示資料。
xml vs 關係型資料庫
"等一下!"你可能問,"你用xml檔案儲存體資料嗎?為什麼不使用資料庫?"
這個問題問的很好。我的回答是,對很多目的用途來說,用資料庫太過浪費了。.要使用一個資料庫,你必須安裝和支援一個分離的伺服器處理進程(a separate server process),它常要求有安裝和支援它的administrator。你必須學習sql, 並用sql寫查詢,然後轉換資料,再返回。而如果你用xml檔案儲存體資料,將可減少額外的伺服器的負荷。還有,你還找到了一個編輯資料的簡單方法。你只要使用文字編輯器,而不必使用複雜的資料庫工具。xml檔案很容易備份,和朋友共用,或下載到你的用戶端。同樣的,你可以方便地通過ftp上傳新的資料到你的網站。
xml還有一個更抽象的優點,即作為層次型的格式比關係型的更好。 它可以用一種很直接的方式來設計資料結構來符合你的需要。你不需要使用一個實體-關係編輯器,也不需要使你的圖表(schema)標準化。 如果你有一個元素(element)包含了另一個元素,你可以直接在格式中表示它,而不需要使用表的關聯。
注意,在很多應用中,依靠檔案系統是不夠充分的。如果更新很多,檔案系統會因為同時寫入而受到破壞。資料庫則通常支援交易處理,可以應付所發生的請求而不至於損壞。對於複雜的查詢統計要有反覆、及時的更新,此時資料庫表現都很優秀。當然,關係型資料庫還有很多優點,包括豐富的查詢語言,圖表化工具,延展性,存取控制等等。
(注意:你可以使用簡單的檔案鎖定來提供一個交易處理伺服器,你還可以在java中執行一種 xml index-and-search工具,不過這已經是另外一篇文章的主題了。)
在下面這樣的案例中,正如大多數中小規模的、基於發布資訊的網站一樣,你可能涉及的大多數資料存取都是讀,而不是寫,資料雖然可能很大,但相對來說並沒有經常的更新變化,你也不需要做很複雜的查詢,即使你需要做,也將用一個獨立的查詢工具,那麼成熟的rdbms的優點消失了,而物件導向型的資料模型的優點則可以得到體現。
最後,為你的資料庫提供一個查詢器外殼來進行sql查詢並將他們轉化進入xml stream也是完全有可能的。
所以你可以選擇這二種方式之一。xml正變成一種非常健壯的,便於編程的工具,作為某個成熟的資料庫的前端工具來進行儲存和查詢。(oracle的xsql servlet即是這種技術的一個很好的例子。)
應用篇:一個線上相簿
所有人都喜歡照相!他們喜歡展示自己的,親人的,朋友的,度假時的照片,而 web 是他們展示的好地方。-- 即使千裡之外的親戚都可以看到。我將著重於定義一個單獨的picture對象。(這一應用的原始碼在resources中可以取得) 。該對象描述了表示一張照片所需要的欄位:title,date,一個可選的標題,以及對圖片來源的一個指向。
一個圖象,需要它自己的一些欄位:源檔案( gif/jpeg)的定位,寬度和高度像素(以協助建立<img> 標記。 這裡可以看到一個很簡單的優點,即使用檔案系統來代替資料庫的時候,你可以將圖形檔案存放在與資料檔案相同的目錄中。
最後,讓我們來用一個元素擴充圖片記錄,該元素定義了一套縮圖(thumbnail)來用於內容表或其它地方。這裡我用了和先前同樣定義的圖片內容。
一張圖片的xml表示可以是這樣的:
<picture>
<title>alex on the beach</title>
<date>1999-08-08</date>
<caption>trying in vain to get a tan</caption>
<image>
<src>alex-beach.jpg</src>
<width>340</width>
<height>200</height>
</image>
<thumbnails>
<image>
<src>alex-beach-sm.jpg</src>
<width>72</width>
<height>72</height>
</image>
<image>
<src>alex-beach-med.jpg</src>
<width>150</width>
<height>99</height>
</image>
</thumbnails>
</picture>
注意,通過使用xml, 你將一張單獨圖片的全部資訊放到了一個單獨的檔案中,而不是將它分散放入3-4個表中。 我們將這稱為 .pix file
-- 於是你的檔案系統會是這樣的:
summer99/alex-beach.pix
summer99/alex-beach.jpg
summer99/alex-beach-sm.jpg
summer99/alex-beach-med.jpg
summer99/alex-snorkeling.pix
etc.
技術篇
俗話說,要剝下貓的皮的方法何止一種。同樣,將xml資料放到jsp中也不止一種辦法。這裡列舉了其中一些方法,(其實,很多其它工具也可以做得同樣出色。)
dom: 你可以使用類(classes)來調用dom介面(interface)對xml檔案進行分析檢查。
xmlentrylist: 你可以使用My Code來將xml載入到name-value pairs 的java.util.list中。
xpath: 你可以使用一個 xpath處理器(如resin)通過路徑名在xml檔案中定位元素。
xsl:你可以使用某種xsl處理器將xml轉換成為html。
cocoon: 你可以使用開放源碼的cocoon framework
運行你自己的bean: 你可以寫一個外殼類(wrapper class),使用某種其它技術來將資料載入到字定義的javabean中。
請注意這些技術將和一個你從另外來源取得的xml stream執行得同樣出色,例如一個用戶端或者一個應用伺服器。
javaserver pages
jsp規範有很多替身,不同的jsp產品表現也不盡相同,不同版本之間也有差別。我選擇了tomcat,這基於以下原因:
它支援大多數最新的jsp/servlet規範
它受到 sun和apache認同
你可以獨立運行它而不需要另外配置一個web伺服器。
它是開放源碼的
你可以選擇任何你喜歡的jsp引擎,但要自己配置它,它必須至少支援jsp 1.0規範。0.91和1.0之間有了許多區別。而jswdk (java server web development kit) 可能剛剛好地適合要求。
jsp結構
當建立一個jsp網站 (webapp), 我喜歡將公用的函數、匯入、常量、變數聲明都放入到一個單獨的檔案init.jsp中。 然後用 <%@include file="init.jsp"%>載入到每一個檔案中去。 <%@include%> 就象c語言的 #include, include在編譯時間使其中的文本作為一個部分被加入並一起進行編譯,相對地, <jsp:include>標記則是使其中的檔案被獨立地進行編譯,然後在檔案中嵌入一個對它的調用。
尋找檔案
當jsp啟動時,初始化後第一件事情就是尋找你要的xml檔案。它是怎麼知道在眾多檔案中你要找的是哪一個? 它來自與一個參數,使用者會在調用jsp的url中加入參數: picture.jsp?file=summer99/alex-beach.pix (或者通過html表單來傳遞檔案參數)。
但是,當jsp接受此參數以後,你仍然只完成了一半工作,因為還要知道檔案系統的根目錄在哪裡。例如,在unix系統中,實際檔案可能在這樣的路徑:
/home/alex/public_html/pictures/summer99/alex-beach.pix。
jsp檔案在執行狀態時沒有當前路徑概念。所以你為java.io包要給出一個絕對路徑。
servlet api可以提供一個方法來將一個url路徑,從相對於當前jsp或servlet的路徑轉化成為一個絕對的檔案系統路徑。方法是:
servletcontext.getrealpath(string)。
每一個jsp有一個叫做application的 servletcontext對象。所以代碼可以是:
string picturefile =
application.getrealpath("/" + request.getparameter("file"));
或者
string picturefile =
getservletcontext().getrealpath("/" + request.getparameter("file"));
它也可以在servlet中工作。(你必須加上 / 因為此方法需要傳遞request.getpathinfo()的結果。)
這裡有一個重要的提示:每當你存取本地的資源,要非常小心地檢查輸入資料的合法性。駭客或者粗心的使用者,可能發送偽造的或錯誤的資料來破壞你的網站。例如,請想一下以下的表達會發生什麼結果:
如果輸入了file=../../../../etc/passwd。這樣使用者回讀到你的伺服器的password檔案!
dom (document object model)
dom 代表文件物件模型document object model。它是瀏覽xml文檔的一種標準api,由world wide web consortium (w3c)發展。 介面在org.w3c.dom包中,文檔參見w3c網站。
有許多可用的dom分析器工具。我選擇了 ibm的xml4j。但你可以使用任何其它的dom分析器。這是因為dom是一套介面,而不是類 --所有的dom分析器(parser)必須返回同樣地處理這些介面的對象。
遺憾的是,雖然很標準,dom還是有兩大缺陷:
1 api雖然也是物件導向的,還是相當笨重。
dom parser並沒有標準的api,所以, 當每一個分析器返回一個org.w3c.dom對象,文檔對象--分析器初始化和檔案自身載入的方式,對應於不同分析器通常總是特定的。
這個簡單的上面已描述的圖片檔案在dom中可以在一個樹結構中通過一些對象表示如下:
document node
--> element node "picture"
--> text node "\n " (whitespace)
--> element node "title"
--> text node "alex on the beach"
--> element node "date"
--> ... etc.
為了取得“alex on the beach”,你要做一些方法調用,遊歷dom樹,而且,分析器可能選擇分散“whitespace”文本nodes的一些資料,你不得不使用迴圈和串聯等 (你可以通過調用normalize()來糾正這個問題。)分析器可能還包含了分離的xml實體(如 &), cdata nodes或者其它實體nodes (如<b>big<b>會被變成至少三個node。也沒有辦法在dom中簡單表示"get me the text value of the title element." 總之,在dom中遊歷有一點笨重。(參見本文用xpath取代dom章節。)
2 從更高處看,dom的問題是xml對象無法象java對象一樣可以直接得到,它們需要通過 dom api一個一個地得到。
你可以參考我的為java-xml data binding技術討論做的一些歸納,那裡也用了這種直接使用java的方法來存取xml資料。
我寫了一個小的工具類,叫做domutils,包含了靜態方法來執行公用的dom任務。例如,要獲得根(圖片)元素的title子項目的常值內容,你可以編寫如下代碼:
document doc = domutils.xml4jparse(picturefile);
element noderoot = doc.getdocumentelement();
node nodetitle = domutils.getchild(noderoot, "title");
string title = (nodetitle == null) ? null : domutils.gettextvalue(nodetitle);