ZOL博客
登录  |   注册  |   帮助
我的主页 日志 好友 相册
论坛
视频装备圈子 网摘
ZOL博客 > 小默默的哭泣 的主页 > 日志

Tapestry入门及进阶


2006-09-12 12:26:30
Tapestry开发一个Web Application,对一个新手来说有点困难的,Tapestry由于不同于

以前的Web Presentation Framework,所以不可讳言,学习曲线比较长这是事实。

我先讲讲一个Web Application的大体结构:
以JBuider9为开发工具,你要先建立一个工程,例如是名称是TapestryExmaple,它的

workspace是F:myprojectTapestryExmaple.它下面的子目录和文件有
bak--   这是Jbuider自建的backup目录。
build--  我的ant的工作目录,我的ant会利用这个目录编译打包。
classes-- JBuilder编译时放class的目录
configs-- 我的一些配置文件放置的地方
lib--      我的要用到的库文件放置的目录
context-- 我的Web application的context目录
 doc-- 我的文档目录
src--     我的java source的目录
build.properties--ant工作的属性设置文件
build.xml--ant工作的定义文件

你建立好这个工程以后,你好需要建立Web Application, 点击File-->New-->Web-->Web
Application,因为我们需要在JBuilder里利用Tomcat4.1调试,用Jboss3.x也可以,不过你
要去下载一个插件叫 EntWizard_JBoss3x_JB9_v3-1-5.zip 的插件,JBuiderx提供对JB
oss的支持,不过Jbuiderx经常有些莫名其妙的怪毛病,而且比较慢,我是不用的。

你建立Web Application的时候你要输入Name和Directory两项值,Name比如是
patientrecord,注意你在这里输入的name,会在你将来测试时的URL里出现:
你的URL就可能像这样:http://localhost:8080/patientrecord/app
后面那个app是在web.xml里配置的,下面再说。
你的Web Application的Directory值要跟我们上面预定的一致,就是那个context目录,

在这里就是F:myprojectTapestryExmaplecontext,这个context目录是web applicatio
n
的核心目录,下面要详细的讲讲。

例如像我的context目录下面有这三个目录
home  -- 我的web applicattion的子模块home的context
     下面有子目录  css--放置本子模块用到的css文件
            images--放置本子模块用到的image(图标)
        文件: Home.html,Register.xml ...
                        这些是我字模块中的页面对应的HTML template(模版)   
  
patientrecord--我的web applicattion的子模块patientrecord的context
        目录结构跟home子模块相同

WEB-INF--最核心的目录
     下面的子目录和文件有
           classes--你的java classes会在这里有一份拷贝
      lib--你的工程引用的lib在这里用一份拷贝
      patientrecord.application--你的Tapestry核心配置文件
      web.xml--web application的核心配置文件

下面我们来研究一下patientrecord.application和web.xml这两个文件,我的范例文
件如下:
web.xml

<?xml version="1.0" encoding="UTF-8"?>
<!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>Patient Record System</display-name>
  <filter>
    <filter-name>redirect</filter-name>
    <filter-class>org.apache.tapestry.RedirectFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>redirect</filter-name>
    <url-pattern>/</url-pattern>
  </filter-mapping>
  <servlet>
    <servlet-name>patientrecord</servlet-name>
    <servlet-class>com.ht.web.PatientRecordServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>patientrecord</servlet-name>
    <url-pattern>/app</url-pattern>
  </servlet-mapping>
  <session-config>
    <session-timeout>15</session-timeout>
  </session-config>
</web-app>  
          
这个里面属性有
display-name --不重要,只是显示名称
filter和filter-mapping配置--不要动,使用默认配置,
servlet配置--这个是很重要的,我的servle name是patientrecord, 对应的class为
com.ht.web.PatientRecordServlet,这是我自己开发的一个类:
这个类看上去很简单:

package com.ht.web;

import org.apache.tapestry.ApplicationServlet;

/**
*  @version $Id: SimpleServlet.java,v 1.9 2002/05/04 12:43:31 hship Exp $
*  @author Howard Lewis Ship
*
*/
public class PatientRecordServlet extends ApplicationServlet
{
}

