JDO(Java Data Object)是JCP中较早开发出来并形成规范的JSR12,该规范对数据的持久化存储进行了一系列规范,并已有众多的商业产品和开源项目是基于该规范。作为一种需要引起重视的技术,研究并探讨其企业应用可行性是十分重要的。

Continue reading "JDO技术分析及企业应用研究"
Posted on October 27, 2006 4:15 PM | | Comments (0) | TrackBacks (0)

      Java数据对象(Java Data Objects,JDO)是一个应用程序接口(API),它是Java程序员能够间接地访问数据库,也就是说,不需使用直接的结构化查询语言(SQL)语句。JDO是作为Java数据库连接(JDBC)的一个补充来介绍的,而JDBC是一个支持使用SOL语句对流行的数据库程序进行访问的接口。有了JDO,程序员就可以使用类来定义数据对象,然后支撑程序就会根据类的定义来管理对给定数据库的实际的数据访问了。  

      JDO是以Sun公司为首所制定的Java Community Process(Java标准制定组织,JCP)的一部分。JDBC仍然保留使用是因为它比起JDO允许程序员在数据库访问上有更大的控制权。除JDO和JDBC外的另一个选择是Enterprise JavaBeans (EJB)。

Continue reading "JDO"
Posted on October 27, 2006 4:08 PM | | Comments (0) | TrackBacks (0)

The Java Pet Store 2.0 Reference Application is a sample application brought to you by the Java BluePrints program at Sun Microsystems. This application is designed to illustrate how the Java EE 5 Platform can be used to develop an AJAX-enabled Web 2.0 application. The application comes with full source-code available under a  BSD-style license, so you can experiment with it and use it in your own applications.

The Java Pet Store 2.0 is the reference application for building AJAX web applications on Java EE 5. It illustrates BluePrints for using AJAX with Java, building AJAX-enabled JSF component libraries, using Java Persistence APIs, applying MVC and other design patterns in an AJAX web app, using Mashups such as Google Maps service for location specific searches of pets and PayPal service for purchases, using an RSS feed as a data  source, and lots more.

Continue reading "Java Pet Store 2.0 Reference Application, Early Access"
Posted on October 26, 2006 1:18 PM | | Comments (0) | TrackBacks (0)

板桥里人 banq@jdon.com

政 府是全社会中最大的信息拥有者和最大的信息技术的用户,有效地利用信息技术,通过建立一个真正有效的、可伸缩的电子政务系统,可以帮助政府向更加勤政、精 简、廉洁和高效的方向发展。电子政务将实现政务应用的四化方向:信息统一化、办公自动化、政务公开化、管理科学化,通过一站式的管理和服务,提升政府部门 职能、办公效率,更好的为国家和人民服务。

整个电子政务系统从需求上可以分为两大部分:综合门户系统和政府政务系统。前者是面向公众的一个窗口,通过门户系统方便企业或市民办事;通过后者可以在政府内部建立一个信息共享、办事流程自动运转的高效协作协调体系。

电子政务系统是个非常复杂纷繁的系统,涉及很多功能和具体要求,越是这样庞大的工程,越需要一个总体架构设计,将那些几百甚至上千种具体功能纳入一个总体框架下,这样电子政务系统才能变成一个可伸缩、可动态扩展、可维护又是可控制性的良性系统。

Continue reading "J2EE电子政务门户系统"
Posted on October 22, 2006 3:30 PM | | Comments (0) | TrackBacks (0)

用NetBeans5.0开发AppFuse1.8.2

