簡介
Jakarta Tapestry是一個(gè)開源的Java Web應(yīng)用框架。你或許會說:"大同小異的東西罷了."多數(shù)
情況下,我同意你的觀點(diǎn),然而,只要花上些許時(shí)間研究一下,你會發(fā)現(xiàn)Tapestry跟別的框架大為不同,它是值得嚴(yán)肅對待的。
Tapestry是一個(gè)基于控件的框架以致于用它開發(fā)Web應(yīng)用類似開發(fā)傳統(tǒng)的GUI應(yīng)用。你用Tapestry開發(fā)Web應(yīng)用時(shí)你無需關(guān)注以操作為中心的(operation-centric) Servlet API.引用Tapestry網(wǎng)站上的一句話:"Tapestry用對象(objects),方法(methods),屬性(properties)替代以往的URLs和查詢參數(shù), 重新詮釋W(xué)eb應(yīng)用開發(fā).Tapestry 3.0即將發(fā)布,它有大量的改進(jìn)和新的特性。
Tapestry的目標(biāo)
簡單性
Tapestry應(yīng)用與傳統(tǒng)的Servlet應(yīng)用相比代碼量更少。大多數(shù)傳統(tǒng)的Servlet應(yīng)用包含如下厭煩和
重復(fù)的任務(wù):解析查詢參數(shù),處理HttpSession對象,構(gòu)建URLs。Tapestry消除了傳統(tǒng)Servelt應(yīng)用中許多無趣的"銜接"代碼("plumbing" code)使開發(fā)者把精力集中到應(yīng)用邏輯上來。
一致性
Tapestry為開發(fā)Web應(yīng)用的頁面提供了一致的方式。這樣有助于消除傳統(tǒng)Servlet應(yīng)用開發(fā)中臆測。由于所有Tapestry應(yīng)用中的頁面都是用相同的可復(fù)用的控件組織而成,所以工作方式是相似的。
效率
Tapestr應(yīng)用擁有高度的可升級性,它利用緩存和對象池使每個(gè)請求的處理時(shí)間最小化。Tapestry應(yīng)用擁有跟傳統(tǒng)Servlet應(yīng)用相仿的性能。
錯(cuò)誤反饋
任何開發(fā)過Servlet/JSP應(yīng)用的人毫無質(zhì)疑有類似經(jīng)歷:為了找出Web.xml文件有什么錯(cuò)誤,不得不花費(fèi)大量時(shí)間察看瀏覽器中的堆棧信息。Tapestry擁有優(yōu)秀的錯(cuò)誤報(bào)告方式,最值得一提的是它會指出哪個(gè)文件以及那一行導(dǎo)致了錯(cuò)誤。
與Struts比較
既然Apache Struts可能是當(dāng)今應(yīng)用最廣泛的Web應(yīng)用框架,拿Tapestry與它比較是唯一公平的。以下是一些觀察比較,它們來源于為這篇文章開發(fā)的幾個(gè)簡單的Tapestry應(yīng)用和為幾個(gè)Struts工程的工作經(jīng)歷。
Struts的優(yōu)點(diǎn)
1 一個(gè)Servlet/JSP開發(fā)者熟悉Struts無需太久。然而Tapestry的學(xué)習(xí)曲線會長一點(diǎn),因?yàn)樗c流行的Web應(yīng)用框架不太相同 。
2 Struts在Java社區(qū)里被廣泛接受和使用。為你的項(xiàng)目找一個(gè)好的Struts開發(fā)人員并非難事,Tapestry近來在開發(fā)者社區(qū)里 贏得一些關(guān)注,然而仍有許多Java老手不知道Tapestry為何物。
3 既然Struts被如此廣泛的使用,所以有很多Struts資源可供參考。相比大多數(shù)開源軟件,Tapestry擁有非常可觀的資源和文檔,但跟Struts相比仍有差距。
Tapestry的優(yōu)點(diǎn)
1 你開發(fā)一個(gè)Tapestry應(yīng)用無需關(guān)注Servlet API,你也無須為你的Servlet應(yīng)用寫一些典型的"銜接"代碼。雖然Struts簡化了工作,然而Servlet API 仍是你需面對的。
2 Tapestry的頁面模板除了幾個(gè)特別屬性和標(biāo)識就是一個(gè)標(biāo)準(zhǔn)的HTML文件。 如果你是一個(gè)開發(fā)小組中的HTML設(shè)計(jì)高手,然而你不懂Java或JSP,這就給你帶來很大方便。
3 因?yàn)門apestry頁面是標(biāo)準(zhǔn)的HTML,所以可以用HTML所見即所得(WYSIWYG)編輯器編輯和預(yù)覽該頁。當(dāng)一個(gè)頁需要修改它的外觀并不需要通知服務(wù)器讓它重新編譯JSP.
4 Tapestry不需要一個(gè)至高的,整個(gè)應(yīng)用范圍的配置文件。Tapestry應(yīng)用中的每一頁是獨(dú)立的,改變一頁不會影響開發(fā)其他 頁面的開發(fā)者,因?yàn)椴]有一個(gè)配置文件把所有頁面的瀏覽粘連在一起。
5 Tapestry擁有極好的錯(cuò)誤報(bào)告。如果你在一個(gè)頁的模板或頁面規(guī)范犯了一個(gè)錯(cuò)誤,Tapestry會指出導(dǎo)致錯(cuò)誤的行號。
6 用Tapestry開發(fā)是種樂趣。這樣說聽上去似乎老調(diào),然而用Tapestry開發(fā)一個(gè)Web應(yīng)用相比應(yīng)用其他流行的框架更為自然有趣。用Tapestry開發(fā)是應(yīng)用了一種基于控件的架構(gòu),與開發(fā)傳統(tǒng)的GUI應(yīng)用非常相似。
Tapestry 架構(gòu)
Tapestry框架是標(biāo)準(zhǔn)Servlet API的一種擴(kuò)展。它需要J2SDK1.2或更高版本的J2SDK和一個(gè)與Servlet API 2.2(或更高)兼容的應(yīng)用服務(wù)器/Servlet容器。
一個(gè)Tapestry應(yīng)用由許多擁有唯一名稱的頁面組成。一個(gè)頁面由一個(gè)模板和一些可復(fù)用的控件構(gòu)成。模板由標(biāo)準(zhǔn)的HTML標(biāo)簽和一些額外的屬性和標(biāo)簽構(gòu)成,這些額外的屬性和標(biāo)簽是為了告訴Tapestry框架這個(gè)頁面的那些部分是由Tapestry控件組成。
簡單的Tapestry應(yīng)用
為了最好的描述構(gòu)建一個(gè)Tapestry頁面的方方面面,我們可以看看這個(gè)Pig Latin翻譯器應(yīng)用的代碼。這個(gè)應(yīng)用只有一個(gè)頁面,在這頁里輸入一個(gè)text值把它翻譯成Pig Latin,然后把翻譯好的值顯示給用戶。
在Tapestry應(yīng)用中每個(gè)頁由3個(gè)部分組成:一個(gè)HTML模板,一個(gè)頁面規(guī)范文件,一個(gè)Java類。
這里有這個(gè)頁面屏幕抓圖:

