continue reading hover preload topbar hover preload widget hover preload

建了两个wiki服务

Categories: wiki应用  |   No Comments

使用了一段时间JSPWiki,越来越喜欢这种简洁的文章编写方式,通过纯文本的格式可以写出有规范格式的文章确实是个便捷的事情。虽然支持的版式简单的点,要是所有的wiki engine能使用通用的编写规则并且能购支持Docbook那样通用的出版格式就更好了。

考虑到使用的方便性,很希望在internet建立一个wiki这样可以方便文字的备份和保存,也可随时随地的添加内容。在比较了几个Java wiki engine后(自己会Java所以偏向使用Java的wiki engine,挺喜欢wikimedia,可惜啦),最后选择安装了JSPWiki和xwiki进行进一步的测试。

JSPWiki的功能相对简单,但是易于安装使用,对中文支持不错,可惜CamelCase功能不能在全中文环境(wikiname直接使用中文)使用。

相比之下,xwiki需要mysql支持,功能更为强大,权限管理功能很完善,编辑时支持velocity语法所以可扩展能力非常强,当然使用也麻烦一些。中文支持不太好,最可惜的是不支持直接使用中文wikiname。

欢迎大家到这里进行试用:

JSPWiki: http://linhaiguanchao.s43.eatj.com/JSPWiki/

xwiki: http://linhaiguanchao.s43.eatj.com/xwiki/bin/view/Main/WebHome

申请的免费空间,经常因为资源进展停止免费空间造成无法访问,试用需要点运气+耐性,不好意思!:=)

建议对中文环境要求高的使用 MediaWiki。

使用JSPWiki建立自己的Wiki服务