你一般就是定义一个类extends org.apache.tapestry.ApplicationServlet就行了
当然,如果你还有什么特别要求,你可对这个类进行强化。
我再来谈谈这个核心类的作用:这其实是个Dispatcher(分发者),它接受外界传来的
http request请求,然后把请求处理后派发给Tapestry Engine处理.

图例:
http://bbs1.nju.edu.cn/file/high-level-component-request.p

servlet-mapping--这是URL映射的设置,一般设为app

<session-config>
    <session-timeout>15</session-timeout>
</session-config>
这段是对HTTP Session的timeout的配置,这里是15分钟。

下面再来研究一下patientrecord.application文件,这也是一个XML文件:
<?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="Patient Record System" engine- om.ht.web.PatientRecordEngine">
  <property name="org.apache.tapestry.visit-class" value="com.ht.web.VisitorState"/>

  <property name="com.ht.home-page" value="home:Home"/>

  <!--<property name="com.ht.exception-page" value="home:PaitentRecordException"/>--> 
  <property name="com.ht.security-exception-page" value="home:SecurityExceptionPage"/>

  <!--Overrided the Home Service to let us to decide which page will be the home page-->
  <service name="home" om.ht.web.HomeService"/>
 
  <library id="home" specification-path="/com/ht/home/Home.library"/>
  <library id="patientrecord" specification-path="/com/ht/patientrecord/PatientRecord.library"/>
</application>

由这个文档我们可以看出
我的application name="Patient Record System"
我的engine-class为com.ht.web.PatientRecordEngine
我的org.apache.tapestry.visit-class为com.ht.web.VisitorState
以上两项是override Tapestry的默认实现

<property name="com.ht.home-page" value="home:Home"/>
这是我配置我的home-page页指向我的web application的子模块home里Home页

<!--<property name="com.ht.exception-page" value="home:PaitentRecordException"/>--> 
  <property name="com.ht.security-exception-page" value="home:SecurityExceptionPage"/>

这两项是我设定出错处理页
其中的com.ht.exception-page是处理普通的org.apache.tapestry.ApplicationException,
现在被注释掉,因为目前在开发阶段,我要察看详细的出错情况,不需处理。
com.ht.security-exception-page是处理java.lang.SecurityException,
因为对于一个需要登录的网站,一个为登录的用户是不可以访问大多数资源,
如果他访问了不可访问的资源,我就抛出SecurityException交给engine处理。

  <!--Overrided the Home Service to let us to decide which page will be the home page-->
  <service name="home" om.ht.web.HomeService"/>
Tapestry engine 可以定义一些service提供服务,我在这里定义一个home service就是
要我自己决定我的home page页,而不是Tapestry的默认配置页。

<library id="home" specification-path="/com/ht/home/Home.library"/>
  <library id="patientrecord" specification-path="/com/ht/patientrecord/PatientRecord.library"/>

这两项是说明我的application包含两个library,这其实是一个Web application切分
子模块的手段,比如/com/ht/home/Home.library"/ 对应着我的home子模块,
而/com/ht/patientrecord/PatientRecord.library则对应着我的patientrecord子模块

下面说说Tapestry engine,例如我的engine实现如下:

package com.ht.web;

import java.io.*;
import java.net.URLEncoder;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.tapestry.*;
import org.apache.tapestry.engine.BaseEngine;
import org.apache.tapestry.engine.IPropertySource;
import org.apache.tapestry.request.ResponseOutputStream;
import org.apache.commons.lang.StringUtils;

/**
* @author Hery Tang
*
* Creation Date 2003-9-18
* Version 1.0
*/
public class PatientRecordEngine extends BaseEngine {
 
        public static final String HOME_PAGE_NAME = "com.ht.home-page";
        public static final String EXCEPTION_PAGE_NAME= "com.ht.exception-page";
        public static final String SECURITY_EXCEPTION_PAGE_NAME = "com.ht.security-exception-page";