頁面模板由標(biāo)準(zhǔn)的HTML標(biāo)簽和一些額外的屬性和標(biāo)簽構(gòu)成,這些額外的屬性和標(biāo)簽是為了告訴Tapestry框架這個(gè)頁面的那些部分是由Tapestry控件組成。頁面模板存放在Web應(yīng)用的根context目錄下。通常,Tapestry在啟動時(shí)會尋找和呈現(xiàn)一個(gè)名叫"Home"的頁。雖然我們可以改變這種行為,但依照Tapestry的慣例會更簡單。
Home.html
??? <html>
??? <head>
????? <title>Tapestry Pig Latin Translator</title>
??? </head>
??? <body>
????? <h1>Pig Latin Translator</h1>
????? <form jwcid="@Form"① listener="ognl:listeners.submit"②>
????? <table border="1">
??????? <tr>
????????? <td>Value to Translate:</td>
????????? <td>
??????????? <input type="text" jwcid="@TextField"③ value="ognl:inputValue"/>
????????? </td>
??????? </tr>
??????? <tr>
????????? <td>Pig Latin:</td>
????????? <td>
??????????? <jwcid="@Insert"④ value="ognl:pigLatinValue"/>
????????? </td>
??????? </tr>
????? </table>
????? <input type="submit" jwcid="@Submit"⑤ value="Translate"/>
????? </form>
??? </body>
??? </html>
???
頁面模板的絕大部分是普通的HTML,只有少部分Tapestry特有的屬性和標(biāo)簽。這種模板機(jī)制的優(yōu)勢就是Tapestry頁面模板可以在一個(gè)可見即所得的編輯器里創(chuàng)建和預(yù)覽。描述Tapestry控件部分的標(biāo)識是有限的和突出的。
標(biāo)識里的jwcid所指是被應(yīng)用的Tapestry控件的Java Web Component ID.在上面代碼斷里,我們是隱式地使用控件。隱式的控件是指直接在頁面模板里聲明使用的控件,。jwcid的前綴 '@ '符號就是通知Tapestry這里聲明使用了一個(gè)隱式控件。
在上面的Pig Latin Translator頁面模板里用了四個(gè)控件:Form①, TextField③, Insert④ 和Submit⑤。它們只是Tapestry框架提供的包含超過40個(gè)控件的控件庫里的四個(gè)。在后面的范例中,我們將會看到如何使用顯式控件。顯式控件是指控件在頁面規(guī)范文件里聲明后再使用的控件。
在前面的HTML模板里,使用控件的同時(shí),也為控件指定了參數(shù)。例如控件Form①有一個(gè)listener②參數(shù)它指定了當(dāng)表單提交時(shí)對應(yīng)的頁面類調(diào)用的方法名稱。那個(gè)ognl:前綴的使用貫穿頁面的HTML模板,指向的是Object Graph Navigation Language (OGNL)。OGNL是一個(gè)強(qiáng)大的開源的表達(dá)式語言,用于將頁面內(nèi)控件的屬性綁定到頁面類的屬性。
現(xiàn)在我們看看頁面規(guī)范文件。頁面規(guī)范文件是一個(gè)擴(kuò)展名為page的XML文件,這個(gè)文件有許多職責(zé),在眾多職責(zé)中最基本是指定頁面對應(yīng)的Java類。頁面規(guī)范文件存放在webapp的WEB_INF目錄。
Home.page
??? <?xml version="1.0"?>
??? <!DOCTYPE page-specification PUBLIC
??????? "-//Apache Software Foundation//Tapestry Specification 3.0//EN"
??????? "http://jakarta.apache.org/tapestry/dtd/Tapestry_3_0.dtd>
??? <page-specification class="Home">
??????? <property-specification name="inputValue" type="java.lang.String"/>
??????? <property-specification name="pigLatinValue" type="java.lang.String"/>
??? </page-specification>
???
頁面規(guī)范文件的根元素有一個(gè)class的屬性,它指定了這個(gè)頁對應(yīng)的Java類。這個(gè)類必須要實(shí)現(xiàn)org.apache.tapestry.Ipage接口。頁面規(guī)范同時(shí)頁定義了兩個(gè)屬性(property)元素,以便Tapestry在頁面類里創(chuàng)建新的屬性。
Tapestry框架提供了org.apache.tapestry.html.BasePage class,它實(shí)現(xiàn)了Ipage接口。頁面類被存放在Web-INF/classes目錄下,跟你的Web應(yīng)用的所需要的其他類放在一起。
Home.java
??? import org.apache.tapestry.html.BasePage;
??? import org.apache.tapestry.IRequestCycle;
??? public abstract class Home extends BasePage {
??????? public abstract String getInputValue();
??????? public abstract void setInputValue(String inputValue);
??????? public abstract String getPigLatinValue();
??????? public abstract void setPigLatinValue(String pigLatinValue);
??????? public void submit(IRequestCycle cycle) {
??????????? String inputValue = getInputValue();
??????????? String pigLatinValue = new PigLatinTranslator().translate(inputValue);
??????????? setPigLatinValue(pigLatinValue);
??????? }
??? }
???
你要提醒的第一件事或許是這個(gè)類為什么是抽象類。它還有幾個(gè)抽象方法訪問inputValue,pigLatinValue屬性。這里利用了Tapestry會在運(yùn)行時(shí)刻創(chuàng)建子類的功能,這個(gè)子類會創(chuàng)建你在頁面規(guī)范里聲明的屬性和生成相應(yīng)的訪問方法。
在表單提交時(shí)頁面類的submit方法會被調(diào)用。為什么會這樣?因?yàn)槲覀冊陧撁婺0謇飳orm控件的listener屬性指定為:ognl:listeners.submit。這就意味著一個(gè)名叫submit的listener會通過頁面類的listeners被訪問。
所有的頁面類和控件類都從org.apache.tapestry.AbstractComponent這個(gè)類繼承來一個(gè)叫l(wèi)isteners的屬性。當(dāng)submit方法完成后,頁面會顯示被翻譯好的詞。
最后講講Web.xml這個(gè)Web發(fā)布描述文件。Tapestry,像許多其他的流行的Web應(yīng)用框架一樣,由一個(gè)Servlet構(gòu)成,但是還需要一個(gè)發(fā)布描述文件。那個(gè)發(fā)布描述文件應(yīng)該被存放在WEB-INF目錄。
web.xml
??? <?xml version="1.0"?>
??? <!DOCTYPE web-app PUBLIC
??????? "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
??????? "http://java.sun.com/dtd/web-app_2_3.dtd>
??? <web-app>
??????? <display-name>Tapestry Pig Latin Translator</display-name>
??????? <servlet>
??????????? <servlet-name>tapestry</servlet-name>
??????????? <servlet-class>org.apache.tapestry.ApplicationServlet</servlet-class>
??????????? <load-on-startup>1</load-on-startup>
??????? </servlet>
??????? <servlet-mapping>
??????????? <servlet-name>tapestry</servlet-name>
??????????? <url-pattern>/app</url-pattern>
??????? </servlet-mapping>
??? </web-app>
???
雖然Pig Latin翻譯應(yīng)用非常簡單,但是它會讓你對在Tapestry應(yīng)用中一個(gè)頁面的3個(gè)組成部分有了基本的了解。它也展示了創(chuàng)建一個(gè)Tapestry應(yīng)用的一個(gè)頁面只需寫多么少的代碼。
表單輸入驗(yàn)證
Tapestry 提供了一些控件以便校驗(yàn)用戶的輸入。校驗(yàn)子系統(tǒng)是ValidField控件的核心。在下面的登錄應(yīng)用中我們將使用ValidField控件。ValidField控件位于表單內(nèi),對用戶在客戶端的校驗(yàn)提供了有用的反饋和視覺上的錯(cuò)誤提示。
區(qū)域化
在Tapestry中,區(qū)域化是相當(dāng)簡單的。Tapestry允許文字和圖形的區(qū)域化。為了區(qū)域化頁面的內(nèi)容,你可以為每一個(gè)添加一個(gè)properties文件,或者提供一個(gè)區(qū)域化的模板。為每個(gè)頁面提供一個(gè)資源文件的方式遠(yuǎn)比管理和維護(hù)一個(gè)巨大的全局的應(yīng)用范圍的資源文件簡單。如果頁面的區(qū)域化并不僅僅只是文字信息的區(qū)域化,例如頁面的布局不同或者組成的控件不同,這樣情況提供區(qū)域化的頁面模板就能派上用場了。我們會在下面的登錄應(yīng)用的使用Tapestry區(qū)域化。
創(chuàng)建控件
Tapestry發(fā)布時(shí)提供了40多個(gè)自帶的控件。如果你想知道更多的關(guān)于Tapestry自帶控件的信息,請參考Tapestry Component Reference.想看看Tapestry的控件的應(yīng)用范例可訪問Tapestry Component Workbench.如果你發(fā)現(xiàn)你需要一個(gè)Tapestry本身沒提供的控件,你可以自己創(chuàng)建一個(gè)。創(chuàng)建你自己的Tapestry控件跟創(chuàng)建一個(gè)頁面是相似的。一個(gè)典型的Tapestry控件由一個(gè)控件規(guī)范文件(XML文檔),一個(gè)HTML控件模板,一個(gè)實(shí)現(xiàn)了org.apache.tapestry.Icomponent接口的Java類。這個(gè)議題有點(diǎn)超出本文的范圍,但是如果你有興趣學(xué)習(xí)如何創(chuàng)建你自己的Tapestry控件,你可以參考Tapestry的原創(chuàng)人,Tapestry In Action 一書的作者--Howard Lewis Ship寫的 Designing Tapestry Mega-Components 。
Tapestry 登錄應(yīng)用
你在Pig latin翻譯器應(yīng)用中看到了Tapestry的一些基本特性。與其用一個(gè)復(fù)雜的應(yīng)用展示Tapestry所有的特性以致于壓得你揣不過氣來,還不如通過一些簡單的應(yīng)用讓你找到一點(diǎn)對Tapestry的感覺。下面這個(gè)應(yīng)用展示Tapestry如何處理頁面導(dǎo)航,區(qū)域化,驗(yàn)證和其他一些特性。
這里有一個(gè)Home頁的屏幕抓圖,下面跟著它的頁面模板。

