Tapestry Specifications

 1. Tapestry Specifications

所有的Specification,不管扩展名是什么,内容都是XML文件。
1.1. Application Specification - ${servlet.name}.application

每个应用程序一般只有一个。
1.2. Library Specification - ${library.name}.library

和Application Specification的结构基本相当,一般每个组件库的jar中一个。
1.3. Page Specification - ${page.name}.page

一个页面一个,应用程序是由许多Page组成。
1.4. Component Specification - ${component.name}.jwc

每个组件一个,如文本框,日期选择等。每个Page可以包含许多Component,每个Component中还可以包含许多Component。
2. 让Tapestry找到你的Application Specification
关联源文件: org.apache.tapestry.services.impl.ApplicationSpecificationInitializer

Application Specification,顾名思义,应用程序定义书。一般情况下,一个WEB应用程序只有一个。文件名为${servlet.name}.application。 其中${servlet.name}为web.xml中配置ApplicationServlet的Servlet名称,其放置的路径必须是/WEB-INF/${servlet.name}.application 或者/WEB-INF/${servlet.name}/${servlet.name}.application如:

<!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>Components for Tapestry Examples Web Application</display-name>
    ...
    <servlet>
        <servlet-name>comp4t-examples</servlet-name>
        <servlet-class>org.apache.tapestry.ApplicationServlet</servlet-class>.
        <load-on-startup>0</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>comp4t-examples</servlet-name>
        <url-pattern>/app</url-pattern>
    </servlet-mapping>
    ...

</web-app>

这样配置的web.xml文件的话,那么Application Specification应该为comp4t-examples.application, 其位于的路径为/WEB-INF/comp4t-examples.application或者/WEB-INF/comp4t-examples/comp4t-examples.application
3. 让Tapestry找到你的Page Specification
关联源文件: org.apache.tapestry.resolver.PageSpecificationResolverImpl
3.1. 置Page Specification于Application Specification相同路径中

最简单,最直接的办法就是这个,只要和Application Specification保持相同路径,就不怕Tapestry找不到它。
3.2. 置Page Specification于/WEB-INF/中

3.3. 置Page Specification于/WEB-INF/${servlet.name}/中

3.4. 置Page Specification于/中,即WEB应用程序最根目录中

3.5. 置Page Specification于相应java文件路径下,但需Application/Library Specification中配置其相应路径。

一般可以放置相应的java文件相同的路径下,如com.javaforge.comp4t.examples.FrameworkComponentExamples, 那么把Page Specification放在/com/javaforge/comp4t/examples/下比较整齐。然后须在Application Specification指定。

<application name="Components for Tapestry Examples">
    ...
    <page name="FrameworkComponentExamples" specification-path="/com/javaforge/comp4t/examples/FrameworkComponentExamples.page"/>
    <page name="Home" specification-path="/com/javaforge/comp4t/examples/Home.page"/>
    ...
</application>

这样的好处是所有的Tapestry Page都列在了Application Specification。便于管理和整理。
4. 管理服务器端状态

每次提交中需要保存以备后续请求处理中使用的数据。
4.1. 保持住Page的属性(Page属性的持久化)

提到Session,大家不会陌生,因为我们要想在其他的服务请求中得到之前的状态,就用到它,最典型的就是用户验证后的登陆信息。最普通的做法就是在用户正常的Login之后,把用户的信息放到Session中,在后面处理中需要用户信息时,从Session中取出。在Tapestry中是不推荐直接使用Session的。框架的作用是什么?就是用封装,抽象还使你需要做的工作得到简化。

下面是一个简单的例子,在Tapestry中我们不再需要和Session打交道。Tapestry已经为你做好了这一切。

public abstract class FrameworkComponentExamples extends BasePage {
    ...
    public abstract List getRecords();

    public abstract void setRecords(List records);
    ...

}

<page-specification class="com.javaforge.comp4t.examples.FrameworkComponentExamples">
    ...
    <property name="records" persist="session"/>
    ...
</page-specification>

这样的配置非常明确的告诉了Tapestry将FrameworkComponentExamples页面的records属性放置到Session中。在以后的调用中, 只要你调用了getRecords()方法,那么Tapestry会从Session中取出这个对象,要是你调用了setRecords(myList),那么Tapestry会将myList设置到Session中,那么Session中的键值是如何维护的呢,我们并没有提供给Tapestry呀,一句话,Tapestry自己拼凑出来的。 后面提到拼凑方法。

