最后一部分便是我们的demo.Home这个JAVA类,它实际上就是一个普通的javaBean,唯一不同的就是它必须从BasePage类中继承。它的作用就是为HTML模板中的user组件提供文本信息的来源,在本例中我们简单的返回”Jack”,但实际上getUserName方法还可以从JNDI、数据库、EJB中取得用户名。
其实demo.Home不仅仅只是提供数据来源,它还可以实现表单组件的提交。Tapestry提供了2个基本类方便用户进行扩充,一个是BasePage类(专用于Page组件),另一个是BaseComponent类(用于用户自定义组件)。通过继承这些组件,可以大大地减轻用户的编程量,从而把精力放到程序流程设计上去。
demo.Home组件类的名称应该与Home.page中定义的名称一致,它一般放在当前web应用的WEB-INF/classes目录下,以本例来说,Home.class这个类应该放在webapps/Welcome/WEB-INF/classes/demo目录下面。
Welcome.application
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE application PUBLIC "-//Apache Software Foundation//Tapestry Specification 3.0//EN" "http://jakarta.apache.org/tapestry/dtd/Tapestry_3_0.dtd">
<application name="Tapestry Illustration 1"> <page name="Home" specification-path="Home.page"/> </application> |
以上这个文件是这个Tapestry应用的配置文件,它是以application作为根元素的,其中page元素指定了一个名为Home的Page组件,它的文件名为Home.page,当然你也可以指定更多的page组件,这个配置文件就像是一个总装车间,把一个个page组件或其它的组件装配起来。
关于这个配置文件的命名规范我们在前面已经讲过了,它一般放在WEB-INF目录下,在本例中它放在了webapps/Welcome/WEB-INF目录下面。
web.xml
<?xml version="1.0"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
<web-app> <display-name>Tapestry Welcome Application</display-name>
<servlet> <servlet-name>Welcome</servlet-name> <servlet-class>org.apache.tapestry.ApplicationServlet</servlet-class> <load-on-startup>0</load-on-startup> </servlet>
<servlet-mapping> <servlet-name>Welcome</servlet-name> <url-pattern>/app</url-pattern> </servlet-mapping>
<session-config> <session-timeout>15</session-timeout> </session-config>
<welcome-file-list> <welcome-file>index.html</welcome-file> </welcome-file-list> </web-app> |
Listing 5. web.xml
这个文件相信所有的JSP/servlet开发员都非常熟悉了吧?在这个文件中我们定义了一个名为Welcome的servlet,它实际上就是org.apache.tapestry.ApplicationServlet这个类,它的主要作用是对客户的请求进行包装,然后转发到各个page组件进行处理,当然如果你有特殊需要,也可以继承ApplicationServlet类(当然这种情况很少出现)。
其中<servlet-mapping>用来进行地址映射,相信servlet开发员应该都知道它的作用吧?我们把所有对于/app的请求全部转发给Welcome这个servlet进行处理,当然你也可以将/app换成其你自己的定义。最后我们打开一个IE浏览器,在地址栏中输入http://localhost:8080/Welcome/app 就会看到结果。
好了,现在我们就讲一下关于上面那个用户注册的例子吧。由于篇幅的关系,我就不把所有的配置文件一一罗列了,只摘录一些关键的配置。
CustInfo.html
<html jwcid="@Shell" title="Welcome Page"> <body jwcid="@Body"> <form jwcid="@Form" listener="ognl:listeners.submit"> Customer Name: <input jwcid="custName" type="text"/><br/> Date-of-Birth: <input jwcid="dob" type="text" format="MMM dd, yyyy"/> (Month DD, YYYY)<br/> <input type="submit" value="Submit"/> </form> </body> </html> |
也许你看了上面这个文件不禁会问:为什么有的组件名称前面加了一个“@”?原因如下:tapestry由于是由众多组件组成的,其中一般的组件都要在其Page组件中用<component>元素进行定义,对于一些简单的或者无其它附加参数的组件来说就显得比较麻烦,因此tapestry提出了显式定义组件和隐式定义组件这个概念,显式定义是指明确地在Page组件中定义过的组件,隐式定义是指没有明确地在Page组件中定义的组件,都要在组件名字前加一个“@”。
这里有一个组件显得很特别:@Form组件,这个组件由于是隐式定义地,因此它的参数就直接在HTML模板中进行定义:listener="ognl:listeners.submit",它表示当用户按下submit按钮后页面流程会交给当前Page组件类(即Welcome这个类)的submit函数进行管理。
下面我们再看一下它的Page组件的定义:
CustInfo.page
<?xml version="1.0" encoding="UTF-8"?> <!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="demo.Welcome"> <property-specification name="custName" type="java.lang.String"/> <property-specification name="dob" type="java.util.Date"/> <component id="custName" type="TextField"> <binding name="value" expression="custName"/> </component> <component id="dob" type="DatePicker"> <binding name="value" expression="dob"/> </component> </page-specification>
|
其中值得关注的是dob这个组件,它的类型是tapestry核心组件库中的DatePicker组件,有了它,我们就可以生成先前那个不可思义的选择日期的界面了。
这里出现了一个新面孔:property-specification元素,它是干什么用的呢?还是先让我们看一下那个“hello,world”例子中的组件类的定义吧:
Home.java
package demo; import org.apache.tapestry.html.BasePage; public class Home extends BasePage { private String userName = "Jack";
public String getUserName() { return this.userName; } } |
其中userName这个属性是一个javaBean属性,通过get或set方法来存取userName的值,当然对于本例,property-specification这个元素和它一样,就是定义了一个javaBean的属性,其实你也可以像上例那样进行存取,而不需在CustInfo.page中进行定义。定义property-specification元素的根本原因其实很可笑:为了偷一点懒! 为什么这么说呢?因为如果你用property-specification元素来定义javaBean的属性的话,那你在组件类中就不必实现其get或set方法了,只需用一个抽像方法来完成,至于真正的get或set方法的实现就由tapestry来代劳了,其中,Welcome这个类的代码如下:
Welcome.java
package demo; import java.util.Date; import org.apache.tapestry.IRequestCycle; import org.apache.tapestry.html.BasePage;
public abstract class Welcome extends BasePage { public abstract void setCustName(String custName); public abstract void setDob(Date dob); public abstract String getCustName(); public abstract Date getDob(); public void submit(IRequestCycle cycle) {
if (getCustName() != null && !getCustName().trim().equals("") && getDob() != null) { Welcome welcome = (Welcome) cycle.getPage("Welcome"); welcome.setCustName(getCustName()); welcome.setDob(getDob()); cycle.activate(welcome); } } } |
前面的几个抽像方法就不多说了,Welcome类的submit方法和在HTML模板中定义的@Form组件中的listener参数正好对应,也就是说,当用户按下提交按钮后,这个submit方法就会被激活,它进一步地引导着页面下一步的动作。在本例中它先检查用户名和用户出生日期(DOB,date of birthday的简称)是否为空,如果不为空,就将用户在页面中输入的值赋予Welocome这个类的custName和dob这两个属性,然后激活welcome这个Page组件,也就是将页面跳转到welcome这个Page上去。
需要注意的是,Form组件定义的监听类方法必须为public,并且要带一个IRequestCycle 的参数,IrequestCycle是一个接口,它是由tapestry提供的一个对于用户而言,当前会话的一个管理工具。
总结
到目前为止,你大概明白了Tapestry的原理了,但是一个复杂的Tapestry应用还是要考虑到很多问题的,比如页面的定义,页面之间的流程,组件元素的持久性设计,与EJB或原有的JSP系统的集成等等问题,但是Tapestry都已经为你考虑好了,你甚至可以把它的源码下载下来仔细研究,也可以到它的邮件列表上发表自己的观点,这正是我所喜爱的----open source ! 它代表着自由!