Home.html
??? <html>
??? <head>
??????? <title>Welcome to the Tapestry Login Application</title>
??? </head>
??? <body>
??????? <h1>Welcome to the Tapestry Login Application</h1>
??????? <span jwcid="@PageLink"① page="Login">Login</span>
??? </body>
??? </html>
???
這個(gè)Home頁的頁面模板除了一個(gè)jwcid屬性定義使用一個(gè)Tapestry PageLink①控件以外都是標(biāo)準(zhǔn)的HTML。
PageLink控件生成了一個(gè)指向Login頁的超鏈接。既然Home頁沒有任何動態(tài)的行為所以它不需要頁面規(guī)范和頁面對應(yīng)的Java類。
這里是Login頁的屏幕抓圖,后面跟著是它的頁面模板。

Login.html
??? <html>
??? <head>
??????? <title>
??????????? <span key="title">①Login</span>
??????? </title>
??? </head>
??? <body jwcid="@Body">②
??????? <span jwcid="@Conditional" condition="ognl:beans.delegate.hasErrors">③
??????????? <div style="color: red">
??????????????? <span jwcid="@Delegator" delegate="ognl:beans.delegate.firstError">④
??????????????????? Error Message
??????????????? </span>
??????????? </div>
??????? </span>
??????? <p style="font-weight: bold" >
??????????? <span key="hint">Hint: Your password is your username spelled backwards.</span>
??????? </p>
???????
??????? <form jwcid="@Form" listener="ognl:listeners.login" delegate="ognl:beans.delegate">
⑤
??????????? <table>
??????????????? <tr>
??????????????????? <td align="right">
??????????????????????? <span jwcid="@FieldLabel" field="ognl:components.inputUsername"⑥>
??????????????????????????? Username:
??????????????????????? </span>
??????????????????? </td>
??????????????????? <td>
??????????????????????? <input type="text" jwcid="inputUsername"⑦ value="simpson_h"
size="30"/>
??????????????????? </td>
??????????????? </tr>
??????????????? <tr>
??????????????????? <td align="right">
??????????????????????? <span jwcid="@FieldLabel" field="ognl:components.inputPassword">
??????????????????????????? Password:
??????????????????????? </span>
??????????????????? </td>
??????????????????? <td>
??????????????????????? <input type="text" jwcid="inputPassword" hidden="true" value=""
size="30"/>
??????????????????? </td>
??????????????? </tr>
??????????????? <tr>
??????????????????? <td colspan="2" align="center">
??????????????????????? <input type="submit" jwcid="@Submit" value="message:login"/>
??????????????????? </td>
??????????????? </tr>
??????????? </table>
??????? </form>
??? </body>
??? </html>
???
這個(gè)頁面模板大多數(shù)是通常的HTML。我們從頁面模板中可以看到Tapestry的區(qū)域化特性:它使用一個(gè)span元素,這個(gè)span元素帶有一個(gè)叫key的屬性,key的值映射到Login.properties文件里一個(gè)屬性。一個(gè)Body控件被聲明使用,因?yàn)樗鼘蛻舳说腏avaScript校驗(yàn)是必需的。
為Form component⑤設(shè)定delegate屬性激活表單輸入驗(yàn)證。delegate屬性是我們在頁面規(guī)范里聲明的org.apache.tapestry.valid.IvalidationDelegate的實(shí)現(xiàn)類。如果驗(yàn)證錯(cuò)誤發(fā)生了,我們用Conditional component③控件判斷delegate是否有任何錯(cuò)誤,如果有就把第一個(gè)錯(cuò)誤④顯示給用戶。如果ognl 表達(dá)式ognl:beans.delegate.hasErrors 為true,Conditional控件將顯示它的內(nèi)容實(shí)體。所有的頁面類和控件類都從AbstractComponent繼承來一個(gè)叫beans的屬性。這個(gè)beans屬性是一個(gè)org.apache.tapestry.IbeanProvider的實(shí)例,利用它可以通過名字取得在頁面規(guī)范文件里定義的beans.FieldLabel⑥被用于為inputuserName validField控件顯示標(biāo)簽,這個(gè)FieldLabel控件也被用來與表單的驗(yàn)證代理協(xié)作,指出包含錯(cuò)誤的輸入域。
InputUserName⑦控件是一個(gè)顯示控件的例子。顯式控件是指在頁面規(guī)范文件聲明的控件。InputUsername和inputPassword控件都是顯式的,它們與FieldLabel聯(lián)合顯示它們的displayName屬性。
下面的是Login頁的資源文件。Login.properties跟頁面規(guī)范一并存放在WEB-INF目錄。
Login.properties
??? title = Login to the Application
??? hint = Hint: Your password is your username spelled backwards.
??? login = Login
??? username = Username:
??? password = Password:
??? invalidpassword = Invalid Password
???
Here is the page specification for the Login page.
Login.page
??? <?xml version="1.0"?>
??? <!DOCTYPE page-specification PUBLIC
??????? "-//Apache Software Foundation//Tapestry Specification 3.0//EN"
??????? "http://jakarta.apache.org/tapestry/dtd/Tapestry_3_0.dtd>
??? <page-specification class="com.ociweb.tapestry.Login">
??????? <bean name="delegate" class="org.apache.tapestry.valid.ValidationDelegate"/>①
??????? <bean name="requiredValidator"②
??????????? class="org.apache.tapestry.valid.StringValidator">
??????????? <set-property name="required" expression="true"/>
??????????? <set-property name="clientScriptingEnabled" expression="true"/>
??????? </bean>
??????? <property-specification name="username" type="java.lang.String"/>
??????? <property-specification name="password" type="java.lang.String"/>
??????? <component id="inputUsername" type="ValidField"> ③
??????????? <message-binding name="displayName" key="username"/> ④
??????????? <binding name="validator" expression="beans.requiredValidator"/> ⑤
??????????? <binding name="value" expression="username"/> ⑥
??????? </component>
??????? <component id="inputPassword" type="ValidField"> ⑦
??????????? <message-binding name="displayName" key="password"/>
??????????? <binding name="validator" expression="beans.requiredValidator"/>
??????????? <binding name="value" expression="password"/>
??????? </component>
??? </page-specification>
???
Page-specification元素的class屬性和兩個(gè)property-specification元素與Pig Latin翻譯器應(yīng)用是相似的。
你會發(fā)現(xiàn)第一個(gè)新東西-bean元素①,bean元素把一個(gè)org.apache.tapestry.valid.ValidationDelegate
的實(shí)例指定了名稱"delegate"。頁面HTML模板里的Form控件把它的參數(shù)delegate設(shè)定為
ognl:beans.delegate,就是指向了這個(gè)org.apache.tapestry.valid.ValidationDelegate實(shí)例。
Bean元素②把一個(gè)org.apache.tapestry.valid.StringValidator的實(shí)例指定了名稱"
requiredValidator",以用于驗(yàn)證。這個(gè)bean的required屬性被設(shè)為true表明使用這個(gè)bean的域是必須被驗(yàn)證的。這個(gè)bean的clientScriptingEnabled屬性被設(shè)定為ture,表明使用這個(gè)bean的域客戶端的JavaScript驗(yàn)證功能是激活的。RequiredValidator bean被用于驗(yàn)證inputUsername和inputPassword的內(nèi)容。
控件inputUsername③被控件規(guī)范聲明為ValidField,ValidField是一種用于Tapestry驗(yàn)證子系統(tǒng)的特殊版本的TextField控件。Message-binding元素被用于指定inputUsername控件的displayName參數(shù)的值,這個(gè)值是用"username"為關(guān)鍵字從login.properties④文件里得到。InputUsername控件的validator參數(shù)被設(shè)定為requiredValidator bean,這是我們在頁面規(guī)范里聲明過的⑤。控件的value參數(shù)跟頁面Java類的username屬性綁定在一起⑥。控件inputPassword的控件規(guī)范跟控件inputUsername幾乎相似,除了用于取得displayName的關(guān)鍵字和綁定的頁面Java類的屬性不同。
通過使用ValidField控件和為表單(form)提供一個(gè)ValidationDelegate, 我們激活了Login表單的驗(yàn)證功能。除了服務(wù)器端的驗(yàn)證,Tapestry也提供了客戶端的驗(yàn)證(利用JavaScript)。下面就是當(dāng)用戶提交一個(gè)表單而沒有為UserName域提供值時(shí),一個(gè)JavaScript錯(cuò)誤對話框彈出時(shí)的屏幕抓圖。