一.首先要做的是应用AppFuse做开发都要经历的步骤(引用自http://raibledesigns.com/wiki/Wiki.jsp?page=AppFuseQuickStart):
AppFuse项目的主要目的是帮助你加速web应用程序的开发。下面说明如何利用它创建一个新工程的基本步骤。
1. 下载安装 J2SE 1.4.2+(http://java.sun.com)并设置环境变量JAVA_HOME指向J2SE所在的目录。
2. 下载AppFuse源程序包(https://appfuse.dev.java.net/servlets/ProjectDocumentList)或者从CVS(cvs -d :pserver:guest@cvs.dev.java.net:/cvs co appfuse)中检出appfuse模块。
3. 下载安装 Install Ant 1.6.2+(http://ant.apache.org/)并设置 ANT_HOME 环境变量。 下载安装 Tomcat 4.1.x+(http://tomcat.apache.org/)(推荐使用Tomcat 5.0.28) 并设置 CATALINA_HOME 环境变量指向对应的Tomcat安装目录。
4. 安装 MySQL 3.23.x+ (推荐使用 4.1.7)(http://dev.mysql.com/downloads/mysql/4.0.html).
注意: 如果你使用 MySQL 4.1.7, 确定正确设置了 UTF-8 字符集和 InnoDB 表类型(很重要,可以参照这里http://raibledesigns.com/wiki/Wiki.jsp?page=AppFuseSupport#ref-AppFuseSupport-5)。
5. 安装设置本地SMTP服务器或者修改 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 文件.
9. 若你想要进行测试以确定所有程序工作良好,运行 ant test-all - 在运行这个任务的时候确定 Tomcat 没有启动。 接下来, 运行 ant test-reports - 运行结束后会显示消息告诉你如何查看产生的报告。(我的测试都没有通过,不过在执行完第8步的时候,我运行TOMCAT,浏览http://localhost:8080/appfuse,已经可以正常运行了)

在你确认你正确的完成了上述步骤以后 - 浏览一下http://raibledesigns.com/wiki/Wiki.jsp?page=Articles了解如何使用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项目用NetBeans环境进行开发(我用的是Struts+Spring+Hibernate结构)
其实可以把appfuse的源码包解压到一个目录,用NetBeans直接通过ANT BUILD文件来新建一个项目,这样的话,第一部分的环境设置需要配置好,但要设置成NetBeans集成的ANT,TOMCAT,还有一些要注意的事情,所以我没有这样做。我是通过新建一个空的WEB项目,再将代码COPY到相应的目录中,进行一些修改实现的,以下是具体步骤。
1. 下载安装布署NetBeans5.0(此过程略)
2. 新建一个空的WEB项目myapp(不要引入STRUTS和JSTL)
3. 资源管理器,到新生成的myapp目录下,新建一个lib目录,这里放置所需要的所有jar包,可以COPY生成的appfuse站点的lib目录(我的是C:\jakarta-tomcat-5.0.28\webapps\appfuse\WEB-INF\lib,或者是将appfuse源代码目录下\dist\webapps目录中的appfuse.war用RAR解开,其中也有所有的JAR包)下的所有JAR包,要记得删除两个JAR包(appfuse-dao.jar和appfuse-service.jar)。
重要:要拷贝jta.jar和mysql-connector-java-3.1.7-bin.jar两个JAR包到NetBeans目录中enterprise2\jakarta-tomcat-5.5.7\common\lib目录中。
4. 在NetBeans中右键点击myapp项目,将lib目录下的所有JAR包引入。
5. 拷贝appfuse源码目录中src中的源程序代码到myapp目录中的src\java目录(注意:代码的包是org.开头的,所以不要dao,service,web这三个目录)
6. appfuse源码目录中build目录,要COPY以下文件到相应的目录:dao\gen目录中的文件(生成的Hibernate的ORM文件*.xml)和web\gen目录中的文件(Struts用到的FormBean文件)到myapp\src\java目录
7. 将生成的appfuse站点下WEB-INF\classes目录中的文件COPY到myapp\src\java目录,记得不要那个已经编译成class文件的目录,我们的目的是要配置文件。
8. 将生成的appfuse站点下的所有文件COPY到myapp\web目录下,并且删除WEB-INF目录中的两个目录classes和lib,我们主要是需要配置文件和JSP文件。
9. appfuse源码目录中build目录,要拷贝dao\gen\META-INF目录下的applicationContext-hibernate.xml文件(spring的context文件)到myapp\web\WEB-INF目录下。
10.appfuse源码目录中build目录,要拷贝service\classes\META-INF目录下的applicationContext-service.xml文件(spring的context文件)到myapp\web\WEB-INF目录下。
11.以上步骤把所需要的代码全部COPY完成了,下面要对配置文件进行一些修改。
12.首先修改web.xml文件,因为appfuse生成的是2.3版本的,而NetBeans带的是TOMCAT5.5,用的2.4版,所以要把WEB.XML改为以下的样子:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-app_2_4.xsd"
version="2.4">

<display-name>AppFuse</display-name>
<description>this is build by AppFuse!</description>
<context-param>
<param-name>listenPort_http</param-name>
<param-value>8084</param-value>
</context-param>
<context-param>
<param-name>listenPort_https</param-name>
<param-value>8443</param-value>
</context-param>
<context-param>
<param-name>daoType</param-name>
<param-value>hibernate</param-value>
</context-param>
<context-param>
<param-name>javax.servlet.jsp.jstl.fmt.localizationContext</param-name>
<param-value>ApplicationResources</param-value>
</context-param>
<context-param>
<param-name>javax.servlet.jsp.jstl.fmt.fallbackLocale</param-name>
<param-value>en</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext-*.xml</param-value>
</context-param>
<filter>
<filter-name>securityFilter</filter-name>
<filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class>
<init-param>
<param-name>targetClass</param-name>
<param-value>net.sf.acegisecurity.util.FilterChainProxy</param-value>
</init-param>
</filter>
<filter>
<filter-name>cacheFilter</filter-name>
<filter-class>com.opensymphony.oscache.web.filter.CacheFilter</filter-class>
</filter>
<filter>
<filter-name>clickstreamFilter</filter-name>
<filter-class>com.opensymphony.clickstream.ClickstreamFilter</filter-class>
</filter>
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter>
<filter-name>hibernateFilter</filter-name>
<filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
</filter>
<filter>
<filter-name>rewriteFilter</filter-name>
<filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
</filter>
<filter>
<filter-name>sitemesh</filter-name>
<filter-class>com.opensymphony.module.sitemesh.filter.PageFilter</filter-class>
</filter>
<filter>
<filter-name>exportFilter</filter-name>
<filter-class>org.displaytag.filter.ResponseOverrideFilter</filter-class>
</filter>
<filter>
<description>Login Filter</description>
<filter-name>loginFilter</filter-name>
<filter-class>org.appfuse.webapp.filter.LoginFilter</filter-class>
<init-param>
<param-name>enabled</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter>
<description>Action Filter</description>
<filter-name>actionFilter</filter-name>
<filter-class>org.appfuse.webapp.filter.ActionFilter</filter-class>
<init-param>
<param-name>isSecure</param-name>
<param-value>false</param-value>
</init-param>
</filter>
<filter>
<description>Compression Filter</description>
<filter-name>compressionFilter</filter-name>
<filter-class>org.appfuse.webapp.filter.GZIPFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>securityFilter</filter-name>
<url-pattern>/j_security_check</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>securityFilter</filter-name>
<url-pattern>*.html</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>securityFilter</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>loginFilter</filter-name>
<url-pattern>/login.jsp</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
<!--
<filter-mapping>
<filter-name>rewriteFilter</filter-name>
<url-pattern>/*</url-pattern>-->
<!-- These are needed by Tomcat 5 for forwards -->
<!--<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>-->

<filter-mapping>
<filter-name>loginFilter</filter-name>
<url-pattern>/logout.jsp</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>hibernateFilter</filter-name>
<url-pattern>*.html</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>actionFilter</filter-name>
<url-pattern>*.html</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>clickstreamFilter</filter-name>
<url-pattern>*.html</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>*.html</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>exportFilter</filter-name>
<url-pattern>*.html</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>compressionFilter</filter-name>
<url-pattern>*.html</url-pattern>
</filter-mapping>
<!--
<filter-mapping>
<filter-name>securityFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
-->
<filter-mapping>
<filter-name>sitemesh</filter-name>
<url-pattern>/*</url-pattern>
<!-- These are needed by Tomcat 5 for forwards -->
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
<listener>
<listener-class>com.opensymphony.clickstream.ClickstreamListener</listener-class>
</listener>
<listener>
<listener-class>net.sf.navigator.menu.MenuContextListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
</listener>
<listener>
<listener-class>org.appfuse.webapp.listener.StartupListener</listener-class>
</listener>
<listener>
<listener-class>org.appfuse.webapp.listener.UserCounterListener</listener-class>
</listener>
<servlet>
<description>Login Servlet</description>
<servlet-name>login</servlet-name>
<servlet-class>org.appfuse.webapp.action.LoginServlet</servlet-class>
<init-param>
<param-name>authURL</param-name>
<param-value>j_security_check</param-value>
</init-param>
<init-param>
<param-name>isSecure</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>encrypt-password</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>algorithm</param-name>
<param-value>SHA</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<!-- Front Controller Servlet Mapping -->
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>login</servlet-name>
<url-pattern>/authorize/*</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>10</session-timeout>
</session-config>
<!--
To specify mime mappings, create a file named mime-mappings.xml, put it in your project's mergedir.
Organize mime-mappings.xml following this DTD slice:

<!ELEMENT mime-mapping (extension, mime-type)>
-->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<error-page>
<error-code>500</error-code>
<location>/error.jsp</location>
</error-page>
<error-page>
<error-code>400</error-code>
<location>/index.jsp</location>
</error-page>
<error-page>
<error-code>403</error-code>
<location>/403.jsp</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/404.jsp</location>
</error-page>
<jsp-config>
<taglib>
<taglib-uri>/WEB-INF/appfuse.tld</taglib-uri>
<taglib-location>/WEB-INF/appfuse.tld</taglib-location>
</taglib>
</jsp-config>

<resource-ref>
<description>DB Connection</description>
<res-ref-name>jdbc/appfuse</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
<!--
To set up security settings for your web app, create a file named web-security.xml, put it in your project's mergedir.
Organize web-security.xml following this DTD slice:

<!ELEMENT security-constraint (display-name?, web-resource-collection+, auth-constraint?, user-data-constraint?)>
<!ELEMENT web-resource-collection (web-resource-name, description?, url-pattern*, http-method*)>
<!ELEMENT web-resource-name (#PCDATA)>
<!ELEMENT url-pattern (#PCDATA)>
<!ELEMENT http-method (#PCDATA)>
<!ELEMENT user-data-constraint (description?, transport-guarantee)>
<!ELEMENT transport-guarantee (#PCDATA)>

<!ELEMENT login-config (auth-method?, realm-name?, form-login-config?)>
<!ELEMENT auth-method (#PCDATA)>
<!ELEMENT realm-name (#PCDATA)>
<!ELEMENT form-login-config (form-login-page, form-error-page)>
<!ELEMENT form-login-page (#PCDATA)>
<!ELEMENT form-error-page (#PCDATA)>
-->

</web-app>
13.在NetBeans中右击myapp项目,执行,应该就可以了。
14.我的NetBeans运行时,userList.jsp在执行时,用户列表没办法显示,将<display:table name="${userList}" 改为:<display:table name="userList"就可以了,但APPFUSE自动生成布署的那个就没有问题,希望有人能给解释一下。


上面是这两天我打算用AppFuse做开发的一个开始,希望朋友们能指出不对和不好的地方,大家互相学习交流,看了一下1.8.2中用到的Acegi,感觉应用很方便,准备把这部分好好研究,下一步我打算把AppFuse的启动到运行的具体执行步骤和过程写一下。

Posted on August 3, 2006 8:56 AM | | Comments (0) | TrackBacks (0)

 

创建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纪录。


创建 Tapestry page 和 HTML 模版 - 介绍如何在AppFuse 项目中创建 Tapestry页面和模版。

Posted on July 14, 2006 12:33 PM | | Comments (0) | TrackBacks (0)
上一页 1 2 3 4 5 6 7 下一页
相关内容
广告计划