需要注意的一点,我们的Page中的属性必须是abstract的, Tapestry文档中会说到,不用abstract也可以,但我告诉你,最好使用上面的写法。想象一下,只有是抽象的,不提供实现,才有可能是从Session或Client中取得属性值, 如果你给他写了实现,而你的实现很可能定义一个成员变量,那到底是从你这个成员变量中取,还是从Session中取。
4.2. 关于Page属性的Session Key
关联源文件: org.apache.tapestry.record.SessionPropertyPersistenceStrategy

拼凑的方法是${applicationId},${pageName},${idPath},${propertyName}其中${idPath}可能没有,

${applicationId}为前面提到的Servlet名,即web.xml中配置的ApplicationServlet的Servlet名,如comp4t-examples。

${pageName}为页面名,如FrameworkComponentExamples。

如果该页面中调用和其他组件,而且组件中的某个属性指定了持久化(persist)属性,那么${idPath}就会存在。

${propertyName}为属性名,如records。

这样的话整个Session Key为comp4t-examples,FrameworkComponentExamples,records,不会重复。
4.3. 页面属性的持久化策略(PersistenceStrategy)

tapestry.persist.xml

  <contribution configuration-id="PersistenceStrategy">
    <strategy name="session" object="service:SessionPropertyPersistenceStrategy"/>
    <strategy name="client" object="service:PageClientPropertyPersistenceStrategy"/>
    <strategy name="client age" object="service:PageClientPropertyPersistenceStrategy"/>
    <strategy name="client:app" object="service:AppClientPropertyPersistenceStrategy"/>
  </contribution>

5. 构造友好的URL。

何为不友好的URL,如

/comp4t-examples/app?component=%24DirectLink&page=Home&service=direct&sp=S123456

/comp4t-examples/app?digest=a4008189d74c98218e216c1e2ce53193&path=%2Fcom%2Fjavaforge%2Fcomp4t%2Fexamples%2Fstyle.css&service=asset

上面的的两例称之为非友好URL,为什么呢,太乱了,不仔细分析是无法弄清其中道理,那为什么会存在呢?因为机器能看懂。虽然乱,但也是标准的URL。

看看下面两例

/comp4t-examples/Home,$DirectLink.d?sp=S123456

/comp4t-examples/assets/a4008189d74c98218e216c1e2ce53193/com/javaforge/comp4t/examples/style.css

虽然没有达到最佳效果,但是进步是很明显的,他和上面的可以表达相同的信息量,因为都能被Tapestry解释。

当我们不对Tapestry进行任何配置的时候,在程序运行过程中,出现的URL是第一种,也是在Tapestry 3.x中固定使用的一种。 但在Tapestry 4.0中有了很大的改观,只要稍加配置,即可形成第二种的效果。

或许你对Hivemind还不了解, 但是我不得不告诉你,Tapestry 从4.0 开始基于Hivemind,可以这样说,不了解Hivemind, 你便精通不了Tapestry。 虽然你很可能不想涉入新的东西,但是新的东西也会给你带来新的效果。你的学习不会浪费。

下面就是一段Hivemind配置,来达到第二种URL效果。位于classpath:/META-INF/hivemodule.xml文件中

<module id="comp4t.examples" version="1.0.0" package="com.javaforge.comp4t.examples">
    ...
    <contribution configuration-id="tapestry.url.ServiceEncoders">
        <page-service-encoder id="page" extension="html" service="page"/>
        <direct-service-encoder id="direct" stateless-extension="d" stateful-extension="sd"/>
        <asset-encoder id="asset" path="/assets/"/>
    </contribution>
    ...
</module>

其实很多时候,我们并不需要知道是什么意思,只需要知道代码要写的位置,和能起到的效果。 但是如果你真想知道其中原委,那我给你解释。 TODO:解释
6. 对象和字符串之间的转换 - DataSqueezer

关联源文件: tapestry.data.xml