        private transient boolean killSession;
       
        protected void cleanupAfterRequest(IRequestCycle cycle)
        {
                super.cleanupAfterRequest(cycle);
                if (killSession)
                {
                        try
                        {
                                HttpSession session = cycle.getRequestContext().getSession();

                                if (session != null)
                                {
                                        session.invalidate();
                                }
                        }
                        catch (IllegalStateException ex)
                        {
                                // Ignore.
                        }
                }
        }

        public void logout()
        {
                VisitorState visit = (VisitorState) getVisit();

                if (visit != null)
                {
                        visit.setUser(null);
                        visit = null;
                }

                killSession = true;
        }

        /**
         * This methods read the home page name which defined by the user by using
         * propetry com.ht.home-page in application specification
         *
         * @return The name of home page.
         */
        public String getHomePageName() {
                return getPropertySource().getPropertyValue(HOME_PAGE_NAME);
        }

        /**
         * Return the security exception page name. If user does not defined this
         * page name, the normal exception page will be return.
         * @return The name of the security exception page.
         */
        public String getSecurityExceptionPageName() {
                String result =
                        getPropertySource().getPropertyValue(
                                SECURITY_EXCEPTION_PAGE_NAME);

                if (StringUtils.isEmpty(result)) {
                        result = getExceptionPageName();
                }

                return result;
        }

        /**
         * Return the exception page name. If user does not defined this page name,
         * the default exception page will be return.
         * @return The name of the normal exception page.
         */
        public String getExceptionPageName() {
       
                String result =
                        getPropertySource().getPropertyValue(EXCEPTION_PAGE_NAME);

                if (StringUtils.isEmpty(result)) {
                        result = super.getExceptionPageName();
                }

                return result;
        }

        /**
         * Overide the method to support security Exception.
         */
        protected void activateExceptionPage(
                IRequestCycle cycle,
                ResponseOutputStream output,
                Throwable cause)
                throws ServletException {
                //Print it to console first
                printExceptions(cause);

                try {
                        String exceptionPageName;

                        Throwable throwable = cause;
                        Throwable securityException = null;
                        boolean isSecurityException = false;

                        if (throwable
                                instanceof org.apache.tapestry.ApplicationRuntimeException) {
                                ApplicationRuntimeException exception =
                                        (ApplicationRuntimeException) throwable;
                                if (exception.getRootCause() instanceof SecurityException) {
                                        securityException = exception.getCause();
                                        isSecurityException = true;
                                }
                        }

                        if (isSecurityException) {
                                exceptionPageName = getSecurityExceptionPageName();
                        } else {
                                exceptionPageName = getExceptionPageName();
                        }

                        IPage exceptionPage = cycle.getPage(exceptionPageName);
                        if (securityException == null) {
                                exceptionPage.setProperty("exception", cause);
                        } else {
                                exceptionPage.setProperty("exception", securityException);
                        }

                        cycle.activate(exceptionPage);

                        renderResponse(cycle, output);
                } catch (Throwable ex) {
                        // Worst case scenario.  The exception page itself is broken, leaving
                        // us with no option but to write the cause to the output.
                        reportException(
                                Tapestry.getMessage(
                                        "AbstractEngine.unable-to-process-client-request"),
                                cause);

                        // Also, write the exception thrown when redendering the exception
                        // page, so that can get fixed as well.
                        reportException(
                                Tapestry.getMessage(
                                        "AbstractEngine.unable-to-present-exception-page"),
                                ex);

                        // And throw the exception.
                        throw new ServletException(ex.getMessage(), ex);
                }
        }

        private void printExceptions(Throwable throwable) {
                if (throwable == null) {
                        return;
                }

                throwable.printStackTrace();
                printExceptions(throwable.getCause());
        }

       
}

 public void logout() 说明
   一个用户登出,则将一个代表用户状态的VisitorState对象里的状态清空,同时置
   killSession标志量为true

  protected void cleanupAfterRequest(IRequestCycle cycle)
    overrride父类BaseEngine实现,先调用父类实现然后再察看killSession标志量,如果为true,则invalidate session.

  public String getHomePageName()
  public String getSecurityExceptionPageName()
  public String getExceptionPageName()