Categories: wiki应用  |   Comments(6)
使用JSPWiki建立自己的Wiki服务
建立自己的JSPWiki应用——本文讲述如何使用JSPWiki建立一个自己的wiki应用
内容提要
JSPWiki是一个基于文本文件的简易wiki系统包括了身份认证和版本控制功能。完全采用JSP/Sevelet开发,采用UTF-8,能很好的支持中文,可以直接使用中文名作为页面(page)名。是一个简单易用的Wiki引擎。本文将向您展示如何建立一个自己的JSPWiki 应用。包括改变模板,安装插件以及为你的wiki加入身份认证功能。 您可以到http://www.jspwiki.org下载程序包和其它插件。
目录
*       [1] 在Tomcat下建立JSPWiki应用
*       [2] 更换模板
*       [3] 安装插件
*       [4] 使用身份认证功能
Tomcat下建立JSPWiki应用 [#1]
1.      下载JSPWiki(我使用的是2.2.28),解压缩,把jspwiki.war直接Copy到Tomcat下面webapps目录下
2.      启动tomcat,JSPWiki.war会自动解压缩到webapps目录下
3.      到JSPWiki/webinf目录下面修改jspwiki.properties文件,作如下修改

  jspwiki.fileSystemProvider.pageDir = C:\\temp\\jspwiki
  jspwiki.basicAttachmentProvider.storageDir = C:\\temp\\jspwiki
  log4j.appender.FileLog.File = C:\\temp\\jspwiki.log
4.      可以运行http://127.0.0.1:8080/JSPWiki/Install.jsp帮助完成上述配置 ,为了能够在wiki中使用HTML需要修改 jspwiki.properties,把allowHTML 选项打开

  jspwiki.translatorReader.allowHTML = true
更换模板 [#2]
1.      下载模板redman,并且解包到tomcat_home\webapps\JSPWiki目录下的template的目录下,目录名称应该就是redman
2.      修改jspwiki.propertiest文件,把

  jspwiki.templateDir = default
改成

   jspwiki.templateDir = redman
安装插件 [#3]
安装java2html插件
1.      下载java2html插件
2.      把下载下来的java2html.jar拷贝到 tomcat_home\webapps\JSPWiki\WEB-INF\lib目录下
3.      修改 jspwiki.propertiest文件

jspwiki.plugin.searchPath = de.java2html.plugin.jspwiki
安装PDF插件
1.      首先需要下载其它组件。下载Apache FOP 并解包把avalon-framework.jar (或者其他版本,比如:avalon-framework-cvs-20020806.jar)、batik.jar、fop.jar拷贝到WEB-INF/lib下;下载 jTidy 并解包,拷贝Tidy.jar to WEB-INF/lib目录下;下载 JIMI 并解包,把JimiProClasses.zip拷贝到WEB-INF/lib/目录并改名为JimiProClasses.jar.
2.      在jspwiki 下载pdf插件
3.      把下载下来的wikipdf.jar拷贝到 tomcat_home\webapps\JSPWiki\WEB-INF\lib目录下
4.      修改 tomcat_home\webapps\JSPWiki目录下Web.xml文件,增加下面的内容

 <servlet>
     <servlet-name>Wiki2PDFServlet</servlet-name>
     <servlet-class>se.eminds.jspwiki.Wiki2PDFServlet</servlet-class>
 </servlet>
 
 <servlet-mapping>
     <servlet-name>Wiki2PDFServlet</servlet-name>
     <url-pattern>/wiki.pdf</url-pattern>
 </servlet-mapping>
5.      修改templates/default/ViewTemplate.jsp文件,加入下面的内容

 <a href="wiki.pdf?page=<wiki:Variable var="pagename" />&ext=.pdf">View PDF</a> 
注意: 修改jspwiki.propertiest文件时,"="后面的变量值后面不能有多余的空格或者tab,否则JSPWiki无法正常读取配置文件导致改动没有效果。
使用身份认证功能 [#4]
1.      修改jspwiki.properties文件,加入下面的内容:

jspwiki.authenticator = FileAuthenticator

  jspwiki.fileAuthenticator.fileName = /tmp/passwords.txt

  jspwiki.auth.useOldAuth=true

# Add the following line authorize all users

  jspwiki.policy.strictLogins = true
2.      编辑对应的password.txt文件加入合法用户,例如:


# The format is simply username = password
  # No encryption is used currently.
  # Comments are allowed; prepend with hash.

  ebu = foobar
  ubi = frobozz
3.      设置全部页面的默认访问权限,增加一个DefaultPermissions页面,包含下面的内容
[{SET defaultpermissions='ALLOW view Guest;DENY edit Guest;ALLOW edit KnownPerson'}]
4.      管理员用户组。可以在jspwiki.propertiest文件中指定管理员用户组的名称,如:

jspwiki.auth.administrator = WikiAdmin
5.      那么默认的管理员用户组被命名为WikiAdmin,然后可以创建WikiAdmin页面加入成员,下面指令可以在WikiAdmin中加入JackJones,JillJones两个用户
[{SET members='JackJones, JillJones'}]
6.      如果需要设置单独页面的访问权限,可在页面内容前面加上访问规则,例如:
[{ALLOW view Guest}] [{DENY edit Guest}] [{ALLOW edit ebu, ubi}]
7.      如果需要增加一个用户组Xyz,增加一个Xyz的页面,加入下面的指令
[{SET members='Foo, Bar'}] }]
注意: JSPWiki有几个默认的用户组。
   任何一个访问wiki的用户都属于Guest用户组;
   任何一个使用user prefenrences设置了用户名的用户都属于NamedGuest用户组;
   所有通过了身份认证的用户属于KnownPerson用户组。
注意: 目前的身份认证处理方式是临时的。
   目前的身份认证方式是临时的,到2.4以上的版本会改变。目前权限规则实现有问题, 必须使用管理员用户组。因为加上了禁止guest访问
的权限管理规则后,除了管理员外所有用户都无法编辑wiki。 如果希望一个用户有wiki的编辑权限,别忘了把他加到管理员用户组。

一些敏捷软件开发的资源

Categories: 敏捷软件开发  |   Comments(5)

         一些介绍敏捷软件开发的相关网站:

重新整理了AppFuse指南中的webwork部分

Categories: 未分类  |   Comments(4)

       这两天整理了AppFuse指南的webwor部分,到目前发布的翻译文章如下:

        加上原来RockSun的翻译

        指南的基本内容都有了,本来英文的内容就很简单,对比一下已有的翻译其他FrameWork的指南也应该很容易了吧!基本翻译就到此为止了,需要的话在翻译其他的部分。

发布appfuse翻译

Categories: 人在旅途  |   Comments(6)

       忙了好长一段时间,终于有点空了,打算这段时间把自己开发的经验总结继续发布出来。无意中抽空在google上搜了一下appfuse,发现自己的文章居然被不少地方转载了。总觉得虽然自己发布的内容在原来Matt Raible的指南翻译的基础上加了些实际开发过程中遇到的问题及解决的过程,但是绝大部分是原文的简单翻译,虽然翻译也有自己的工作在里面,总要得到原作者的同意才好发布的吧!于是联系了一下Matt Raible说明了一下情况,Matt同意我在自己的Blog上保留自己的内容,同时也邀请我把自己翻译的内容一起发布在他的wiki上面。这时候发现也有同胞在作同样的事情了!呵呵!正可以好把我自己关于webwork的部分补充上去,这样放在一块,也方便国人阅读!^_^

      上午拿到Matt给的帐号,上去看了一下,未经允许就自作主张把内容和菜单重新组织了一下,为了发布方便,顺便把AppFuseQuickStart和Article两篇翻译了一下,比较仓促,希望大家看了多提意见。可惜中文的文章发布以后再编辑都成了Unicode。唉….中文总是个问题为了方便以后修改,保存了一份中文原稿!需要修改的话可以跟我索取!:-)

Appfuse开发指南

Categories: Appfuse  |   Comments(6)

Appfuse开发指南

这里是我写的一些文章内容提炼和链接,以方便大家能够找到一些有用的资料:

Appfuse开发指南

如果你已经下载了AppFuse并且希望在你的机器上完成安装配置,最合适的参考资料是快速起步指南。一旦你完成了所有的安装设置,下面的手册是学习如何使用AppFuse开发的重要资料。

注意: Appfuse的发布包里面已经包括了这个指南。如果你希望更新你的工程目录下的手册( docs 目录下),运行 "ant wiki"

1.6.1 版本开始,你可以产生这个指南所包含的源代码。如果你使用 Struts+Hibernate,你可以产生所有的源代码,如果使用Spring 或者 WebWork,因为写专门的安装程序比较麻烦所以你需要手工配置 Controllers Actions。造成这个局面的主要原因是我没有为这些 web framework 使用 XDoclet 从而使得基于ant的安装程序功能受到了限制。 AppGen 工具可以产生第一部分所需要的全部源代码。

还有另外一个AppFuse Generator
项目建立的工具有类似AppGen的功能。

第一部分: AppFuse建立DAOPOJO - 讲述如何创建POJO(对应相应的数据表) DAO(管理POJO持久化操作的对象)。

第二部分: 创建新的Manager讲述如何创建一个 Business Facades ,它可以跟数据层 (DAOs)web (Actions Controllers)交互。

第三部分: (Struts) 创建 Struts ActionsJSPs讲述如何创建在你自己的appfuse工程里面创建ActionsJSPs。包括生成JSP并且进行修改定制让它们好看一点。此外,你需要编写WebTest来测试这个JSP的功能。其他可选的web framework如下所示:

*      Spring: 创建 Spring Controllers JSPs

*      WebWork: 创建 WebWork Actions JSPs

*      JSF: 创建 JSF Beans JSPs

*      Tapestry: 创建 Tapestry 页面和模版

第四部分: (Struts) 增加校验功能和列表页面增加一个验证personFormfirstNamelastName为必填项的校验逻辑,并且增加一个列表面显示数据库中所有的person记录。

*      Spring: 增加校验功能和列表页面

*      WebWork: 增加校验功能和列表页面

*      JSF: 增加校验功能和列表页面

*      Tapestry: 增加校验功能和列表页面

注意: 你可以用AppGen生成这个指南里创建的所有源文件。在经验不足的情况下你可以把自己编写的程序和AppGen产生的程序作比较以检查问题。如果你想这样做的话,进入extras/appgen目录并运行"ant test-detailed",这将创建一个"appfuse-appgen"工程,产生这个指南的所有代码并且进行测试

Thomas Gaudin的非常好的AppFuse开发指南

Thomas Gaudin在他的网站上放了两个详细的很容易学习的开发指南

*      处理日期 (使用AppFuse Struts

*      创建一个可持久化的动态 web tree

相关的 AppFuse 指南

*      我如何建立自己的开发环境.

*      使用Eclipse开发你自己的AppFuse应用程序.

*       MyEclipse 中使用Appfuse: 一部分 第二部分 .

*       AppFuse 也支持 IDEA 4.0 out-of-the-box, or at least the project files are included.

*      使用Anthill 或者 CruiseControl 完成 AppFuse 自动测试。

*      如何使用Hibernate创建对象间的关系.

*      如何Resin上运行AppFuse.

*      如何Orion上运行AppFuse.

*      如何Oracle上运行AppFuse.

*      如何DB2上运行AppFuse.

*      如何PostgreSQL上运行AppFuse.

*      如何使用Velocity Template代替JSP.

*      Eclipse使用HibernateXDoclet Template.

*      如何Appfuse中增加一个独立类库.

*      如何Appfuse中增加一个servlet.

*      如何自动从DAO层产生PDF文档.

*      如何自动产生测试用随机数据.

*      Apache 2.xSSL

*      Apache 2.xTomcat 4.x

*       Apache/Tomcat/SSL in Real-Time

*      Apache 1.3.xTomcat 4.x

*      启动ApacheTomcat



*      Jabber Server Setup (1.4.2)

*      Securing Directories in IIS

*      Tips for configuring Tomcat

*       AppFuse开发者技巧

还有些用处的旧文档:

*       Struts Example for iPlanet

*       Wiki Evaluation (Java-based)

 

AppFuse快速起步指南

Categories: Appfuse  |   Comments(3)

AppFuse快速起步指南

AppFuse项目的主要目的是帮助你加速web应用程序的开发。下面说明如何利用它创建一个新工程的基本步骤。

1.      安装 J2SE 1.4.2+ 并设置环境变量JAVA_HOME指向J2SE所在的目录。

2.      下载
源程序报或者从CVS(cvs -d :pserver:guest@cvs.dev.java.net:/cvs co appfuse)中检出appfuse模块。

注意: 你可以使用the all-in-one installer 一次性下载安装AppFuse 1.8, Ant 1.6.2, Tomcat 5.0.28 MySQL 4.1.11 (for Windows)。查看相关的 README.txt 文件可以了解更多信息。

3.      安装 Install Ant 1.6.2+ 并设置 ANT_HOME 环境变量。 安装 Tomcat 4.1.x+ (推荐使用Tomcat 5.0.28) 并设置 CATALINA_HOME 环境变量指向对应的Tomcat安装目录。 参阅我的文章 设置开发环境 可以了解在哪里下载到这些程序并且可以了解我通常如何安装配置这些程序。

4.      安装 MySQL 3.23.x+ (推荐使用 4.1.7).

注意: 如果你使用 MySQL 4.1.7, 确定正确设置了 UTF-8 字符集和 InnoDB 表类型。 这里说明如何设置

5.      设置本机的 SMTP server 或者修改 mail.properties 文件( web/WEB-INF/classes 目录下) build.properties 文件(在根目录下 log4j 消息使用) 以使用一个已有的smtp server – 默认值是 localhost

6.      把文件 lib/junit3.8.1/junit.jar 拷贝到 $ANT_HOME/lib目录下。

注意: 你可能会发现在 $ANT_HOME/lib目录已经有一个 ant-junit.jar 文件了. 这个 jar 包不是 JUnit 类库, 而是 Ant junit task 实现,在执行Ant junit task时会使用你新增的这个junit.jar 文件。

7.      如果你计划使用 iBATIS (代替 Hibernate) 或者 struts 以外的 web framework, 现在安装它们 (安装指令下面可以查到)。安装完成以后, appfuse目录下运行 ant new 。会提示你输入application name, database name package name。完成这些操作以后会在appfuse同级目录家里一个包含新程序所需要的全部文件的以你输入的application name命名的目录。

警告: 请不要在输入的application name, database name package name使用 "test", 包含 "appfuse" 的名称 或者 以数字作为起始字符的名称,否则将会导致任务无法正常执行,此外使用两个破折号 (-) 会造成结果混乱。

8.      切换到这个新的工程目录并且运行 ant setup (或者运行 ant setup-db setup-tomcat deploy) 创建数据库,配置tomcat并发布你的新应用程序。创建数据库的任务只有在你的root用户密码为空的情况下才能工作。需要的话你可以修改 build.properties 文件if necessary. 需要 安装设置帮助setup?

9.      若你细想要进行测试以确定所有程序工作良好,运行 ant test-all在运行这个任务的时候确定 Tomcat 没有启动。 接下来, 运行 ant test-reports运行结束后会显示消息告诉你如何查看产生的报告。

在你确认你正确的完成了上述步骤以后浏览一下 指南 了解如何使用appfuse开发程序。

安装的可选项

*       如果你希望使用 iBATIS 作为持久化framework, 查看extras/ibatis目录下的 README.txt 文件或者运行 ant install-ibatis

*       如果你希望使用 Spring 作为web framework, 查看 extras/spring 目录下的 README.txt 文件或者运行 ant install-springmvc

*       如果你希望使用 WebWork 作为web framework, 查看extras/webwork 目录下的 README.txt 文件或者运行 ant install-webwork

*       如果你希望使用 JSF 作为web framework, 查看 extras/jsf 目录下的 README.txt 文件或者运行 ant install-jsf

*       如果你希望使用 Tapestry 作为web framework, 查看 extras/tapestry 目录下的 README.txt 文件或者运行 ant install-tapestry

*       如果你想写脚本自动基于Appfuse创建并测试自己的工程。在CVS上面有两个我已经写好的用来测试的脚本供参考: spring+ibatis webwork 注意在实际开发过程中最好不要使用 "appfuse" 作为工程名 因为可能会造成不必要的查找/替换。

*       如果你不希望安装 iBATIS, Spring MVC, WebWork, JSF Tapestry – 你应该在把它们检入前删除extras下的相关目录。

注意: 安装程序会修改 Eclipse classpath, 但是不会是理想的方法。 你需要人工修改它。

 

appfuse快速开发应用程序指导手册

Categories: Appfuse  |   Comments(1)

appfuse快速开发应用程序指导手册

本文提供如果利用appfuse快速建立一个简单应用程序的简明操作手册。

*      [1]建立一个新的应用程序

*      [2]利用appgen建立简单的CURD程序

1.      选择需要的WebFramework
如果使用struts,这一步可省略
切换到新建的应用目录,跟appfuse同一级别运行。
ant install-webwork

2.       建立自己的应用
运行ant任务创建新的应用。
ant new –Dapp.name=myApp –Ddb.name=myDb

      3.      修改 user.java E-maildoc @hibernate.column 部分,增加 长度限制 length=“166”
        原因参考appfuse实践一

4.      启动 mysql (字符集采用urf8)

5.      安装发布应用,建立数据库
运行 ant setup

6.      打开浏览器测试
打开浏览器输入[http://127.0.0.1:8080/myApp进行测试,即可正常登陆使用。

利用appgen添加简单的CURD功能[#2]

1.      备份
最好结合版本控制工具增加ant任务自动备份,为了能够在利用appgen产生代码后恢复程序。

2.      创建POJO
src\dao\org\appfuse\model目录下创建新的实体对象,需要继承BaseObject对象。

3.      添加需要的属性
在对象中添加属性,利用eclipse生成对应的get/set方法。并添加合适的hibernate标签。

4.      实现BaseObject接口方法
利用commoneclipse添加tostring(),hashcode(),compareto()方法。

5.      修改Hibernate配置文件中
修改applicationContext-hibernate.xml文件加入新的POJO对象的hibernate配置文件。

6.      使用appgen生成这个新的POJOCURD程序
切换到extra\appgen目录下运行
ant install-detailed -Dmodel.name=<model.name> -Dmodel.name.lowercase=<model.lowercaseName>
这样可以自动生成CURD部分的源代码并且自动发布到相应的源程序目录中去。 注意:model.lowercaseName只有小写只需要第一个字符,其他跟model.name相同

7.      增加中文描述是界面能正确显示中文信息
切换到应用程序目录app下,修改ApplicationResources_zh_CN.properties文件增加对应的中文描述。

8.      发布新的程序
运行ant undeploy卸载原来的应用;
运行 ant setup-db创建表填充测试数据;
运行 ant deploy发布新的应用。

9.      打开浏览器测试
打开浏览器输入http://127.0.0.1:8080/myApp 进行测试,登陆后可以看到新的菜单项,可以进行测试各个功能是否正常。

10.  如不成功,恢复到前面备份的版本,重复上述步骤

 

 

 

 

 

公司新年晚宴上的即席讲话

Categories: 人在旅途  |   -

公司新年晚宴上的即席讲话

 

 

 

(上周在公司年终聚餐对先进项目组表彰上的讲话,整理稿)

 

 

 

今年是我们公司,也是我们部门面临很大危机的一年,可以算是内外交困的一年,由于种种原因,公司面临了客户方前所未有的信任危机,部门内部人员流动频繁。还记得我刚到公司时,与XXX去拜访我们的客户时,总会面临客户方项目相关人员大量的抱怨、指责和批评。在这种困难的情况下,我们高兴的看到通过大家一年的艰苦努力(注:准确地说是半年),年底客户评价有了根本的改变,重新赢得了客户的信任。许多客户的后续项目正在洽谈中。这在半年前是我根本都无法想象的事情。

 

 

 

这里要特别表扬我们部门的XX项目组,关于这个项目,我只有一个感觉,两个字——“放心”,非常放心,整个项目过程中我基本上就没有向XXX(项目经理)过问过项目的任何问题,不仅仅我有这种感觉,公司X总也有同样的感觉,项目在XXX领导下不仅仅做到了公司内部领导放心,更难得的事做到了客户方放心。目前XXX在后续要开发的系统中,都优先跟我公司洽谈,这和XXX整个项目组的努力工作是分不开的,虽然目前XXX的项目还没有上线试运行,但是我们有理由相信,有一个好的过程作保证,一定有一个好的结果在等待。

 

 

 

另外一个突出的是XXX项目组,提起这个项目组,我只有一个感觉,叫“临危受命”,真的算得上市受命于危难之际,在公司面临空前的危机时,XXX(项目经理)没有丝毫的犹豫和推辞,接受了这个艰巨的任务。由于历史的原因,整个新成立项目组不得不承受客户的责难。在这种情况下,整个项目组一方面默默无闻的接受指责,另一方面回公司加班加点工作。“人做一次好事很容易,难的是一辈子做好事”,整个项目组连续加班7个月,不管放在那个公司,那个场合,都是一件难能可贵的事情。

 

 

 

还有值得一提的是XXX项目,半年前,我都难以想象这个项目会在今天这个场合提出来表扬,整个项目是两位新近公司的同事接受,在没有接受公司任何的业务和技术培训的情况下,XXXXXX加入公司一周后就到客户方开发,两周后客户方项目负责人给我打了个电话,我当时不在公司,很紧张,因为这个项目客户每次打电话给我都是有问题,但这一次他说了一句话“你们新来的两个小伙子很好,希望能让他们在我们这边开发”。短短两个礼拜态度有了如此的改变,说明了XXXXXX的技术能力和项目协调能力。

 

 

 

还有XXXXXX,都是没有相关工作经验的新手,但是我们欣喜地看到,他们上手很快,目前已经在项目中独当一面,可以独立负责部门模块的分析开发工作。

我深深地为部门能够拥有这样优秀的员工感到骄傲!!!

 

 

 

新的一年里,希望软件部全体员工继续发扬敢挑重担,敢于承担责任的精神,群策群力,在去年奠定的良好基础上,取得更大进步。

我希望通过大家的努力,我们的工作成效最后能够不仅仅体现在客户的口头表扬上,更能体现在公司的财务报表上,能够体现在每一位员工的实际收入上!!!

 

 

 

最后,我利用这个机会,对软件部全体人员的过去一年的辛苦付出表示感谢!

代表软件部对X总对部门工作的指导表示感谢!

对人事、后勤、行政部门对我们工作的支持表示感谢!

XX部和XXX部对我们工作的帮助表示感谢!

希望我们公司全体员工在新的一年里,销售人员多开单,多提成。开发人员多做项目,多拿奖金。总之一句话,大家好才是真的好!!

 

 

 

谢谢大家!

appfuse使用Taperstry框架(一)——创建Tapestry框架页面

Categories: Appfuse  |   Comments(2)


创建Tapestry框架页面

第三部分: 创建 Tapestry page 和 HTML 模版 – 介绍如何在AppFuse 项目中创建 Tapestry页面和模版。

这个指南依赖于 第二部分: 创建新的 Managers 对象

说明

这个指南将会告诉你如何创建 Tapestry 页面和 HTML 模版。同时也将说明如何编写 JUnit 测试以测试PersonForm页面。我们创建的这个JSP页面将会使用我们在”创建Managers类“ 指南创建的PersonManager类。在大多数的web框架(web frameworks)中,控制逻辑都写在一个类似”Action” 的类中。但是在Tapestry中,这些控制逻辑通常可以以类似”Page”的方式被引用。使用这些pages的方法被称为listeners。这份指南不会讲述关于Tapestry工作机制的问题,但是会知道你快速上手使用Taperstry框架。如果你希望跟深入的学习Taperstry的有关知识,我建议你阅读Howard Lewis Ship’的 Tapestry in Action一书。当我在把Tapersty集成到Appfuse的过程中就把这部书放在身边以便随时查阅。感谢Howard的帮助!

我将以斜体字说明在 实际过程 使用的经验。

现在让我们开始在Appfuse’s的整体架构下创建新的页面和HTML模版。如果你此时还没有安装Tapestry模块,请马上运行ant install-tapestry

内容提要

  • [1] 使用XDoclet创建 pageForm.html
  • [2] 创建 PersonFormTest 以测试 PersonForm
  • [3] 创建 PersonForm
  • [4] 运行 PersonFormTest
  • [5] 在你的浏览器中参看刚创建的PersonForm
  • [6] 创建 Canoo WebTests 以模拟测试 PesonForm 对浏览器中操作的响应

使用XDoclet创建 pageForm.html 模版[#1]

在这一步,你将自动生成一个HTML模版以显示Person对象的信息。这个模版将包含符合Taperstry语法规则的待填充表单元素 – 就是那些HTML文件中带有”jwcid” 的属性。用来自动生成HTML模版的AppGen工具是基于 StrutsGen工具实现的 – 这个工具最初是由Erik Hatcher开发的。基本上是由一对Java类和一组XDoclet模版组成。这些文件都可以在extras/appgen目录下找到。

下面给出产生这个HTML模版文件和一个包含了form的标签元素的properties文件的具体步骤:

  • 在命令行环境下,切换到 “extras/appgen” 目录
  • 执行 ant -Dmodel.name=Person -Dmodel.name.lowercase=person 将在extras/appgen/build/gen目录下产生这组文件。事实上,它将产生你在完成这个向导所用到的所有文件。不过,我们现在仅仅抓住那些你需要的那些文件。
    • web/WEB-INF/classes/Person.properties (表单元素的标签[label]字符串)
    • web/pages/PersonForm.html (显示一个Person对象信息的HTML 模版文件)
    • web/pages/PersonForm.page (说明前一页面的Page)
    • web/pages/PersonList.html (显示一个People列表的HTML 模版文件)
    • web/pages/PersonList.page (说明前一页面的Page)
  • 把Person.properties文件中的内容拷贝到web/WEB-INF/classes/ApplicationResources_en.properties文件中。这些都是你在JSP页面中用来显示的titles/headings 和form属性对应的具体键-值对。下面是你需要在ApplicationResources_en.properties文件中新增的内容示例:
# -- person form --
personForm.id=Id
personForm.firstName=First Name
personForm.lastName=Last Name

person.added=Person has been added successfully.
person.updated=Person has been updated successfully.
person.deleted=Person has been deleted successfully.

# -- person list page --
personList.title=Person List
personList.heading=Persons

# -- person detail page --
personDetail.title=Person Detail
personDetail.heading=Person Information
  • 拷贝PersonForm.html 和 PersonForm.page 文件 到 web/pages/personForm.jsp 和 web/pages/personForm.page文件。拷贝PersonList.html和PersonList.page文件到web/pages/personList.jsp和web/pages/personList.page。请注意目标文件名的第一个字符是小写。

在 “pages” 目录下的文件将被罚不到”WEB-INF/pages”目录下。因为容器会为WEB-INF目录下的文件提供安全性保护。这意味着直接来自客户端的请求,而不是由 Tapestry’s ApplicationServlet 发送的 forward请求,将无法访问对应的 JSP 页面。就是说把所有的 HTML 模版放在 WEB-INF 目录下将保证所有的 JSP 页面只能通过Taperstry的 Pages 来访问。这就允许把所有的安全性处理集中放到 Taperstry Page 中去,在那里可以得到更有效的处理,而不在需要在表示层中处理。

Appfsue的web应用程序安全性保证所有 *.html url-patterns 都是有保护的 (除了 /signup.html 和 /passwordHint.html), 这将保证客户端必须通过Page(Tperstry 框架页面条专逻辑控制器对应的 Page )来访问 template 文件。

注意: 如果你希望为特别的页面自定义一个CSS,你可以在这个文件的最上方加入 <body id=”pageName”/> 标签 (紧跟在</content> 标签后面)。 SiteMesh会特别处理并把它放在最终的页面中。你也可用使用如下的代码一个页面一个页面的自定义CSS:
body#pageName element.class { background-color: blue } 

创建PersonFormTest以测试[#2]

要为PersonForm创建一个 Junit 测试,首先在 test/web/**/action 目录下创建一个 PersonFormTest.java 文件。


package org.appfuse.webapp.action;

import java.util.ResourceBundle;

import org.appfuse.model.Person;
import org.appfuse.service.Manager;

public class PersonFormTest extends BasePageTestCase {
    private PersonForm page;
    private Manager manager;

    protected void setUp() throws Exception {    
        super.setUp();
        page = (PersonFormgetPage(PersonForm.class);
        // unfortunately this is a required step if you're calling 
        // getMessage in the page class
        page.setBundle(ResourceBundle.getBundle(MESSAGES));
        page.setValidationDelegate(new Validator());

        // this manager can be mocked if you want a more "pure" unit test
        manager = (Managerctx.getBean("manager");
        page.setManager(manager);
        // default request cycle
        page.setRequestCycle(getCycle(request, response));
    }

    protected void tearDown() throws Exception {
        super.tearDown();
        page = null;
    }

    public void testAdd() throws Exception {
        Person person = new Person();
        // set required fields
        person.setFirstName("firstName");
        person.setLastName("lastName");
        page.setPerson(person);

        page.save(page.getRequestCycle());
        assertFalse(page.hasErrors());
    }

    public void testEdit() throws Exception {
        MockRequestCycle cycle = (MockRequestCyclepage.getRequestCycle();
        cycle.addServiceParameter(new Long(1));
        
        page.edit(cycle);

        assertNotNull(page.getPerson());
        assertFalse(page.hasErrors());
    }
    
    public void testSave() {
        assertNotNull(manager);
        Person person = (Personmanager.getObject(Person.class, new Long(1));

        // update fields
        person.setFirstName("firstName");
        person.setLastName("lastName");
        page.setPerson(person);

        page.save(page.getRequestCycle());
        assertFalse(page.hasErrors());
    }

    public void testRemove() throws Exception {
        Person person = new Person();
        person.setId(new Long(2));
        page.setPerson(person);

        page.delete(page.getRequestCycle());
        assertFalse(page.hasErrors());
    }
}

此时将不能通过编译因为你还没有创建被测试的 PersonForm 。

创建 PersonForm [#3]

在 src/web/**/action 目录下创建 PersonForm.java 文件,输入下面的内容:


package org.appfuse.webapp.action;

import org.apache.tapestry.IRequestCycle;
import org.apache.tapestry.event.PageEvent;
import org.apache.tapestry.event.PageRenderListener;

import org.appfuse.model.Person;
import org.appfuse.service.PersonManager;

public abstract class PersonForm extends BasePage implements PageRenderListener {
    public abstract PersonManager getPersonManager();
    public abstract void setPersonManager(PersonManager mgr);
    public abstract void setPerson(Person person);
    public abstract Person getPerson();

    public void pageBeginRender(PageEvent event) {
        if ((getPerson() == null&& !event.getRequestCycle().isRewinding()) {
            setPerson(new Person());
        else if (event.getRequestCycle().isRewinding()) { // add
            setPerson(new Person());
        }
    }

    public void cancel(IRequestCycle cycle) {
        if (log.isDebugEnabled()) {
            log.debug("Entering 'cancel' method");
        }

        cycle.activate("mainMenu");
    }

    public void delete(IRequestCycle cycle) {
        if (log.isDebugEnabled()) {
            log.debug("entered 'delete' method");
        }

        getPersonManager().removePerson(getPerson().getId().toString());

        MainMenu nextPage = (MainMenucycle.getPage("mainMenu");
        nextPage.setMessage(getMessage("person.deleted"));
        cycle.activate(nextPage);
    }

    public void edit(IRequestCycle cycle) {
        Object[] parameters = cycle.getServiceParameters();
        Long id = (Longparameters[0];
        
        if (log.isDebugEnabled()) {
            log.debug("getting person with id: " + id);
        }
        
        setPerson(getPersonManager().getPerson(id.toString()));
        cycle.activate(this);
    }
    
    public void save(IRequestCycle cycle) {
        if (getValidationDelegate().getHasErrors()) {
            return;
        }

        boolean isNew = (getPerson().getId() == null);

        getPersonManager().savePerson(getPerson());

        String key = (isNew"person.added" "person.updated";

        if (isNew) {
            MainMenu nextPage = (MainMenucycle.getPage("mainMenu");
            nextPage.setMessage(getMessage(key));
            cycle.activate(nextPage);
        else {
            PersonForm nextPage = (PersonFormcycle.getPage("personForm");
            nextPage.setMessage(getMessage(key));
            cycle.activate("personForm")// return to current page
        }
    }
}

你可能注意到在文件中使用了一组键(keys) – “person.deleted”,”person.added” 和 “person.updated”。所有的这些键值定义在你的 i18n 绑定文件(ApplicationResources_en.properties)中。你在这篇指南的开头应该已经添加了这部分内容。如果你希望在程序中改变这些基本信息,加入 person 的 name 或者其他内容,只需要在对应的信息内容中简单得添加一个 “{0}” 然后再程序中使用 setMessage(format(key, stringtoreplace)) 方法填充具体的内容。

你现在可能注意到了我们在这里调用 PersonManager 的代码和我们 PersonManagerTest 的相应代码是一样的。因为 PersonForm 和 PersonManagerTest 都是PersonManagerImpl 的客户 , 所以这是个优雅的结构。

现在你要告诉 Tapestry 这个 page 的存在了。你要做的是在 web/WEB-INF/tapestry.application 文件中加入 page 入口。


    <page name="personForm" specification-path="pages/personForm.page"/>

如果你把 HTML 模板文件保存在 WEB-INF 目录下,上面的步骤是不需要的。希望Taperstry未来的版本允许你设置全局路径。

这个从cancel(), delete() and save()方法 PersonForm 返回到 “MainMenu” 页面 。在下面的部分,你将把它改变成 PersonList 页面。

运行 PersonFormTest [#4]

你看 PersonFormTest 可以发现所有的测试依赖于数据库 person 表中一条 id=1 的纪录( testRemove 方法依赖于 id=2 的纪录 ),所以要在示例数据文件( metadata/sql/sample-data.xml )中加入这些纪录。我通常在文件的底部加入这些内容 – 这个顺序并不重要因为它和其他数据表没有任何关系。

  <table name='person'>
    <column>id</column>
    <column>first_name</column>
    <column>last_name</column>
    <row>
      <value>1</value>
      <value>Matt</value>
      <value>Raible</value>
    </row>
    <row>
      <value>2</value>
      <value>James</value>
      <value>Davidson</value>
    </row>
  </table>

在运行所有的测试以前 DBUnit 会加载这些数据到数据库中,所以这些纪录对你的 Form 测试是可靠的。

保证你的项目中的文件都正确保存。那样你运行ant test-web -Dtestcase=PersonForm – 所有的事情就像你最初期望的那样。

BUILD SUCCESSFUL
Total time: 12 seconds

在浏览器中查看这个表单[#5]

现在执行 ant db-load deploy,启动 Tomcat 在浏览中输入 http://localhost:8080/appfuse/personForm.html ,你将看到如下的界面(略):

注意: Tapestry 会自动把焦点设置在表单中第一个必须输入的字段中。如果希望改变焦点所在位置,可以查看 mailing list archives

在 Tapestry 中,URLs 显得有点丑陋,不过他们包含了大量的信息。与其他的框架只需要你简单调用Action中的方法不一样, – 你需要调用 Page 类中的 listeners 。为了调用PrsonForm 对象中的 “edit” listener ,需要在 web/pages/mainMenu.html 文件中加入下面的代码。

    <a jwcid="@DirectLink" listener="ognl:requestCycle.getPage('personForm').listeners.edit"
        parameters="ognl:new java.lang.Long(1)">Edit Person</a>

最后为了提高界面的用户友好性,你也许希望在表单的上方加入信息,这可以在 personForm.html 中前面使用 <span key=”…”/> 加入所需要显示的信息。

[Optional] 创建一个Canoo WebTest 以模拟测试 PesonForm 对浏览器中操作的响应[#6]

最后一步(可选步骤)是创建一个 Canoo WebTest 测试这个 HTML 模板。

我之所以说着步骤是可选的,是因为你可以通过浏览器实现同样的操作。

你可以使用下面的步骤测试adding、editing 和 saving操作。

  • Add – http://localhost:8080/appfuse/personForm.html.
  • Edit – 使用你在 Main Menu 中创建的 URL ( 确保事先运行过 ant db-load )。
  • Delete – 使用上面的 edit 链接点击 Delete 按钮。
  • Save – 点击 Main Menu 中的 edit 链接 ( 如果你已经删除了纪录需要再次运行 ant db-load ) 然后点击 Save 按钮。

Canoo 测试相当灵活,只需要通过在一个XML文件中配置实现。为了增加 add, edit, save 和 delete 操作的测试,打开 test/web/web-tests.xml 文件并且加入下面的XML。你可以看到一个命名为 PersonTests 目标的片断可以运行所有相关的测试。

我使用 CamelCase 命名 target ( 不同于传统的小写字母中线分割的命名方法 ) 因为你测试时要输入-Dtestcase=Name ,我发现我习惯使用 CamelCase 命名我的单元测试。


    <!-- runs person-related tests -->
    <target name="PersonTests"
        depends="EditPerson,SavePerson,AddPerson,DeletePerson"
        description="Call and executes all person test cases (targets)">
        <echo>Successfully ran all Person HTML Template tests!</echo>
    </target>

    <!-- Verify the edit person screen displays without errors -->
    <target name="EditPerson"
        description="Tests editing an existing Person's information">
        <canoo name="editPerson">
            &config;
            <steps>
                &login;
                <clicklink label="Edit Person"/>
                <verifytitle stepid="we should see the personDetail title"
                    text="${webapp.prefix}${personDetail.title}"/>
            </steps>
        </canoo>
    </target>

    <!-- Edit a person and then save -->
    <target name="SavePerson"
        description="Tests editing and saving a user">
        <canoo name="savePerson">
            &config;
            <steps>
                &login;
                <clicklink label="Edit Person"/>
                <verifytitle stepid="we should see the personDetail title"
                    text="${webapp.prefix}${personDetail.title}"/>
                <!-- update some of the required fields -->
                <setinputfield stepid="set firstName" name="firstNameField" value="Canoo"/>
                <setinputfield stepid="set lastName" name="lastNameField" value="WebTest"/>
                <clickbutton label="${button.save}" stepid="Click Save"/>
                <verifytitle stepid="Page re-appears if save successful"
                    text="${webapp.prefix}${personDetail.title}"/>
            </steps>
        </canoo>
    </target>

    <!-- Add a new Person -->
    <target name="AddPerson"
        description="Adds a new Person">
        <canoo name="addPerson">
            &config;
            <steps>
                &login;
                <invoke stepid="View Person Form" url="/personForm.html"/>
                <verifytitle stepid="we should see the personDetail title"
                    text="${webapp.prefix}${personDetail.title}"/>
                <!-- enter required fields -->
                <setinputfield stepid="set firstName" name="firstNameField" value="Jack"/>
                <setinputfield stepid="set lastName" name="lastNameField" value="Raible"/>
                <clickbutton label="${button.save}" stepid="Click button 'Save'"/>
                <verifytitle stepid="Main Menu appears if save successful" 
                    text="${webapp.prefix}${mainMenu.title}"/>
                <verifytext stepid="verify success message" text="${person.added}"/>
            </steps>
        </canoo>
    </target>

    <!-- Delete existing person -->
    <target name="DeletePerson"
        description="Deletes existing Person">
        <canoo name="deletePerson">
            &config;
            <steps>
                &login;
                <clicklink label="Edit Person"/>
                <clickbutton label="${button.delete}" stepid="Click button 'Delete'"/>
                <verifytitle stepid="display Main Menu" text="${webapp.prefix}${mainMenu.title}"/>
                <verifytext stepid="verify success message" text="${person.deleted}"/>
            </steps>
        </canoo>
    </target>

完成了前面的操作后,你可以在Tomcat运行的状态下运行 ant test-canoo -Dtestcase=PersonTests; 也可以在没有 Tomcat 运行的情况下运行 ant test-html -Dtestcase=PersonTests , Ant 会启动启动/停止 Tomcat。为了在运行所有 Canoo 测试的时候能够包括 PersonTests, 在”run-all-tests” target. 中加入对应的dependency。

你可能注意到Canoo测试没有客户端的日志记录。如果你想看看它到底做了什么,你可以在 web/WEB-INF/classes/log4j.properties 中加入 tweak the log4j settings

BUILD SUCCESSFUL
Total time: 27 seconds


下面的内容: 第四部分: 加入校验和列表页面 – 说明如何增加校验逻辑来使得 firstName 和 lastName 是必填字段。也将展示如何增加一个列表页面显示数据库中所有的person纪录。