在WEB 应用中,不可避免出现对字符串和对象进行转换,因为浏览器表单数据或者是HTTP URL,或者Cookie全部都是字符串,而我们的应用中,非常普遍的做法,就是对提交上来的数据和对象进行绑定,在Struts和Spring中用表单中项目的名称和对象的属性进行对应(并做了简单原始类型的转换)。 但在Tapestry中做法是不同的,DataSqueezer和SqueezeAdaptor起着关键性的作用。

我们简单看一下Tapestry中的Test,就不难发现其中转换的规律。

org.apache.tapestry.junit.utils.TestDataSqueezer

    public void testBoolean()
    {
        attempt(Boolean.TRUE, "T" ;
        attempt(Boolean.FALSE, "F" ;
    }

    public void testNull()
    {
        attempt(null, "X" ;
    }

    public void testByte()
    {
        attempt(new Byte((byte) 0), "b0" ;
        attempt(new Byte((byte) -5), "b-5" ;
        attempt(new Byte((byte) 72), "b72" ;
    }

    public void testFloat()
    {
        attempt(new Float(0), "f0.0" ;
        attempt(new Float(3.1459), "f3.1459" ;
        attempt(new Float(-37.23), "f-37.23" ;
    }

    public void testDouble()
    {
        attempt(new Double(0), "d0.0" ;
        attempt(new Double(3.1459), "d3.1459" ;
        attempt(new Double(-37.23), "d-37.23" ;
    }

    public void testInteger()
    {
        attempt(new Integer(0), "0" ;
        attempt(new Integer(205), "205" ;
        attempt(new Integer(-173), "-173" ;
    }

    public void testLong()
    {
        attempt(new Long(0), "l0" ;
        attempt(new Long(800400300l), "l800400300" ;
        attempt(new Long(-987654321l), "l-987654321" ;
    }

    public void testShort()
    {
        attempt(new Short((short) 0), "s0" ;
        attempt(new Short((short) -10), "s-10" ;
        attempt(new Short((short) 57), "s57" ;
    }

    /** @since 2.2 * */

    public void testCharacter()
    {
        attempt(new Character('a'), "ca" ;
        attempt(new Character('Z'), "cZ" ;
    }

    public void testString()
    {
        attempt("Now is the time for all good men ...", "SNow is the time for all good men ..." ;
        attempt("X marks the spot!", "SX marks the spot!" ;
        attempt("So long, sucker!", "SSo long, sucker!" ;
    }

    public void testComponentAddress()
    {
        ComponentAddress objAddress = new ComponentAddress("framework irectLink",
                "component.subcomponent" ;
        attempt(objAddress, "Aframework irectLink,component.subcomponent" ;

        objAddress = new ComponentAddress("framework irectLink", null);
        attempt(objAddress, "Aframework irectLink," ;
    }

每种类型都对应一个或者一串前缀,一个字符串如果开头单个字母是前缀中的一个的时候,那么就被确定为该类型,如"T"是"TF"中之一,那么"T"字符串被确定为Boolean形。

Table 1. SqueezeAdaptor
类型 Adaptor 前缀 举例
null   X X
Boolean BooleanAdaptor TF T, F
Byte ByteAdaptor b b127 ,b0, b-5
Character CharacterAdaptor c ca, cZ
Double DoubleAdaptor d d0.0, d3.1459, d-37.23
Float FloatAdaptor f f0.0, f3.1459, f-37.23
Integer IntegerAdaptor -0123456789 0, 205, -173
Long LongAdaptor l l0, l800400300, l-987654321
Short ShortAdaptor s s0, s-10, s57
String StringAdaptor S SNow is the time, SX marks
Serializable SerializableAdaptor OZ O123NWEQ..., Z23423EXF...
ComponentAddress ComponentAddressAdaptor A Aframework irectLink,component.subcomponent
7. Form处理的核心阶段Rewind

所有的Form组件(Form Control Component),如:文本框,文本域,提交按钮,选择列表等等(但是Form本身不属于此列,它只是一个装Form组件的容器) 都继承自AbstractFormComponent,在AbstractFormComponent中有一个非常重要的方法rewindFormComponent。当画面中的Form进行提交时,第一个阶段就是Rewind阶段。 Form对象会首先进行循环调用其中所有的Form组件的rewindFormComponent方法。

根据Form组件的性质,每个 Form组件中的rewindFormComponent方法的实现是不同的,如文本框,列表,单选,多选等都是简单的把提交上去的值重新付给这些组件对象,并且在同时进行了对Page中相应属性的绑定(即把这些值更新到Page的属性当中去)。但是如提交按钮,提交链接中所做的则是把Template中指定的监听加到Form对象当中去,以便Rewind过程结束之前,调用这些Page中的监听方法(FormSupportImpl的runDeferredRunnables()方法)。
8. Tapestry中的Validation
8.1. Tapestry中提供的Validator

Tapestry中提供了许多Validator,我们直接使用他们就可能完成大部分通常的Check。
关联源文件tapestry.form.validator.xml

  <contribution configuration-id="Validators">
    <validator name="email" configurable="false" class="Email"/>
    <validator name="required" configurable="false" class="Required"/>
    <validator name="max" class="Max"/>
    <validator name="maxDate" class="MaxDate"/>
    <validator name="maxLength" class="MaxLength"/>
    <validator name="min" class="Min"/>
    <validator name="minDate" class="MinDate"/>
    <validator name="minLength" class="MinLength"/>
    <validator name="pattern" class="Pattern"/>    
  </contribution>

8.2. 简单的例子

<input type="text" jwcid="minAndMax@TextField"
       value="ognl:minAndMax"
       translator="translator:number,pattern=#####"
       displayName="literal:Min,Max"
       validators="validators:min=3,max=5"/>

上面是一个非常简单的例子,达到的目的就是改组件对应的Page的int属性minAndMax必须在3和5之间,由于Page的minAndMax属性为 int型, 不是字符串,所以我们必须告诉Tapestry如何将页面中的值转换为Page的属性,所以这里面就指定了translator。
8.3. Validation Specification - validators属性的格式
关联源文件org.apache.tapestry.form.validator.ValidatorFactory

里面的内容前缀首先是validators:,Validator之间用逗号分隔,每个Validator的格式可以是下面形式

   1.

      name
   2.

      name=value
   3.

      name[message]
   4.

      name[%message-key]
   5.

      name=value[message]
   6.

      name=value[%message-key]
   7.

      $name

其中name为Validator名,如:email, required, max, maxDate, min等等。

value为传入的参数值,如:min=3, max=5等。

message为当该Validator校验失败后显示的Message。

message -key为本地化文件中Message的Key值, 如Tapestry Page Name为Login的话,那么相应的有Login.html,Login.page,Login.properties,Login_zh_CN.properties等,这里面的两个properties文件就是本地化文件。

下面是具体的例子

ValidationExamples.html中

<input type="text" jwcid="minAndMaxWithMessage@TextField"
       value="ognl:minAndMaxWithMessage"
       translator="translator:number,pattern=#####"
       displayName="literal:Min,Max"
       validators="validators:min=3[%must-larger-than-3],max=5[Must less than 5]"/></td>

那么在ValidationExamples.properties中为

must-larger-than-3=Field {0} must larger than 3.

需要注意的是,其中的{0}参数会被自动用Field名替换。

$name中的name是对一个bean的引用,该bean可以在Page Specification中定义。

如在ValidationExamples.page中定义为

    <bean name="zipValidator" class="org.apache.tapestry.form.validator.Pattern">
        <set name="pattern" value="message:zip-code-pattern"/>
        <set name="message" value="message:zip-code-message"/>
    </bean>

那么在ValidationExamples.html中就可以像下面这样引用。

<input type="text" jwcid="pattern@TextField" 
       value="ognl attern" 
       displayName="literal:Pattern" 
       validators="validators:$zipValidator"/>

9. 特殊用语的解释
9.1. classpath:/META-INF/hivemodule.xml中的classpath

表示在classpath下/META-INF/中的hivemodule.xml文件 , 所谓classpath,在WEB应用程序中,通常指/WEB-INF/lib下的每一个jar中,或者/WEB-INF/classes下。
9.2. ${servlet.name}.application中的${}

是一个替代符号,或者叫变量名,上面的表达表示一个通用的名字,如在应用程序中可能具体为MyTapestryExamples.application, PetStore.application等。
loading...

你可能感兴趣的:(servlet,session,service,tapestry,byte,login)