下面就是當(dāng)用戶提交一個(gè)表單而沒有為Password域提供值時(shí),一個(gè)JavaScript錯(cuò)誤對話框彈出時(shí)的屏幕抓圖。

下面就是Login頁對應(yīng)的Java 類。
Login.java
??? package com.ociweb.tapestry;
??? import org.apache.tapestry.html.BasePage;
??? import org.apache.tapestry.IRequestCycle;
??? import org.apache.tapestry.valid.ValidationConstraint;
??? import org.apache.tapestry.valid.IValidationDelegate;
??? public abstract class Login extends BasePage {
??????? public abstract String getUsername();
??????? public abstract void setUsername(String username);
??????? public abstract String getPassword();
??????? public abstract void setPassword(String password);
??????? public void login(IRequestCycle cycle) {
??????????? String username = getUsername();
??????????? String password = getPassword();
??????????? StringBuffer sb = new StringBuffer(username);
??????????? String validPassword = sb.reverse().toString();
??????????? if (password.equals(validPassword)) {
??????????????? cycle.activate("Success");①
??????????? } else {
??????????????? String errorMessage = getMessage("invalidpassword");②
??????????????? IValidationDelegate validationDelegate =
??????????????????????? (IValidationDelegate) getBeans().getBean("delegate");③
??????????????? validationDelegate.record(errorMessage,
????????????????????????????????????????? ValidationConstraint.CONSISTENCY);④
??????????? }
??????? }
??? }
???
跟Pig Latin翻譯器應(yīng)用一樣,我們的頁面類也是抽象的,它有抽象方法訪問在頁面規(guī)范里定義的屬性(properties)。Tapestry會在運(yùn)行時(shí)刻創(chuàng)建username和password屬性。Login方法只是簡單的驗(yàn)證一下用戶輸入的密碼值是否剛好是用戶名的反向。如果密碼通過驗(yàn)證,用戶將被引領(lǐng)導(dǎo)Success page①。
如果密碼輸入有誤,我們用關(guān)鍵字"invalidPassword"通過從org.apache.tapestry.AbstractComponent里繼承來的getMessage()方法從Login.properties②里查找對應(yīng)的資源。我們需要把密碼錯(cuò)誤信息紀(jì)錄到我們在頁面規(guī)范中定義的頁面validation delegate中去。我們可以利用我們在頁面規(guī)范中指定的名稱,從頁面的beans屬性中找回validationDelegate③。最后,我們調(diào)用org.apache.tapestry.valid.IvalidationDelegate的record方法把將要顯示給用戶看的錯(cuò)誤信息保存起來。下面就是當(dāng)用戶輸入錯(cuò)誤密碼的提交后的屏幕抓圖。