都是为了取出我在在patientrecord.application的配置。

protected void activateExceptionPage(
                IRequestCycle cycle,
                ResponseOutputStream output,
                Throwable cause)
override AbstractEngine的是实现,用我自己的方式处理各种Exception.
下面来谈谈Tapestry的开发流程。
Tapestry的开发主要有两种:一种是控件的开发,一种是页面的开发,其实页面也可以被看成控件。

以我的工程为例,我的控件全放在com.ht.components下
以我的ToolBar为例,
我的ToolBar控件放在com.ht.components.toolbar下
下面有
IToolItemDescirption.java
IToolItemListener.java
SimpleToolItemDescription.java
ToolBar_en_US.properties
ToolBar_zh_CN.properties
ToolBar.html
ToolBar.jwc
ToolItem.html
ToolItem.java
ToolItem.jwc

因为我是这样设计的,一个ToolBar包括一些ToolItem项,每个ToolItem的数据由一个
IToolItemDescription来提供.

一个控件XXX的通常组成为
一个XXX.java文件
一个XXX.jwc文件
一个XXX.html文件(当然html也可以不要,一个控件的render完全交给XXX.java去实现也是可行的)
可选的为
XXX._en_US_properties(对应的美国英语字符资源文件)
XXX._zh_CN_properties(对应的中国大陆字符资源文件)
XXX.xxx_properties(你还可以添加其他的local字符资源文件,只要xxx是合法的locale)

我的例程实现:

package com.ht.components.toolbar;

/**
* <p>Title: </p>
* <p>Description: </p>
* <p>Copyright: Copyright (c) 2003</p>
* <p>Company: </p>
* @author Hery Tang
* @version 1.0
*/
import org.apache.tapestry.IAsset;

public interface IToolItemDescription {
   public IToolItemListener getToolItemListener();
   public IAsset getAsset();
   public IAsset getDisabledAsset();
   public String getLabel();
   public String getActionType();
   public boolean isSeparator();
   public boolean isEnable();
}

package com.ht.components.toolbar;

/**
* <p>Title: </p>
* <p>Description: </p>
* <p>Copyright: Copyright (c) 2003</p>
* <p>Company: </p>
* @author Hery Tang
* @version 1.0
*/
import org.apache.tapestry.IPage;

public interface IToolItemListener {
  public void actionPerformed();
}

package com.ht.components.toolbar;

/**
* <p>Title: </p>
* <p>Description: </p>
* <p>Copyright: Copyright (c) 2003</p>
* <p>Company: </p>
* @author Hery Tang
* @version 1.0
*/
import org.apache.tapestry.IAsset;

public class SimpleToolItemDescription implements IToolItemDescription{
  private IToolItemListener toolItemListener;
  private IAsset enabledAsset;
  private IAsset disabledAsset;
  private String label;
  private String actionType;
  private boolean isSeparator;
  private boolean isEnable;
  public SimpleToolItemDescription() {
    isEnable = true;
    isSeparator = false;
  }

  public IToolItemListener getToolItemListener() {
    return toolItemListener;
  }
  public void setToolItemListener(IToolItemListener listener) {
    toolItemListener = listener;
  }

  public IAsset getAsset() {
    return enabledAsset;
  }

  public void setAsset(IAsset asset) {
    this.enabledAsset = asset;
  }
  public IAsset getDisabledAsset() {
    return disabledAsset;
  }
  public void setDisabledAsset(IAsset asset) {
    this.disabledAsset = asset;
  }

  public String getLabel() {
    return label;
  }

  public void setLabel(String label) {
    this.label = label;
  }

  public void setActionType(String actionType) {
    this.actionType = actionType;
  }
  public String getActionType() {
    return actionType;
  }
 
  public boolean isSeparator() {
    return isSeparator;
  }
 
  public void setIsSeparator(boolean value) {
    isSeparator = value;
  }
 
  public boolean isEnable() {
    return isEnable;
  }
 