下面就是Success頁的頁面模板。Success頁的頁面模板僅僅包含HTML標(biāo)識,所以它不需要頁面規(guī)范和頁面
Java類。
Success.html
??? <html>
??? <head>
??????? <title>Successful Login</title>
??? </head>
??? <body>
??????? <p>
??????????? Congratulations! You have successfully logged on.
??????? </p>
??? </body>
??? </html>
???
總結(jié)
我希望這篇文章已經(jīng)向你展示了Tapestry框架在構(gòu)建Web應(yīng)用的是多么簡單,然而優(yōu)雅。Tapestry與大多數(shù)主流Web應(yīng)用框架最大不同在于它讓你用基于控件的方式開發(fā),而非以操作為中心的方式開發(fā)。如果這篇文章激起了你的興趣,我建議你把它下載下來利用它你自己的簡單的Web應(yīng)用。通過感受簡單的應(yīng)用,這是你了解這個(gè)框架的優(yōu)點(diǎn)的唯一途徑。如果你想在你的下一個(gè)項(xiàng)目里使用Tapestry,我強(qiáng)烈建議你購買
Tapestry In Action 這本書。我擁有這本書,對它我感到很滿意。
References
1 Zip file with all source code and war files from the article. (12K)
???????????????? http://www.ociweb.com/jnb/jnb2004_05.zip
2 Tapestry Home Page http://jakarta.apache.org/tapestry/
3 Tapestry In Action Page http://www.manning.com/lewisship/
4 Tapestry Wiki http://jakarta.apache.org/tapestry/wiki_frame.html
5 OGNL page http://www.ognl.org/
6 Tapestry Component Reference
???????????????? http://jakarta.apache.org/tapestry/doc/ComponentReference/index.html
7 Tapestry Component Workbench http://www.t-deli.com/app
8 Designing Tapestry Mega-Components
???????????????? http://www.onjava.com/pub/a/onjava/2001/11/21/tapestry.html
注:原文地址:http://www.inspiresky.com/Article/java/2006-02-14/412.html