  public void setIsEnable(boolean value) {
    isEnable = value;
  }

}

ToolBar_en_US.properties
ToolBar_zh_CN.properties
暂时为空,我没有需要国际化的字符资源

ToolBar.html

<span jwcid="$content$">
<table width="100%" border="0" cellpadding="0" cellspacing="1">
        <tr>
                <span jwcid="@Conditional" condition="ognl:!isAlignLeft">
                        <td align="right">
                <table border="0" cellpadding="0" cellspacing="0">
                                        <tr>
                                                <span jwcid="@Foreach" source="ognl:toolItemDescriptions" value="ognl:currentToolItemDesc">
                                                                  <td>
                                                                          <span jwcid="@ToolItem" description="ognl:currentToolItemDesc"/>
                                                                  </td>
                                                                  <td width="10">
                                                                           
                                                                  </td>

                                                    </span>
                                            </tr>
                                    </table>
                            </td>
    </span>
    <span jwcid="@Conditional" condition="ognl:isAlignLeft">
        <td align="left">
                                <table border="0" cellpadding="0" cellspacing="0">
                                        <tr>
                                                <span jwcid="@Foreach" source="ognl:toolItemDescriptions" value="ognl:currentToolItemDesc">
                                                                <td>
                                                                        <span jwcid="@ToolItem" description="ognl:currentToolItemDesc"/>
                                                                </td>
                                                                <td width="10">
                                                                         
                                                                </td>
                                                       </span>
                                              </tr>
                                    </table>
            </td>
    </span>
            </tr>
</table>
</span>

ToolBar.java
package com.ht.components.toolbar;

import org.apache.tapestry.BaseComponent;
import org.apache.tapestry.IRequestCycle;
import java.util.List;

/**
* Simple ArrayViewer object, for the Component chapter of the Tutorial
* @author neil clayton
*/
public abstract class ToolBar extends BaseComponent {
        private IToolItemDescription currentToolItemDesc;
        private List toolItemDescriptions;

        public IToolItemDescription getCurrentToolItemDesc() {
          return currentToolItemDesc;
        }

        public void setCurrentToolItemDesc(IToolItemDescription itemDesc) {
          this.currentToolItemDesc = itemDesc;
        }

        public List getToolItemDescriptions() {
          return toolItemDescriptions;
        }

        public void setToolItemDescriptions(List descList) {
          toolItemDescriptions = descList;
        }

        /**
         * @see net.sf.tapestry.AbstractComponent#cleanupAfterRender(IRequestCycle)
         */
        protected void cleanupAfterRender(IRequestCycle cycle) {
                currentToolItemDesc = null;
                toolItemDescriptions = null;
                super.cleanupAfterRender(cycle);
        }

        public abstract boolean getIsAlignLeft();
}

ToolBar.jwc

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE component-specification PUBLIC
  "-//Apache Software Foundation//Tapestry Specification 3.0//EN"
  "
http://jakarta.apache.org/tapestry/dtd/Tapestry_3_0.dtd >


<component-specification om.ht.components.toolbar.ToolBar" allow-body="no" allow-informal-parameters="yes">

    <parameter
        name="toolItemDescriptions"
        type="java.util.List"
        direction="in"
        required="yes">
    </parameter>

    <parameter name="isAlignLeft"
type="boolean"
required="no"
direction="in"/>

</component-specification>

ToolItem.html

<span jwcid="$content$">
<span jwcid="@Conditional" condition="ognl:!isSeparator">
<span jwcid="@Conditional" condition="ognl:IsEnable">
        <span jwcid="link"><img jwcid="enabledImage"/><font color="White"><span jwcid="@InsertText" value="ognl:label"/></font></span>
</span>
<span jwcid="@Conditional" condition="ognl:!IsEnable">
        <img jwcid="disabledImage"/><font color="#A0A0A0"><span jwcid="@InsertText" value="ognl:label"/></font>
</span>
</span>
<span jwcid="@Conditional" condition="ognl:isSeparator">
<img jwcid="@Image" image="ognl:enabledImage"/>
</span>
</span>

ToolItem .java

package com.ht.components.toolbar;

import org.apache.tapestry.BaseComponent;
import org.apache.tapestry.IBinding;
import org.apache.tapestry.IRequestCycle;
import org.apache.tapestry.components.Foreach;
import org.apache.tapestry.IAsset;

public class ToolItem extends BaseComponent{
  private IToolItemDescription desc;

  /**
   * @see net.sf.tapestry.AbstractComponent#cleanupAfterRender(IRequestCycle)
   */
  protected void cleanupAfterRender(IRequestCycle cycle) {
    desc = null;
    super.cleanupAfterRender(cycle);
  }

  public IToolItemDescription getDescription() {
    return desc;
  }

  public void setDescription(IToolItemDescription desc) {
    this.desc = desc;
  }
  public void excuteCurrent(IRequestCycle cycle) {
    if(desc != null) {
      IToolItemListener listener = desc.getToolItemListener();
      listener.actionPerformed();
    }
  }
  public IAsset getEnabledAsset() {
    return desc.getAsset();
  }
  public IAsset getDisabledAsset() {
    return desc.getDisabledAsset();
  }
  public String getLabel() {
    return desc.getLabel();
  }
  public boolean getIsSeparator() {
    return desc.isSeparator();
  }
  public boolean getIsEnable() {
    return desc.isEnable();
  }
}

ToolItem.jwc

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE component-specification PUBLIC
  "-//Apache Software Foundation//Tapestry Specification 3.0//EN"
  "
http://jakarta.apache.org/tapestry/dtd/Tapestry_3_0.dtd >


<component-specification om.ht.components.toolbar.ToolItem" allow-body="no" allow-informal-parameters="yes">

    <parameter
        name="description"
        type="com.ht.components.toolbar.IToolItemDescription"
        direction="in"
        required="yes">
    </parameter>

    <component id="link" type="LinkSubmit">
            <binding name="listener" expression="listeners.excuteCurrent"/>
    </component>

    <component id="enabledImage" type="Image">
            <binding name="image" expression="enabledAsset"/>
    </component>

<component id="disabledImage" type="Image">
            <binding name="image" expression="disabledAsset"/>
    </component>
</component-specification>

我就拿ToolBar这个控件讲:
先看ToolBar.jwc

<component-specification om.ht.components.toolbar.ToolBar" allow-body="no" allow-informal-parameters="yes">

这句是去说控件ToolBar对应的class为com.ht.components.toolbar.ToolBar
allow-body="no'是说这个控件不允许有body, allow-informal-parameters="yes"是说这个控件允许带非正式参数。

控件的参数有两种一种是在jwc文件里规定的,一种是没有规定,为什么会有非正式参数呢?大家知道Tapestry的控件,最后render到页面最终还是什么
<table width="100%" border="0" cellpadding="0" cellspacing="1">
   <tr>
      <td xx"></td>
   </tr>
</table>
HTML的元素如table, tr, td,span等等都是可以带很多参数,如果我们的控件输出一般会
对应于一个元素,这时非正式参数就会以元素的参数输出。

<parameter
        name="toolItemDescriptions"
        type="java.util.List"
        direction="in"
        required="yes">
 , ;, ;   </parameter>
这个说明ToolBar控件需要配制一个叫toolItemDescriptions的参数,参数类
谁顶了这篇日志>>  还有谁对该日志投票? 最近读者>>
0
好文,顶
0
烂文,踩
 
网友评论>>
发表评论:  
 
内 容:
插入表情 文采分大于5可以使用表情
   *Ctrl+Enter快速回复

针对ZOL博客您有任何使用问题和建议 您可以 联系博客管理员查看帮助
ZOL简介 | 用户注册 | 广告服务 | 人员招聘(月) | ZOL历程 | 互动营销中心 | 站点地图 | 联系方式 | 欢迎投稿 | RSS订阅 | 友情链接
Copyright ©1999 - 2008 ZOL. All rights reserved. 中关村在线 版权所有.