应用Struts的网站建设

前言:
        前一阶段学习和使用了struts完成了一个web网站,期间得到许多经验教训,但也得到很多好的启发。总得来说,struts是一个很好的MVC框架,为了整理自己的开发思路,才冒出了这个想法,希望能与大家分享好的经验。有关MVC和struts的介绍及很多背景知识,网上的资料很多,在此我不再重复。从我的个人经验上来讲,通过一个好的sample可以学到很多实际的东西,再结合理论上的学习,再能事半而功倍。所以,在这里,我的重点是一步一步建立一个实用的网站。这将是一个简单而野心勃勃的网站。简单指的是它的功能非常简单,野心勃勃则指的是它将尽可能应用struts的各部分功能,同时又能够体现这些功能的优势(这是我的初衷,希望到完成时不会有人说我把struts糟蹋了,:))。当然,这是一个完整的网站,所以它将应用到其他的技术和知识,最重要是的XP(极限编程)理论的一部分,在技术上体现为单元测试(unit test)和重构(refactoring),最重要的一点的kiss(keep it simple and stupid)。同时还会用到很多的开放源码,比如dbcp、log4j等。具体将在开发过程中再一一体现。

网站的功能说明:
        这个网站将实现一个ACL(Access Control List system)系统,同时也将是acl的一个应用,具体体现在对acl本身的维护功能上。完成以后它将能够比较方便的应用在真正的网站应用中。关于acl的理论也不多说明。
它的功能主要有下列几个,由于我们采用XP思想,所以现在只是粗略的列举功能,以后随着开发的深入,将对这些功能特点进行重构。

        1、实现完整的ACL功能,包括验证、维护等等,并且能够很好的应用到其他系统中(第一阶段主要考虑实现,在实现的基础上采用重构来提高易用性)
       2、要完全实现国际化,即通过简单的配置就能够切换语言。
       3、尽可能的可配置,包括sql语言等内容在内的可配置

另外完成此网站的目的还有几个
        1、实践xp的简单计划与反馈。
        2、介绍单元测试的实用技巧

二、结构建设

首先书写用户故事(user story),当然采用CRC card

用户故事描述(准备工作):(注,由于是业余时间完成此工作,并且需要写文章记录过程,所以估算的时间应该专业开发的时间长)

Engineering Task Card

Date: 2003-3-29____

Story #: 准备工作____      Software Eng: _紫龙____ Task Estimate: ___5小时___

Task Description:

    1、建立本系统开发所需要的所有环境,包括开发环境、配置环境及相关的工作。
     2、在开发之前进行一些实验,以证明一些技术的可行性。
     3、记录所有过程

 

Software Engineer’s Notes

Task Tracking:

DateDoneto douse timeComments
2003-2-29完成基本环境设置 1h22m 
     
     

首先,我们来建立开发环境:

1、当然,基本反馈,要有时间统计,因此,拿出以前做的一个小软件,点开时计时。OK
2、JDK,当然选择最新的JDK1.4,在本机上已经安装完毕,没有花费时间。
3、开发工具,选择Eclipse,Eclipse是开发源码的开发工具,在所有的开发工具中对中文的支持也最好(不会发生半个字符的问题),并且有许多插件可以选择(当然,也是由于IBM大力支持的结果),所以此次开发选择它,在以后的开发过程中将看到它的很多好处。由于我们的机器上有完全的开发环境,所以这一步不花时间。
4、web服务器,目前的应用来讲只需要用tomcat就可以了,因此,下载,解包,OK,基本不花时间
5、数据库服务器,目前机器上已经安装好了oracle,因此选择它啦,数据库服务器的安装也ok了

6、好啦,目前看来没有什么基本环境需要准备了,打开eclipse进行工作,先建立一个project,由于我们的Eclpise有tomcat插件,因此,可以选择建立一个tomcat project。

project的名字当然叫strutsacl然后,然后一路点下去,最后就建立了一个project

然后看一下,eclipse为我们完成了什么。

WEB-INF的结构已经替我们完成了,呵呵,不错吧。下面我们对目录进行改造以满足我们的需要。好的习惯能使你更轻松,所以这个目录结构要好好规划。

下面就是我们规划好的目录结构

config这是存放配置文件的目录,将会被编译到class目录下
lib这是存放lib文件的地方,这些lib是只有在程序编译的时候才需要的,在运行时不需要
research这是存放研究文件的地方,在开发过程中会有一些技术的试验,但不适合放在src目录下,所以特别指定这个目录,这样正式文件编译的时候只要把这个目录不放入编译路径就可以了
src这是所有java程序包存放的地方,在这里,源程序将放在org.8866.champion包下
testdata这是存放测试数据的目录,所有的测试数据将以xml方式存放,然后被程序导入到系统中
unittest顾名思议,当然是存放单元测试的地方
userful一个存放一些有用的指导内容的地方,比如相关的文章或代码,但这些内容不需要在本系统中使用
web网站的目录,最后发布的时候只要将web目录打包就OK了。
webtest直接的对web的测试,在这里我们用cactus和jmeter实现web测试,前者可以从tomcat内部进行测试,后者对web应用层面和进行压力测试

   好了,把struts1.1rc1的blank-war解包放入web目录下。运行tomcat(注这里的tomcat服务配置在80端口,而不是默认的8080),打开IE,访问http://localhost/strutsacl/

可以看到网页。证明我们的基本开发环境建立成功了。

好了,做到这里,终于休息一下了。看看时间统计,共计1小时22分钟。期间中断了5次。初步统计一下,跟估算时间相差太远,但是考虑到数据库及基本开发环境的准备都没有花时间,因些估计也不能算很失败。


新的一天来了,外面的太阳不错,真想出去游玩,可惜,只有一个人。算了,还是继续写程序吧。今天要完成一到两个任务(注,由于这是一个教程性质的项目,所以任务划分的比较小)

Engineering Task Card

Date: 2003-3-30____

Story #: 完成struts基本配置____      Software Eng: _紫龙____ Task Estimate: ___3小时___

Task Description:

    1、解释并配置struts的配置文件以达到我们的需要(重点在于基本配置及模板配置)
     2、完成一些基本的页面。如错误页面、消息页面等
     3、记录所有过程

 

Software Engineer’s Notes

Task Tracking:

DateDoneto douse timeComments
2003-3-30完成struts基本设置 3h:52m 
     
     

昨天我们已经把struts-blank运行起来了,这是开发我们的struts应用的好的起点,因为有很多配置文件都替我们做好了。下面我们一一分析各个文件,记住,由于这是ISO-8859-1的,所以经过在其中加入中文注释以后就不能使用了,真正的使用请从struts里取。

<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">

<web-app>

<!-- Standard Action Servlet Configuration (with debugging)
servlet的配置,指定了struts的配置文件,/WEB-INF/struts-config.xml,及其他的一些属性(详细参看相关文档)。
-->
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>2</param-value>
</init-param>
<init-param>
<param-name>detail</param-name>
<param-value>2</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>


<!-- Standard Action Servlet Mapping
此处指定了struts应用的前缀或后缀,比如此处,访问时,出来的是Welcome.do,通过修改它,可以变成其他的形式
-->
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>


<!-- The Usual Welcome File List -->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>


<!-- Struts Tag Library Descriptors
taglib的配置
-->
<taglib>
<taglib-uri>/tags/struts-bean</taglib-uri>
<taglib-location>/WEB-INF/struts-bean.tld</taglib-location>
</taglib>

<taglib>
<taglib-uri>/tags/struts-html</taglib-uri>
<taglib-location>/WEB-INF/struts-html.tld</taglib-location>
</taglib>

<taglib>
<taglib-uri>/tags/struts-logic</taglib-uri>
<taglib-location>/WEB-INF/struts-logic.tld</taglib-location>
</taglib>

<taglib>
<taglib-uri>/tags/struts-nested</taglib-uri>
<taglib-location>/WEB-INF/struts-nested.tld</taglib-location>
</taglib>

<taglib>
<taglib-uri>/tags/struts-tiles</taglib-uri>
<taglib-location>/WEB-INF/struts-tiles.tld</taglib-location>
</taglib>

</web-app>

再来看struts-config.xml,由于此处内容较多,不一一解释了,在下面的开发过程中我们会一一使用到这些内容的。

<?xml version="1.0" encoding="ISO-8859-1" ?>

<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">

<!--

This is a blank Struts configuration file with an example

welcome action/page and other commented sample elements.

Tiles and the Struts Validator are configured using the factory defaults

and are ready-to-use.

NOTE: If you have a generator tool to create the corresponding Java classes

for you, you could include the details in the "form-bean" declarations.

Otherwise, you would only define the "form-bean" element itself, with the

corresponding "name" and "type" attributes, as shown here.

-->

<struts-config>

<!-- ==================================== Data Source Configuration -->

<!--

<data-sources>

<data-source>

<set-property

property="autoCommit"

value="false"/>

<set-property

property="description"

value="Example Data Source Configuration"/>

<set-property

property="driverClass"

value="org.postgresql.Driver"/>

<set-property

property="maxCount"

value="4"/>

<set-property

property="minCount"

value="2"/>

<set-property

property="password"

value="mypassword"/>

<set-property

property="url"

value="jdbc:postgresql://localhost/mydatabase"/>

<set-property

property="user"

value="myusername"/>

</data-source>

</data-sources>

-->

<!-- ======================================== Form Bean Definitions -->

<form-beans>

<!-- sample form bean descriptor for an ActionForm

<form-bean

name="inputForm"

type="app.InputForm"/>

end sample -->

<!-- sample form bean descriptor for a DynaActionForm

<form-bean

name="logonForm"

type="org.apache.struts.action.DynaActionForm">

<form-property

name="username"

type="java.lang.String"/>

<form-property

name="password"

type="java.lang.String"/>

end sample -->

</form-beans>

<!-- ================================= Global Exception Definitions -->

<global-exceptions>

<!-- sample exception handler

<exception

key="expired.password"

type="app.ExpiredPasswordException"

path="/changePassword.jsp"/>

end sample -->

</global-exceptions>

<!-- =================================== Global Forward Definitions -->

<global-forwards>

<!-- Default forward to "Welcome" action -->

<!-- Demonstrates using index.jsp to forward
看到此处的path了, /Welcome.do,其中.do就来自出上面的urlpattern
-->

<forward

name="welcome"

path="/Welcome.do"/>

</global-forwards>

<!-- =================================== Action Mapping Definitions -->

<action-mappings>

<!-- Default "Welcome" action -->

<!-- Forwards to Welcome.jsp -->

<action

path="/Welcome"

type="org.apache.struts.actions.ForwardAction"

parameter="/pages/Welcome.jsp"/>

<!-- sample input and input submit actions

<action

path="/Input"

type="org.apache.struts.actions.ForwardAction"

parameter="/pages/Input.jsp"/>

<action

path="/InputSubmit"

type="app.InputAction"

name="inputForm"

scope="request"

validate="true"

input="/pages/Input.jsp"/>

end samples -->

</action-mappings>

<!-- ===================================== Controller Configuration -->

<controller

processorClass="org.apache.struts.tiles.TilesRequestProcessor"/>

<!-- ================================ Message Resources Definitions -->

<message-resources parameter="resources.application"/>

<!-- ======================================= Plug Ins Configuration -->


<!-- ========== Tiles plugin =================== -->
<!-- -->
<!--
This plugin initialize Tiles definition factory. This later can takes some
parameters explained here after. The plugin first read parameters from web.xml, then
overload them with parameters defined here. All parameters are optional.
The plugin should be declared in each struts-config file.
- definitions-config: (optional)
Specify configuration file names. There can be several comma
separated file names (default: ?? )
- moduleAware: (optional - struts1.1)
Specify if the Tiles definition factory is module aware. If true (default),
there will be one factory for each Struts module.
If false, there will be one common factory for all module. In this later case,
it is still needed to declare one plugin per module. The factory will be
initialized with parameters found in the first initialized plugin (generally the
one associated with the default module).
true : One factory per module. (default)
false : one single shared factory for all modules
- definitions-parser-validate: (optional)
Specify if xml parser should validate the Tiles configuration file.
true : validate. DTD should be specified in file header. (default)
false : no validation

Paths found in Tiles definitions are relative to the main context.
-->
<!-- comment following if struts1.0.x -->
<plug-in className="org.apache.struts.tiles.TilesPlugin" >
<set-property property="definitions-config"
value="/WEB-INF/tiles-defs.xml" />
<set-property property="moduleAware" value="true" />
<set-property property="definitions-parser-validate" value="true" />
</plug-in>

<!-- end comment if struts1.0.x -->

<plug-in className="org.apache.struts.validator.ValidatorPlugIn">
<set-property
property="pathnames"
value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"/>
</plug-in>

</struts-config>

最基本的两个配置文件已经看到了,下面我们来作一个简单的更改。

在web.xml中,找下下一段。

<!-- Standard Action Servlet Mapping
此处指定了struts应用的前缀或后缀,比如此处,访问时,出来的是Welcome.do,通过修改它,可以变成其他的形式
-->
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>

把其中的*.do替换为/do/*

<!-- Standard Action Servlet Mapping -->
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>/do/*</url-pattern>
</servlet-mapping>

在struts-config.xml中找下这一段

<global-forwards>

<!-- Default forward to "Welcome" action -->

<!-- Demonstrates using index.jsp to forward
看到此处的path了, /Welcome.do,其中.do就来自出上面的urlpattern
-->

<forward

name="welcome"

path="/Welcome.do"/>

</global-forwards>

将/Welcome.do替换为/do/Welcome

<global-forwards>

<!-- Default forward to "Welcome" action -->

<!-- Demonstrates using index.jsp to forward -->

<forward

name="welcome"

path="/do/Welcome"/>

</global-forwards>



输入http://localhost/strutsacl/,得以的路径是http://localhost/strutsacl/do/Welcome;jsessionid=BA5D66D8FF71240A1AEF3F9BC271E894。看到了吧,我们的路径就已经得到/do/Welcome,而不是以前的/Welcome.do了。完整的程序包 在此下载

好了,下一步是进入实际应用了。首先。我们前面不是吹过网站要支持国际化吗?我们来看看实际操作是怎样进行的。我们都知道,java的国际化是通过资源文件来实现的。struts也是如此。我们可以在WEB-INF/src/java/resources下找到一个application.properties,它就是我们要处理的。好了,先打开rbmanager(如果你还不知道rbManger是什么,那就看这篇文章吧IBM的介绍吧),导入application.properties的内容,导入时选择放入basicgroup组中,这个组我们是这个网站的一些通用内容所存放的地方。然后建立一个zh_CN的资源文件,这个就是放GB2312的文件啦,如下图所示。

然后对其中的内容一一翻译。最后另存到我们的config/resources目录中(前面我们说过,所有的配置文件都要放在config目录下的),名称不变(省去修改struts-config的资源文件的名称了)。下一步,删除WEB-INF/src/java/resources,因为我们有自己的src目录了,所以不需要它。然后进行Eclipse,刷新目录,看到resouces目录了。在project菜单下选择rebuild project,就会把资源文件编译到需要的目录下了。重起tomcat,在IE中刷新。看到了,乱码????。别着急,这是常常碰到的中文问题啦。打开Welcome.jsp文件,一看,果然,struts的人员在偷懒,没关系,在第一行加上<%@ page contentType="text/html;charset=UTF-8" language="java" %>,再刷新。出来了,没问题。如下图所示。

这一篇太长了,再放入下一篇吧。


下面我们来研究一下Welcome.jsp,看看它是怎么实现这种转换的

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
这一句是我们加的,解决了中文问题

<%@ taglib uri="/tags/struts-bean" prefix="bean" %>
<%@ taglib uri="/tags/struts-html" prefix="html" %>
<%@ taglib uri="/tags/struts-logic" prefix="logic" %>
上面是引用了tablib库的声明,此处使用了bean,html和logic三个

<html:html locale="true">
上面这一句,locale="true",就是tablib的应用,可以自己判断浏览器的字符集,并传回相应的内容
<head>
<title><bean:message key="welcome.title"/></title>
上面这一句<bean:message key="welcome.title"/>,就是从资源文件中取得值,在application.properties里有welcome.title=Struts/ Blank/ Application,所以在英文浏览器中就显示Struts Blank Application,中文版中则从application_zh_CN.properties中取得
<html:base/>
这一句可以取得相对路径,在以后的应用的再详细说明
</head>
<body bgcolor="white">

<logic:notPresent name="org.apache.struts.action.MESSAGE" scope="application">
<font color="red">
ERROR: Application resources not loaded -- check servlet container
logs for error messages.
</font>
</logic:notPresent>
一个logic的判断,如果资源文件没有load进来,就显示这一段内容

<h3><bean:message key="welcome.heading"/></h3>
<p><bean:message key="welcome.message"/></p>

上面这两句就是显示

欢迎!
开始第一个struts应用

内容。从welcome.heading及welcome.message中取得

</body>
</html:html>

从上面的分析中,可以看得到Taglib的强大之处了吧。通过这些,我们可以很简单的达到国际化的功能。当然struts所用的taglib不是jstl的标准,但我们只是在应用,好用就行,而且实际上这些都快成了事实标准了。

好了,再来看下一个文件,index.jsp,是blank-struts中仅有的两个文件之一。它的内容如下:

<%@ taglib uri="/tags/struts-logic" prefix="logic" %>
<logic:redirect forward="welcome"/>

一个简单的forward,其功能相当于jsp的forward

<%--

Redirect default requests to Welcome global ActionForward.
By using a redirect, the user-agent will change address to match the path of our Welcome ActionForward.

--%>

有关Taglib的功能,先研究到此,以后会有更多,更复杂的应用,将能更好的体现它的好处。

下面我们继续来解决上面提到的中文问题,上面的方法只解决了显示的问题,没有解决输入的问题,我们通过加一个filter,就可以彻底解决这个问题。当然,我们不会自己去写这个filter的,那么这个filter来自何处?找到tomcat目录,在它的webapps/examples/WEB-INF/classes/filters目录下我们可以找到SetCharacterEncodingFilter.java,就是它,我们把它copy到我们的src目录下。将来我们还会用到很多来自apache的源码,为以示区别,我们建立org.apche包,并把这个文件copy到filters包下,把package改成org.apache.filters,编译就ok了。下面我们来配置这个filter,把下面的加入web.xml就可以了

<filter>
<filter-name>Set Character Encoding</filter-name>
<filter-class>org.apache.filters.SetCharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>

<filter-mapping>
<filter-name>Set Character Encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

重起tomcat,出错了?再仔细查一查,原来是版本问题,把 <!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">中的2.2改成2.3就ok了。

好,但是没有出错,我怎么知道filter就起作用了呢?这就是Eclipse的tomcat插件的强大之处了。在dofilter函数里中断,再刷新页面。看到了吧,停在中断处了。

按resume继续运行,页面就出来了。Eclipse不仅能够方便的调试普通java程序,还能在tomcat的运行期调试所有的包括jsp、servlet等等程序。

好,下一步是做一个错误页面,做一个消息页面。这都是通用的。当然要前期先做好。

不过好在我们有现成的,从strutsresume里copy了一个过来。略加修改,当然,还要在我们的资源文件中加入相应的条目内容。然后rebuild project,再重起tomcat

访问http://localhost/strutsacl/common/error.jsp,出现下列图片。说明我们成功了。

当然,message我们也如法炮制。 再加入css和其他script文件。目前我们的网站就完成了一些基本元素了。合计一下时间,是三个小时52分,比估算超出一个小时,没关系,记录在案,下次估算的时候改进。

好,把现在完成的工作打包,可从这里下载

对了,要证明是国际化了,当然要看到英文的喽,好,我们把IE的语言改成en,然后重启IE,看到如下图片

这下国际化没有问题了吧


改进struts和web开发的基本配置,今天继续改进系统,工作量估计跟昨天的差不多,因此,跟距昨天的结果,今天的预计时间是4小时。

Engineering Task Card

Date: 2003-3-30____

Story #: 改进struts基本配置____      Software Eng: _紫龙____ Task Estimate: ___4小时___

Task Description:

    1、改进struts配置
     2、完成log的配置和探讨
     3、完成错误处理过程
     4、记录所有过程

 

Software Engineer’s Notes

Task Tracking:

DateDoneto douse timeComments
2003-3-303   
2003-4-12、1   
     

首先,我们来实现一个JSP页面的错误处理,用来捕获未知的错误。同样,我们在web目录下建立一个research目录,用于存放我们测试的文件。建立一个testfail.jsp文件。

<%@ taglib uri="/tags/struts-bean" prefix="bean" %>
<%@ taglib uri="/tags/struts-html" prefix="html" %>
<%@ taglib uri="/tags/struts-logic" prefix="logic" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" errorPage="/common/error.jsp"%>
<html:html locale="true">
<head>
<title></title>
<html:base/>
</head>
<body bgcolor="white">


<%int i=0;%>
<%=3/i%>

</body>
</html:html>

在蓝色处,有一个除0错。
我们现在看一下效果。运行tomcat,在IE里输入http://localhost/strutsacl/research/testfail.jsp,错误处理功能实现了。如下所示

好,对普通jsp文件的处理就这么简单。下面我们来看怎么处理struts的action。先建立一个GlobalException

<!-- ========== Global Exception Definitions ============================== -->
<global-exceptions>
<exception key="errors.detail" path="/common/error.jsp" type="java.lang.Exception" />

</global-exceptions>

是一个全局的Exception,其中errors.detail是一个资源项,存放在资源文件中。类型是所有的Exception。

然后我们建立一个Action来测试这个Exception是否能够正常工作。

同样在research目录下建立com.ewuxi.champion.struts.action.TestFailAction类。如下

package com.ewuxi.champion.struts.action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

/**
* Created on 2003-3-31
* Version on version
* Author champion
* changed on 2003-3-31 11:22:41
*

*/
public class TestFailAction extends Action {

// --------------------------------------------------------- Instance Variables

// --------------------------------------------------------- Methods

/**
* Method execute
* @param ActionMapping mapping
* @param ActionForm form
* @param HttpServletRequest request
* @param HttpServletResponse response
* @return ActionForward
* @throws Exception
*/
public ActionForward execute(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {

throw new UnsupportedOperationException("Generated method 'execute(...)' not implemented.");
}

}

其上execute只抛出一个错误。在struts-config.xml中加入

<action path="/testfail" type="com.ewuxi.champion.struts.action.TestFailAction">

</action>

重启tomcat,在浏览器中输入http://localhost/strutsacl/do/testfail,看到如下效果。

OK,说明GlobalException成功了。好了,暂停,吃饭去了。

好,继续昨天的工作。下面进入第二个主题。有关日志系统。大家都知道,日志是我们将来查错和维护的主要依据。好的日志可以让你事半功们,而差的日志呢,会是一堆你无法看懂的东西,影响胃口。所以我们的系统当然要把日志系统先弄明白,有个规则。以后才好办。闲话少说,我们的日志系统采用的是有名log4j(怎么?你还没听说过吗?要好好的补课喔),然后我们遵循《EJB 异常处理的最佳做法》的指示,建立我们的日志规则。很简单,把从网站上下载来的包放到com.ewux.champion.opensrc目录了,为了以示区别,加一个目录加logkit。 然后从一些简单的refactoring,就是改名拉。有了Eclipse,象rename这一类的refactoring就简单了,让它帮我们搞定。OK,下面来看配置。

我们配置如下

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">

<appender name="ASYNC" class="org.apache.log4j.AsyncAppender">
<appender-ref ref="TEMP"/>
<appender-ref ref="CONSOLE"/>
</appender>

<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/>
</layout>
</appender>


<appender name="TEMP" class="org.apache.log4j.FileAppender">
<param name="File" value="error.log"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/>
</layout>
<filter class="org.apache.log4j.varia.LevelRangeFilter">
<param name="LevelMin" value="ERROR"/>
<param name="LevelMax" value="FATAL"/>
<param name="acceptOnMatch" value="TRUE"/>
</filter>
</appender>
<appender name="LOG" class="org.apache.log4j.FileAppender">
<param name="File" value="log.log"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/>
</layout>
<filter class="org.apache.log4j.varia.LevelMatchFilter">
<param name="LevelToMatch" value="ERROR"/>
<param name="acceptOnMatch" value="false"/>
</filter>
</appender>

<logger name="org.apache">
<level value="ERROR"/>
</logger>

<logger name="com.ewuxi.champion">
<level value="DEBUG"/>
</logger>


<root>
<priority value="ERROR"/>
<appender-ref ref="CONSOLE"/>
<appender-ref ref="TEMP"/>
<appender-ref ref="LOG"/>
</root>

</log4j:configuration>

很简单,建立三个append,一个是CONSOLE,就是普通的控制台拉,还有两个文件类型的error.log和log.log,其中error.log专门记录错误信息,即ERROR和FATAL一级。log.log则记录其他信息。当然,如果有必要,也可以建立一个debug.log专门记录debug信息。总之LOG4j的功能强大,就在可配置性强。好好研究吧。

好了,日志已经完成了,下面需要改进的是什么?有两个,一个是数据连接池,一个是网页的模板。这样,模板作为一个单独任务考虑比较合理。那这个任务就以楝完成数据连接池为结束吧。首先我们需要JDBC驱动程序,放到WEB-INF/lib目录下。然后我们准备采用DBCP作为连接池。所以把DBCP的类库也copy到WEB-INF/lib下。由于要配置在tomcat的server.xml目录下,所以在config目录下建立一个server.xml,真正发布时需要把它copy到{tomcat}/config/server.xml的相应地方。

其内容如下

<!--此处需要改成实际的目录 --->
<Context path="/strutsacl" docBase="G:/myeclipse/eclipse/workspace/strutsacl/web" workDir="G:/myeclipse/eclipse/workspace/strutsacl/web/work/org/apache/jsp">
<!---JNDI的配置---->
<Resource name="jdbc/strutsacl" auth="Container"
type="javax.sql.DataSource"/>
<ResourceParams name="jdbc/strutsacl">
<parameter>
<name>factory</name>
<value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
</parameter>
<!-- Maximum number of dB connections in pool. Make sure you
configure your mysqld max_connections large enough to handle
all of your db connections. Set to 0 for no limit.
-->
<parameter>
<name>maxActive</name>
<value>100</value>
</parameter>
<!-- Maximum number of idle dB connections to retain in pool.
Set to 0 for no limit.
-->
<parameter>
<name>maxIdle</name>
<value>30</value>
</parameter>
<!-- Maximum time to wait for a dB connection to become available
in ms, in this example 10 seconds. An Exception is thrown if
this timeout is exceeded. Set to -1 to wait indefinitely.
-->
<parameter>
<name>maxWait</name>
<value>10000</value>
</parameter>
<!-- MySQL dB username and password for dB connections -->
<parameter>
<name>username</name>
<value>test</value>
</parameter>
<parameter>
<name>password</name>
<value>test</value>
</parameter>
<!-- Class name for mm.mysql JDBC driver -->
<parameter>
<name>driverClassName</name>
<value>oracle.jdbc.OracleDriver</value>
</parameter>
<!-- The JDBC connection url for connecting to your MySQL dB.
The autoReconnect=true argument to the url makes sure that the
mm.mysql JDBC Driver will automatically reconnect if mysqld closed the
connection. mysqld by default closes idle connections after 8 hours.
-->
<parameter>
<name>url</name>
<value>jdbc:oracle:thin:@127.0.0.1:1521:champion</value>
</parameter>
</ResourceParams>
</Context>

配置完成后,我们需要一个程序来测试配置是否成功。当然,最简单的就是一个Severlet啦。好,开始。


package com.ewuxi.champion.Servlet;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.sql.DataSource;

import org.apache.log4j.Logger;

import com.ewuxi.champion.opensrc.logkit.StackTraceUtil;


/**
 * Created on 2003-4-1
 * Version on version
 * Author champion
 * changed on 2003-4-1 21:41:54 
 */
public class TestServlet extends HttpServlet {
    /**
     * @see javax.servlet.GenericServlet#init()
     */
    public void init() throws ServletException {
        super.init();

        String jndi_name = "java:comp/env/jdbc/strutsacl";
        Logger log = Logger.getLogger(this.getClass());

        try {
            Context ctx = new InitialContext();

            if (ctx == null) {
                throw new Exception("No Context");
            }

            DataSource ds = (DataSource) ctx.lookup(jndi_name);
            Connection conn=ds.getConnection();
            PreparedStatement psmt=conn.prepareStatement("select * from dual");
            ResultSet rs=psmt.executeQuery();
            log.info("test ok");
        } catch (NamingException e) {
            log.error("can't find Nameing Error" + 
                      StackTraceUtil.getStackTrace(e));
        } catch (Exception e) {
            log.error("Other Error" + StackTraceUtil.getStackTrace(e));
        }
    }
}

重启tomcat,看看效果。

2003-4-1 22:02:00 org.apache.commons.modeler.Registry loadRegistry
信息: Loading registry information
2003-4-1 22:02:00 org.apache.commons.modeler.Registry getRegistry
信息: Creating new Registry instance
2003-4-1 22:02:02 org.apache.commons.modeler.Registry getServer
信息: Creating MBeanServer
2003-4-1 22:02:04 org.apache.coyote.http11.Http11Protocol init
信息: Initializing Coyote HTTP/1.1 on port 80
Starting service Tomcat-Standalone
Apache Tomcat/4.1.18-LE-jdk14
2003-04-01 22:02:21,172 INFO [main] Servlet.TestServlet (TestServlet.java:46) - test ok
2003-4-1 22:02:31 org.apache.coyote.http11.Http11Protocol start
信息: Starting Coyote HTTP/1.1 on port 80
2003-4-1 22:02:32 org.apache.jk.common.ChannelSocket init
信息: JK2: ajp13 listening on /0.0.0.0:8009
2003-4-1 22:02:32 org.apache.jk.server.JkMain start
信息: Jk running ID=0 time=40/420 config=G:/myeclipse/tomcat/conf/jk2.properties

OK,测试成功,那么不成功的情况怎么样?关掉oracle,再来重启一次tomcat

2003-4-1 22:40:28 org.apache.commons.modeler.Registry loadRegistry
信息: Loading registry information
2003-4-1 22:40:28 org.apache.commons.modeler.Registry getRegistry
信息: Creating new Registry instance
2003-4-1 22:40:30 org.apache.commons.modeler.Registry getServer
信息: Creating MBeanServer
2003-4-1 22:40:34 org.apache.coyote.http11.Http11Protocol init
信息: Initializing Coyote HTTP/1.1 on port 80
Starting service Tomcat-Standalone
Apache Tomcat/4.1.18-LE-jdk14
DBCP borrowObject failed: java.sql.SQLException: Io 异常: The Network Adapter could not establish the connection
2003-04-01 22:40:50,032 ERROR [main] Servlet.TestServlet (TestServlet.java:51) - Other Errororg.apache.commons.dbcp.DbcpException: java.sql.SQLException: Io 异常: The Network Adapter could not establish the connection
at org.apache.commons.dbcp.DriverConnectionFactory.createConnection(DriverConnectionFactory.java:85)
at org.apache.commons.dbcp.PoolableConnectionFactory.makeObject(PoolableConnectionFactory.java:184)
at org.apache.commons.pool.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:722)
at org.apache.commons.dbcp.AbandonedObjectPool.borrowObject(AbandonedObjectPool.java:117)
at org.apache.commons.dbcp.PoolingDataSource.getConnection(PoolingDataSource.java:108)
at org.apache.commons.dbcp.BasicDataSource.getConnection(BasicDataSource.java:312)
at com.ewuxi.champion.Servlet.TestServlet.init(TestServlet.java:43)
at javax.servlet.GenericServlet.init(GenericServlet.java:256)
at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:934)
at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:821)
at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:3420)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:3608)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1188)
at org.apache.catalina.core.StandardHost.start(StandardHost.java:738)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1188)
at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:347)
at org.apache.catalina.core.StandardService.start(StandardService.java:497)
at org.apache.catalina.core.StandardServer.start(StandardServer.java:2189)
at org.apache.catalina.startup.Catalina.start(Catalina.java:512)
at org.apache.catalina.startup.Catalina.execute(Catalina.java:400)
at org.apache.catalina.startup.Catalina.process(Catalina.java:180)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:324)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:203)
Caused by: java.sql.SQLException: Io 异常: The Network Adapter could not establish the connection
at oracle.jdbc.dbaccess.DBError.throwSqlException(DBError.java:180)
at oracle.jdbc.dbaccess.DBError.throwSqlException(DBError.java:222)
at oracle.jdbc.dbaccess.DBError.throwSqlException(DBError.java:335)
at oracle.jdbc.driver.OracleConnection.<init>(OracleConnection.java:361)
at oracle.jdbc.driver.OracleDriver.getConnectionInstance(OracleDriver.java:442)
at oracle.jdbc.driver.OracleDriver.connect(OracleDriver.java:321)
at org.apache.commons.dbcp.DriverConnectionFactory.createConnection(DriverConnectionFactory.java:83)
... 25 more

2003-4-1 22:41:02 org.apache.coyote.http11.Http11Protocol start
信息: Starting Coyote HTTP/1.1 on port 80
2003-4-1 22:41:02 org.apache.jk.common.ChannelSocket init
信息: JK2: ajp13 listening on /0.0.0.0:8009
2003-4-1 22:41:02 org.apache.jk.server.JkMain start
信息: Jk running ID=0 time=20/340 config=G:/myeclipse/tomcat/conf/jk2.properties

OK,如果连接不通,通过log4j就能够看到错误了。同时在elcipse目录下查看log.log
其内容只有一句

2003-04-01 22:02:21,172 INFO [main] Servlet.TestServlet (TestServlet.java:46) - test ok

而err.log则有如下内容
2003-04-01 22:40:50,032 ERROR [main] Servlet.TestServlet (TestServlet.java:51) - Other Errororg.apache.commons.dbcp.DbcpException: java.sql.SQLException: Io 异常: The Network Adapter could not establish the connection
at org.apache.commons.dbcp.DriverConnectionFactory.createConnection(DriverConnectionFactory.java:85)
at org.apache.commons.dbcp.PoolableConnectionFactory.makeObject(PoolableConnectionFactory.java:184)
at org.apache.commons.pool.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:722)
at org.apache.commons.dbcp.AbandonedObjectPool.borrowObject(AbandonedObjectPool.java:117)
at org.apache.commons.dbcp.PoolingDataSource.getConnection(PoolingDataSource.java:108)
at org.apache.commons.dbcp.BasicDataSource.getConnection(BasicDataSource.java:312)
at com.ewuxi.champion.Servlet.TestServlet.init(TestServlet.java:43)
at javax.servlet.GenericServlet.init(GenericServlet.java:256)
at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:934)
at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:821)
at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:3420)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:3608)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1188)
at org.apache.catalina.core.StandardHost.start(StandardHost.java:738)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1188)
at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:347)
at org.apache.catalina.core.StandardService.start(StandardService.java:497)
at org.apache.catalina.core.StandardServer.start(StandardServer.java:2189)
at org.apache.catalina.startup.Catalina.start(Catalina.java:512)
at org.apache.catalina.startup.Catalina.execute(Catalina.java:400)
at org.apache.catalina.startup.Catalina.process(Catalina.java:180)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:324)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:203)
Caused by: java.sql.SQLException: Io 异常: The Network Adapter could not establish the connection
at oracle.jdbc.dbaccess.DBError.throwSqlException(DBError.java:180)
at oracle.jdbc.dbaccess.DBError.throwSqlException(DBError.java:222)
at oracle.jdbc.dbaccess.DBError.throwSqlException(DBError.java:335)
at oracle.jdbc.driver.OracleConnection.<init>(OracleConnection.java:361)
at oracle.jdbc.driver.OracleDriver.getConnectionInstance(OracleDriver.java:442)
at oracle.jdbc.driver.OracleDriver.connect(OracleDriver.java:321)
at org.apache.commons.dbcp.DriverConnectionFactory.createConnection(DriverConnectionFactory.java:83)
... 25 more

由此可以证明,所有的配置都成功了。好了,可以告一段落了。统计一下时间,3小时42分。跟预计的时间相差不多。明天的任务是完成模板,即tiles的研究和配置。


内容

Engineering Task Card

Date: 2003-4-2____

Story #: 完成模板的功能____      Software Eng: _紫龙____ Task Estimate: ___6小时___

Task Description:

    1、探讨tiles的功能,并在我们的网站中实现tiles,考虑到以tites不熟,因此,多估计一些时间
     2、记录所有过程

 

Software Engineer’s Notes

Task Tracking:

DateDoneto douse timeComments
2003-4-2    
     
     

有关tiles的使用,当然要看经典的文章(http://www.javaworld.com/javaworld/jw-01-2002/jw-0104-tilestrut.html),我们的配置也是根据这篇文章的指导来实现的。同时我们要参考tiles-documentation的指导。
先建立最基本的模板页:/layouts/root.jsp

<%@ page contentType="text/html; charset=UTF-8" language="java" errorPage="/common/error.jsp" %>
<%@ include file="/common/taglibs.jsp" %>
<html:html locale="true">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel=stylesheet href="<html:rewrite page="/layouts/stylesheet.css"/>" type="text/css">
<title><tiles:getAsString name="title"/></title>
<html:base/>
</head>
<body>
<tiles:insert attribute="body" />
</body>
</html:html>
然后建立一个/research/testroot.jsp
<%@ page language="java" errorPage="/common/error.jsp" contentType="text/html;charset=UTF-8" %>
<%@ include file="/common/taglibs.jsp" %> <tiles:insert page="/layouts/root.jsp" flush="true">
<tiles:put name="title" ><bean:message key="welcome.message"/></tiles:put>
<tiles:put name="body" value="test.jsp"/>
</tiles:insert>
以及一个/research/test.jsp内容很简单
a test page


启动服务器,然后在浏览器里输入http://localhost/strutsacl/research/testroot.jsp

OK,测试成功了,让我们再来进行另一种类型的测试
首先建立tiles-tests-defs.xml,这个XML将是我们测试tiles的主机文件

<?xml version="1.0" encoding="ISO-8859-1" ?>

<!DOCTYPE tiles-definitions PUBLIC
"-//Apache Software Foundation//DTD Tiles Configuration 1.1//EN"
"http://jakarta.apache.org/struts/dtds/tiles-config_1_1.dtd">

<tiles-definitions>


<definition name="testroot" path="/layouts/root.jsp">
<put name="title" value="testrootinxml" />
<put name="body" value="/research/test.jsp" />
</definition>

</tiles-definitions>


意识到这个系统将来需要实现的功能与现在所测试的功能可能会混在一起,不利于将来的维护,然后我们对系统作一次大的refactoring。

首先我们利用Struts1.1的新特性,将struts-config.xml中的测试内容移到struts-strutstest-config.xml。同时修改web.xml

<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>config/strutstest</param-name>
<param-value>/WEB-INF/struts-strutstest-config.xml</param-value>
</init-param>

这样我们就可以得到两个配置文件,其中一个只作为试验功能所用。另一个则是将来的实际功能的配置文件。同时将

<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>将/do/*修成当前值
</servlet-mapping>

然后我们配置一个tiles-tests-defs.xml文件。

<?xml version="1.0" encoding="ISO-8859-1" ?>

<!DOCTYPE tiles-definitions PUBLIC
"-//Apache Software Foundation//DTD Tiles Configuration 1.1//EN"
"http://jakarta.apache.org/struts/dtds/tiles-config_1_1.dtd">

<tiles-definitions>


<definition name="testroot" path="/layouts/root.jsp">
<put name="title" value="testrootinxml" />
<put name="body" value="/research/test.jsp" />
</definition>

</tiles-definitions>

然后在struts-strutstest-config.xml加入

<action parameter="testroot" path="/testroot" type="org.apache.struts.actions.ForwardAction" />

最后我们在浏览器里输入http://localhost/strutsacl/strutstest/testroot.do

效果如下


OK,成功了。再进行下一步测试。即模板的继承功能的测试。同样测试成功。OK,我们开始真正的规划,不过在此之前先去吃饭。

XP原则,我们先只作两个基本的模板,并且使用静态模板,因此,设置如下:

<?xml version="1.0" encoding="ISO-8859-1" ?>

<!DOCTYPE tiles-definitions PUBLIC
"-//Apache Software Foundation//DTD Tiles Configuration 1.1//EN"
"http://jakarta.apache.org/struts/dtds/tiles-config_1_1.dtd">

<tiles-definitions>
<definition name="tiles.center" extends="/layouts/centerLayout.jsp">
<put name="title" value="title need to be updated(from center )" />
<put name="body" value="/layouts/centerLayout.jsp" />
</definition>
<definition name="tiles.classic" extends=/layouts/classicLayout.jsp">
<put name="title" value="title need to be updated(from cliassic )" />
<put name="body" value="/layouts/classicLayout.jsp" />
</definition>

</tiles-definitions>

并建立testclassic.jsp和testcenter.jsp用于测试。其完整的代码如下下载

总结,到目前为止,总共使用约9小时52分来完成此部分功能。从功能说明上来讲,由于对tiles的不熟,因此,提出的目标比较模糊,就此前已经实现的功能,称为实现静态模板。若为一般应用,已经足够了。不过与估计的6小时相比,所花费的时间远远多出了估计的时间。再加上一些未在计算机前度过的思考时间。在此功能上花费的时间不少于十二个小时,而已实现的功能却少于所估算的(下一个任务将是实现动态模板)。因此,把这个任务分开,目前已经完成的归在本任务中,未完成的划到下一个任务中去。总之,由这个实践可以看出,采用新技术为软件开发所带来的风险。即使我们相比前几个任务已经多估算了时间,但实际所花费的时间却大大超出了估算(一倍有余)。在我们这个以研究为目的的项目中还可以过得去,但在以合同为目的的项目中却可能已经埋下了失败的因素。好了,闲话不说,进入下一个任务。


经过对tiles的研究,大概了解它能够完成什么了,所以,在这个任务里我们将实现tiles的功能以满足我们的使用。 由于上一个任务所花费的时间,在这个任务里可以节约一部分时间,所以估计仍然为6小时。

Engineering Task Card

Date: 2003-4-11___

Story #:_实现Tiles的多数功能___      Software Eng: _紫龙____ Task Estimate: __6小时___

Task Description:

    1、实现动态模板功能
     2、实现模板的国际化功能
     3、用户可以在运行时选择模板
     4、记录全过程

 

Software Engineer’s Notes

Task Tracking:

DateDoneto douse timeComments
2003-04-11    
2003-04-121,2,3,4 6:52 
     


要实现动态模板,则需要一些程序了。我们把example的程序放入我们的project里面,然后进行相应的设置。把包命名为org.apache.struts.tiles.skin,同时修改程序的一部分以适应我们的需要。然后修改tiles-defs.xml

<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE tiles-definitions PUBLIC
"-//Apache Software Foundation//DTD Tiles Configuration 1.1//EN"
"http://jakarta.apache.org/struts/dtds/tiles-config_1_1.dtd">

<tiles-definitions>
<definition name="tiles.root" path="/controller/layoutSwitch.do">
<put name="userSettingsName" value="tiles.skin.user.settings" />
<put name="catalogSettings" value="available.skins" />
<put name="layout.attribute" value="page.layout" />
<put name="title" value="title need to be updated(from root)" />
<put name="header" value="/pages/head.jsp"/>
<put name="footer" value="/pages/foot.jsp"/>
<put name="menu" value="tiles.menu.bar" />
</definition>


<!-- Menu bar definition -->
<!-- Some definition come from other definition files (ex : doc.menu.*) -->
<definition name="menu.root" path="/layouts/menu.jsp" >
</definition>
<definition name="tiles.menu.bar" path="/layouts/vboxLayout.jsp" >
<putList name="list" >
<add value="menu.links" />
<add value="menu.settings" />
<!--<add value="menu.userMenu" />


<add value="menu.admin" />-->
</putList>
</definition>
<!-- main menu definition -->
<definition name="menu.links" extends="menu.root" >
<put name="title" value="Links" />
<putList name="items" >
<item value="Home" link="/index.jsp" />
</putList>
</definition>
<!-- Preferences menu definition -->
<definition name="menu.settings" extends="menu.root" >
<put name="title" value="Preferences" />
<putList name="items" >
<item value="my Layout Settings" link="/control/SkinSettings.do" />
</putList>
</definition>

<!-- Skin main page -->
<definition name="skin.settings.page" extends="tiles.root">
<put name="title" value="Skin Setting" />
<put name="body" value="skin.settings.body"/>
</definition>
<!-- Skin body declaration-->
<definition name="skin.settings.body" path="/skinsetting/skinSettings.jsp"
controllerUrl="/controller/SkinSettings.do" >
<put name="userSettingsName" value="tiles.skin.user.settings" />
<put name="catalogSettings" value="available.skins" />
</definition>
<!-- Available skins -->
<definition name="available.skins" >
<putList name="skin.list" >
<add value="default.skin" />
<add value="menuright.skin" />
</putList>
</definition>

<!-- Default skin values -->
<definition name="default.skin" >
<put name="skin.label" value="Default" />
<put name="page.layout" value="/layouts/classicLayout.jsp" />
<put name="menu.layout" value="/layouts/menu.jsp" />
<put name="menuBar.layout" value="/layouts/vboxLayout.jsp" />
</definition>

<!-- Default skin values -->
<definition name="menuright.skin" extends="default.skin" >
<put name="skin.label" value="Right Menu" />
<put name="page.layout" value="/layouts/skin1/menuLeftLayout.jsp" />
<put name="menu.layout" value="/layouts/menu.jsp" />
</definition>
</tiles-definitions>

建立一系列的菜单和模板页面。修改struts-config.xml,加入以下内容

<!-- ======================================================= -->
<!-- ACL Controls -->
<!-- ======================================================= -->
<action path = "/login"
type = "com.ewuxi.champion.struts.actions.LoginAction"
name = "loginForm"
scope = "request"
validate = "true"
input = "/index.jsp">

<forward name="success" path="/aclController.do?fwd=users" />
<forward name="failure" path="index.jsp" />
</action>
<!-- ======================================================= -->
<!-- Tiles 1.1 Skin Settings -->
<!-- ======================================================= -->
<action path="/controller/layoutSwitch" type="org.apache.struts.tiles.skin.LayoutSwitchAction" />
<action path="/control/SkinSettings"
type="org.apache.struts.tiles.actions.NoOpAction"
name="SkinSettingsForm" >
<forward name="success" path="skin.settings.page"/>
</action>
<action path="/controller/SkinSettings" type="org.apache.struts.tiles.skin.LayoutSettingsAction" name="SkinSettingsForm" />
</action-mappings>

然后建立layout目录,并放入相应的文件,建立pages目录,建立相应的文件。通过以上的设置,我们实现了动态更换模板并建立了第一个登录页面。

其结果如下:默认的登录页面


默认的皮肤页面设置

第二种状态,即菜单在右边的状态

菜单在右的主页面

总结:通过对Tiles应用的深入研究,我们实现在动态更改皮肤的功能,并且也实现了皮肤的国际化功能(其实现原理与前述的国际化原理一样)。这样我们达到了本任务的要求。统计一下时间是6小时52分。超过统计约一个小时,其中的主要原因是原来example中有一个bug,即更改的皮肤必须要按刷新按钮才能实现,大约花了一个多小时来查出这个bug,并修正了这个bug。通过上面的配置内容可见,模板的可继承以及嵌套的功能给我们的设计带来了极大的方便。同时menu的设计也带来很多益处。当然,我们还没有实现跟据用户的设置来实现每次进入的时候风格相同。这将在下一个任务中实现。这个任务的源代码如下下载


通过完成上一个任务以后,我们发现我们的网站已经初具雏形了。真是令人振奋啊。 下面我们对前面的工作作进一步的改进。

Engineering Task Card

Date: 2003-4-12___

Story #:_实现用户可以保存国际化设置及皮肤___      Software Eng: _紫龙____ Task Estimate: _5小时___

Task Description:

    1、实现用cookie保存设定
     2、实现可以在浏览器中自己设定语言类别
     3、记录全过程

 

Software Engineer’s Notes

Task Tracking:

DateDoneto douse timeComments
     
     
     

由于要实现在功能相对简单,所以估算的时间为5个小时,其中大部分应该是在更好的改进方面。首先我们要实现用户可以自己选择显示语言而不依赖于浏览器来自动选,实现上在Struts里实现这一点很简单。我们先建立一个jsp来显示语言选择

<%@ page language="java" errorPage="/common/error.jsp" 
      contentType="text/html;charset=UTF-8" %>
<%@ page import="java.util.Locale"%>
<%@ include file="/common/taglibs.jsp" %>
<html:form name="changeCountry" action="/control/changeCountry.do" type="org.apache.struts.action.DynaActionForm">
<select name="Country" >
<%
Locale locale=new Locale("");
Locale l[]=locale.getAvailableLocales();
%>
<%=l.length%>
<%
for (int i=0;i<l.length;i++){%>
<% if (!"".equals(l[i])){ %>
<option value="<%=l[i]%>" <%if(l[i].equals(request.getSession().getAttribute("org.apache.struts.action.LOCALE"))){ %>selected <%}%>><%=l[i].getDisplayName((Locale)request.getSession().getAttribute("org.apache.struts.action.LOCALE"))%></option>
<% }}
%>
</select>
<input type="checkbox" name="remember" ><bean:message key="commons.remember"/>
<p>
<div><html:submit property="validate"> <bean:message key="button.submit"/></html:submit></div>
</html:form>

实现一个很简单的设计,自动列出所有的支持的国际语言列表,并通过选择来实现这个修改。然后实现一个action来完成修改

public ActionForward execute(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
if (request.getParameter("Country") != null) {
Locale[] locale = Locale.getAvailableLocales();
int i = 0;

try {
for (i = 0; i < locale.length; i++) {
if (locale[i]
.toString()
.equals(request.getParameter("Country"))) {
break;
}
}

if ("on".equals(request.getParameter("remember"))) {
WebUtils.setCookie(
response,
ComponentConstants.LOCALE_KEY,
locale[i].toString());
}

WebUtils.setLocale(request, response, locale[i]);
log.info(locale[i]);
} catch (Exception e) {
ActionErrors errors = new ActionErrors();
errors.add(
ActionErrors.GLOBAL_ERROR,
new ActionError(StackTraceUtil.getStackTrace(e)));

saveErrors(request, errors);

return mapping.findForward("failure");
};
}

return mapping.findForward("success");
}

功能很简单,通过比较列表,找到用户所选择的语言,并将其保存到session和cookie中去。保存到session通过一个setLocal方法

public static void setLocale(
HttpServletRequest request,
HttpServletResponse response,
Locale locale) {
request.setAttribute(ComponentConstants.LOCALE_KEY, locale);

HttpSession session = request.getSession(true);

if (session != null) {
session.setAttribute(ComponentConstants.LOCALE_KEY, locale);
}
}

其中ComponentConstants.LOCALE_KEY是关键,通过这个值可以指定struts的语言。然后通过一个Filter来实现这种设置的正确性

if ((((HttpServletRequest) request).getSession(false) != null) &&
(((HttpServletRequest) request).getSession(false)
.getAttribute(ComponentConstants.LOCALE_KEY) != null)) {
;
} else {
Cookie cookie = WebUtils.getCookie((HttpServletRequest) request,
ComponentConstants.LOCALE_KEY);

if (cookie != null) {
Locale locale = new Locale(cookie.getValue());

if (locale != null) {
WebUtils.setLocale((HttpServletRequest) request,
(HttpServletResponse) response, locale);
}
}
}

判断在session中语言的设置是否存在,如果不存在,则从cookie中取得设置,并设置在session中。同样,通过对layout程序的改进来实现cookie。在LayoutSettingsAction类中加入这个功能。然后我们实现了我们的目标。OK,让我们来看一看效果下载。然后我们统计一下所花费的时间,4小时33分。差别是半小时左右。当技术是我们所熟知的时候,我们的估计越来越准确了,值得祝贺。这是所有的代码下载。总结一下,我们完成的功能是完全国际化,动态模板及相关的网页设置功能。有关网页这一层的基础工作我们大致已经完成,从明天开始,我们就可以开始系统级的设置了。:)


不知不觉这个系列已经写到7了,可是我却发现离完成这个小项目的目标还很远,离揭开struts很多好的功能也还很远。可是我却停止这个工作很长时间了,看了一个时间表,上一次完成的时候是四月十三号,今天已经是五一的晚上了。这段时间里有很多朋友在QQ上或发mail给我,问我这个小东西什么时候完成,什么时候能出下一篇。今天终于开始了这一篇的工作。其实中止这个东西的原因主要是我最近找了一份工作,在上海,去过很多公司,希望找到一个能让我尽情发挥的地方。让我很遗憾的是,居然我有接近五年的IT经验,有非富的开发和项目的实践,却很多地方要么是因为我仅仅只有一年的JAVA经历而把我拒之门外,要么就是看中了我的delphi啊,VB甚至是ASP的能力而希望我仍然从事这些东西。却不知,做delphi,对我来说只是一个体力活而已,而JAVA却能给我更多的精益求精的机会,同时我也相信能给公司带来更多的好处。所以,回到了无锡,选择了这个能够给我完全信任的公司,想不到,大上海的包容能力不如小小的无锡。所以这段时间投入在工作上的精力更多,以至于回到家来不想再碰一下计算机了。

好了,闲话少扯。先完成一个小小的功能做一下热身吧,毕竟好久没有碰过了,有些要回忆回忆了。有一位朋友就问我,既然称国际化,为什么选择中文以后,菜单没有用中文来显示。这个功能呢,很早的一个版本我已经实现了,不过被我注释掉了,细心的朋友可能已经知道怎么做了,但是热身吧,就把这个问题详细的说一下吧。

Engineering Task Card

Date: 2003-4-12___

Story #:_实现模板定义的全中文化___      Software Eng: _紫龙____ Task Estimate: _1小时___

Task Description:

    1、中文的界面全部用中文
     3、记录全过程

 

Software Engineer’s Notes

Task Tracking:

DateDoneto douse timeComments
2003-5-11、3   
     
     

打开tiles-defs.xml,看到了

<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE tiles-definitions PUBLIC
"-//Apache Software Foundation//DTD Tiles Configuration 1.1//EN"
"http://jakarta.apache.org/struts/dtds/tiles-config_1_1.dtd">

<tiles-definitions>
<definition name="tiles.root" path="/controller/layoutSwitch.do">
<put name="userSettingsName" value="tiles.skin.user.settings" />
<put name="catalogSettings" value="available.skins" />
<put name="layout.attribute" value="page.layout" />
<put name="title" value="title need to be updated(from root)" />
<put name="header" value="/pages/head.jsp"/>
<put name="footer" value="/pages/foot.jsp"/>
<put name="menu" value="tiles.menu.bar" />
</definition>


<!-- Menu bar definition -->
<!-- Some definition come from other definition files (ex : doc.menu.*) -->
<definition name="menu.root" path="/layouts/menu.jsp" >
</definition>
<definition name="tiles.menu.bar" path="/layouts/vboxLayout.jsp" >
<putList name="list" >
<add value="menu.links" />
<add value="menu.settings" />
<add value="menu.admin" />
<!--<add value="menu.userMenu" />


-->
</putList>
</definition>
<!-- main menu definition -->
<definition name="menu.links" extends="menu.root" >
<put name="title" value="Links" />
<putList name="items" >
<item value="Home" link="/index.jsp" />
</putList>
</definition>
<!-- admin menu definition -->
<definition name="menu.admin" extends="menu.root" >
<put name="title" value="admin" />
<putList name="items" >
<item value="reload" link="/admin/tiles/reload.do" />
<item value="view" link="/admin/tiles/view.do" />

</putList>
</definition>
<!-- Preferences menu definition -->
<definition name="menu.settings" extends="menu.root" >
<put name="title" value="Preferences" />
<putList name="items" >
<item value="my Layout Settings" link="/control/SkinSettings.do" />
<item value="my country Settings" link="/control/changeCountry.do" />
<item value="reset all setting" link="/admin/tiles/clearcookie.do" />
</putList>
</definition>
<!-- changeCountry main page -->
<definition name="country.settings.page" extends="tiles.root">
<put name="title" value="Country setting" />
<put name="body" value="/skinsetting/selectCountry.jsp"/>
</definition>
<!-- Skin main page -->
<definition name="skin.settings.page" extends="tiles.root">
<put name="title" value="Skin Setting" />
<put name="body" value="skin.settings.body"/>
</definition>
<!-- Skin body declaration-->
<definition name="skin.settings.body" path="/skinsetting/skinSettings.jsp"
controllerUrl="/controller/SkinSettings.do" >
<put name="userSettingsName" value="tiles.skin.user.settings" />
<put name="catalogSettings" value="available.skins" />
</definition>
<!-- Available skins -->
<definition name="available.skins" >
<putList name="skin.list" >
<add value="default.skin" />
<add value="menuright.skin" />
</putList>
</definition>

<!-- Default skin values -->
<definition name="default.skin" >
<put name="skin.label" value="Default" />
<put name="page.layout" value="/layouts/classicLayout.jsp" />
<put name="menu.layout" value="/layouts/menu.jsp" />
<put name="menuBar.layout" value="/layouts/vboxLayout.jsp" />
</definition>

<!-- Default skin values -->
<definition name="menuright.skin" extends="default.skin" >
<put name="skin.label" value="Right Menu" />
<put name="page.layout" value="/layouts/skin1/menuLeftLayout.jsp" />
<put name="menu.layout" value="/layouts/menu.jsp" />
</definition>
</tiles-definitions>

蓝色的地方就是我们界面上显示的内容,那么怎么改成可以动态选择呢,其实根本的道理就是JAVA的i18n,所以我们需要一个tiles-defs_ZH.xml

把上面的内容copy进去,修改成这样

<?xml version="1.0" encoding="GB2312" ?>

<!DOCTYPE tiles-definitions PUBLIC
"-//Apache Software Foundation//DTD Tiles Configuration 1.1//EN"
"http://jakarta.apache.org/struts/dtds/tiles-config_1_1.dtd">

<tiles-definitions>
<definition name="tiles.root" path="/controller/layoutSwitch.do">
<put name="userSettingsName" value="tiles.skin.user.settings" />
<put name="catalogSettings" value="available.skins" />
<put name="layout.attribute" value="page.layout" />
<put name="title" value="title need to be updated(from root)" />
<put name="header" value="/pages/head.jsp"/>
<put name="footer" value="/pages/foot.jsp"/>
<put name="menu" value="tiles.menu.bar" />
</definition>


<!-- Menu bar definition -->
<!-- Some definition come from other definition files (ex : doc.menu.*) -->
<definition name="menu.root" path="/layouts/menu.jsp" >
</definition>
<definition name="tiles.menu.bar" path="/layouts/vboxLayout.jsp" >
<putList name="list" >
<add value="menu.links" />
<add value="menu.settings" />
<add value="menu.admin" />
<!--<add value="menu.userMenu" />


-->
</putList>
</definition>
<!-- main menu definition -->
<definition name="menu.links" extends="menu.root" >
<put name="title" value="链接" />
<putList name="items" >
<item value="主页" link="/index.jsp" />
</putList>
</definition>
<!-- admin menu definition -->
<definition name="menu.admin" extends="menu.root" >
<put name="title" value="管理" />
<putList name="items" >
<item value="重载" link="/admin/tiles/reload.do" />
<item value="查看" link="/admin/tiles/view.do" />

</putList>
</definition>
<!-- Preferences menu definition -->
<definition name="menu.settings" extends="menu.root" >
<put name="title" value="个人设置" />
<putList name="items" >
<item value="个人皮肤设置" link="/control/SkinSettings.do" />
<item value="个人语言设置" link="/control/changeCountry.do" />
<item value="取消cookie" link="/admin/tiles/clearcookie.do" />
</putList>
</definition>
<!-- changeCountry main page -->
<definition name="country.settings.page" extends="tiles.root">
<put name="title" value="语言设置" />
<put name="body" value="/skinsetting/selectCountry.jsp"/>
</definition>
<!-- Skin main page -->
<definition name="skin.settings.page" extends="tiles.root">
<put name="title" value="皮肤设置" />
<put name="body" value="skin.settings.body"/>
</definition>
<!-- Skin body declaration-->
<definition name="skin.settings.body" path="/skinsetting/skinSettings.jsp"
controllerUrl="/controller/SkinSettings.do" >
<put name="userSettingsName" value="tiles.skin.user.settings" />
<put name="catalogSettings" value="available.skins" />
</definition>
<!-- Available skins -->
<definition name="available.skins" >
<putList name="skin.list" >
<add value="default.skin" />
<add value="menuright.skin" />
</putList>
</definition>

<!-- Default skin values -->
<definition name="default.skin" >
<put name="skin.label" value="默认" />
<put name="page.layout" value="/layouts/classicLayout.jsp" />
<put name="menu.layout" value="/layouts/menu.jsp" />
<put name="menuBar.layout" value="/layouts/vboxLayout.jsp" />
</definition>

<!-- Default skin values -->
<definition name="menuright.skin" extends="default.skin" >
<put name="skin.label" value="菜单居右" />
<put name="page.layout" value="/layouts/skin1/menuLeftLayout.jsp" />
<put name="menu.layout" value="/layouts/menu.jsp" />
</definition>
</tiles-definitions>

注意,除了内容改成中文以外,xml的encoding要改成"GB2312", 否则会产生解析出错的。然后我们看一下效果,就能发现菜单也能根据设置变化了,只是一个地方没有变,就是右边的皮肤选择还是英文的。这个地方需要改一个java程序了,找到LayoutSwitchAction.java有一句

if (catalog == null) { // create catalog

....}

而这个catalog是定义成static的,所以只会第一次被调用的时候才会去读设置,以后就不变了。我们把这一句去掉就可以了。再看效果,就能看到如下结果了:

选英文的时候是

OK,大功告成了。把文件打一个包,下载。不过由于这里的改动并不大,希望大家在上版的地基础上自已动手喔。另外听很多网友说,这个包在jb下可能运行有问题,看原因是有些东西编译不过去。不过大多数编译出问题的文件都可以delete掉的。同时也希望哪位能够提供一个可以直接在JB下使用的project,方便大家。好了,看一下时间,54分钟。等于预测。呵呵。

:)。明天会有一个大的功能的研究,就是struts里的validation,一个我觉得非常非常好的功能。好吧,明天见。



早上起来,空气清新,真是干活的好日子。今天开始写validation的功能,本来的计划是这个应该放在后面再写的,毕竟我们的结构还没有完全完成呢。但应一位网友的要求,就先写在前面吧,说起validation,可是struts1.1提供的好功能啊,作为我个人来讲是非常非常喜欢它的,因为它帮我解决了输入合法性的检查,并且是一次书写,到处可以。总之,一句话,实用。

Engineering Task Card

Date: 2003-4-12___

Story #:_实现一个validation的实例___      Software Eng: _紫龙____ Task Estimate: _2小时___

Task Description:

    1、实现完整的实用的validation的实例
    2 、记录全过程

 

Software Engineer’s Notes

Task Tracking:

DateDoneto douse timeComments
2003-5-21.2   
     
     

对于struts应用来讲,validation的实现,主要在涉及三个部分。一是validator-rules.xml和validator.xml(这个文件名可以自定义),在JSP中加入taglib,实现客户端检查。二在actionForm中的validate函数中实现更复杂的检查。三在action的execute(这个函数也可以自定义)函数中实现可能会更多的检查。首先,使用第一种方法,可以做到的是简单的检查,比如说,某一个输入必填、数据长度不能少于多少、日期格式应该是怎么样的等等吧(因为它支持正则表达式),第二种检查是struts1.0里的方法,现在从类的定义上来讲,在actionForm中检查可能并不太适合,如果又更复杂的检查,比如从数据库中读出一些内容来,与输入值比较以确定输入合法之类的,应该放在Action里更好一点。下面我们先做一下第一种情况是怎么实现的(另外两种的实现很简单,只是写java代码,相信大家都会)。

要实现validation功能,就要在strut-config中加入一个plug-in,比如我们看struts-strutstest-config.xml中

plug-in className="org.apache.struts.validator.ValidatorPlugIn">
<set-property property="pathnames" value="/WEB-INF/validator-rules.xml,/WEB-INF/validationtest.xml" />
</plug-in>

其中蓝色字就是配置文件,此处的rules使用默认的文件名,而说明文件则自定义,这里是validationtest.xml。为简单起见,我们在research目录下建立一个testval.jsp文件,其内容如下,其中借用了login的一个form。:),我是比较懒的。

<%@ page language="java" errorPage="/common/error.jsp" contentType="text/html;charset=UTF-8" %>
<%@ include file="/common/taglibs.jsp" %>
<html:form action="/testval.do" >
<table width="300" border="0" style="font-family: arial, helvetica; font-size: 12px;">
<tr>
<td colspan=2><html:errors/></td>
</tr>
<td colspan=2>
test validation<br>&nbsp;
</td>
</tr>
<tr>
<td align="right"><bean:message key="prompt.login"/>&nbsp;</td>
<td align="left"><html:text property="login" size="20"/></td>
</tr>
<tr>
<td align="right"><bean:message key="prompt.password"/>&nbsp;</td>
<td align="left"><html:text property="passwd" size="20"/></td>
</tr>
<tr>
<td>&nbsp;</td>
<td align="lerf">
<html:submit>
<bean:message key="button.submit"/>
</html:submit>
<html:reset>
<bean:message key="button.reset"/>
</html:reset>
</td>
</tr>
</table>
</html:form>

只不过action改成了/testval.do,然后我们来进行其他的配置。

首先修改struts-strutstest-config.xml,加入一个form-bean。这是一个动态Form(这也是struts1.1的新功能,让我们不必定义一个javaBean了),

<form-bean name = "testvalform"
type = "org.apache.struts.validator.DynaValidatorForm">
<form-property name="login" type="java.lang.String"/>
<form-property name="passwd" type="java.lang.String"/>
</form-bean>

然后加入两个action

<action path= "/testval"
type= "org.apache.struts.actions.ForwardAction"
name= "testvalform"
scope= "session"
validate= "true"
input= "/testvalinput.do"
parameter="/pages/Welcome.jsp"
>
</action>

<action parameter="/research/testval.jsp" path="/testvalinput" type="org.apache.struts.actions.ForwardAction" />

第一个atcion就是/testval.do,在参数中指定form的名字是testvalform,并且validate=true。input是/testvalinput.do,也就是下面这个action。这样,如果成功,就跳到Welcome.jsp,如果不成功,就返回输入页面。

最后我们需要在validationtest.xml中加入

<form name="testvalform">
<field
property="login"
depends="required">
<arg0 key="prompt.login"/>
</field>
<field
property="passwd"
depends="required,mask">
<arg0 key="prompt.password"/>
<var>
<var-name>mask</var-name>
<var-value>^[0-9a-zA-Z]*$</var-value>
</var>
</field>
</form>

这样,就能够自动来判断输入的内容了。其中<arg0 key="prompt.password"/>中的key值是提示的值,也是从资源文件中取得的。

启动tomcat,在浏览器中输入http://localhost/strutsacl/strutstest/testvalinput.do, 看到如下界面。

直接按提交,得到结果是这样的。

有人就问了,“登录名”和“密码”从哪里出来我是知道的,那么后面怎么会自动提示"是必须的"呢?答案是仍然从资源文件里出来的,打开application.properties文件,看到

errors.required={0}/ is/ required.(我们没有打开application_ZH.properties,是因为里面的内容看不了)。{0}代表这个内容会被传递过来的第一个参数替换掉,也就是我们前面看到的<arg0 key="prompt.password"/>所代表的实际内容。同样,errrors.有一个系列。比如errors.date={0}/ is/ not/ a/ date.。表示不是一个合法的时间。还有很多,自已去研究吧。到这里为止,我们已经领略一下validation的功能了。但是它还有更强的功能,那就是客户端检查。而实现这个功能,做起来却非常简单。打开jsp文件,在里面加入一行,修改一行

<html:javascript formName="testvalform" method="validate"/>
<html:form action="/testval.do" οnsubmit="return validate(this)" >

甚至不用重启tomcat,刷新刚才的页面,呵呵,计划没有成功,查了半天,发现了问题的来源,在我们的taglibs.jsp文件里有一句<html:xhtml />,影响了<html:javasrcipt>,把这一句去掉,再刷新,按提交的时候就能看到下面的样子:

查看源码,发现有一长段script

<script type="text/javascript" language="Javascript1.1">

<!-- Begin

var bCancel = false;

function validate(form) {
if (bCancel)
return true;
else
return validateRequired(form) && validateMask(form);
}

function required () {
this.aa = new Array("login", "登录名 是必须的.", new Function ("varName", " return this[varName];"));
this.ab = new Array("passwd", "密码 是必须的.", new Function ("varName", "this.mask=/^[0-9a-zA-Z]*$/; return this[varName];"));
}

function mask () {
this.aa = new Array("passwd", "密码 非法.", new Function ("varName", "this.mask=/^[0-9a-zA-Z]*$/; return this[varName];"));
}

......

}

//End -->
</script>

 

这么长如果是我自己写的话,我想一定要写死了。下面再加一个时间的检查就结束吧,由于碰到这个怪怪的问题,导致时间多花了一倍还多。

在JSP中加入

<tr>
<td align="right">date&nbsp;</td>
<td align="left"><html:text property="logindate" size="20" /></td>
</tr>

在config里加入

<form-property name="logindate" type="java.lang.String" />

在validationtest里加入

<field
property="logindate"
depends="required,date">
<arg0 key="logindate" resource="false"/>
<var>
<var-name>datePatternStrict</var-name>
<var-value>yyyyMMdd</var-value>
</var>
</field>

得到下列结果

实际结果就是只有输入20020102这种日期类型时,才能够成功。看一下时间,总共使用了5小时24分,远超过计划。这是由于碰到了一两个意外,这也就是导致对时间的预计不准确的最大因素。当然,其深层次的原因是我对struts并不是那么的熟。在这一部分,加入了DynaForm,这也是struts1.1的新功能,值得花时间去学习的,而在此处,因为加入这个,所以也导致速度多花费时间。不管如何,我们完成了这个实现。下载源码(注,为了减小包文件,把jar文件去除了,需要jar文件,从前面的包里下载,它的大小只有136K)

更详细的内容请参看struts-documentation/userGuide/dev_validator.html



struts教程的一些FAQ

在这个FAQ之前,首先要说一些自己的感受。首先,作为一个程序,当然会选择一些相对成熟的东西,其实,很多支持软件的安装、使用过程并不在这个教程的范围之内。这不是我在推卸责任,比如说JDK如何安装使用,这个SUN当然会提供足够的资料,又比如tomcat、elcipse和rbmanager这些在IBM的网站都有很详细的介绍,而本教程主要涉及的当然是跟struts非常相关的内容。如果所有的内容都要这个教程来完成,恐怕这个教程将变成长篇大论(而长篇大论在我的概念中往往是垃圾很多),因此,在学习和练习本教程前,希望大家先把基本的知识学完。其次,就我个人的实践来看,出现问题并解决问题,在这个过程中所学到的,远远比简单的完全按照一个教程全部完成来得多。因此,在此呼吁一句,享受出现问题这个过程吧。

1、我在按这个教程学习的时候,吃了不少苦头,大多错误都是由于我所使用的软件的版本与这个教程中所采用的不一样。像我们初学者,也不知道哪个版本最适合,所以去下载的时候,都是下的最新版本,结果各个软件之间的兼容性并没有那么好(比如用TOMCAT5.x就会出错,换成4.x就好了)。而这种错误是比较隐蔽的,不容易发现。就算发现了,重新安装,一切又要从头开始。所以在一开始确定软件的版本是很重要的。
可以在这里加入下面的话:
本教程中所使用的软件及版本号如下:
jdk1.x.x 下载地址:http://xxxxxxxxxxxxxxxxxxx
eclipse 2.x.x 下载地址:http://xxxxxxxxxxxxxx
eclipse的tomcat插件 2.x.x 下载地址:http://xxxxxxxxxxxxxxxxx
tomcat 4.0.6 下载地址:http://xxxxxxxxxxxxxxxxxxxxx
struts 1.1.x 下载地址:http://xxxxxxxxxxxxxxxxxxxxxxxx
rbManager x.x.x 下载地址:http://xxxxxxxxxxxx
oracle x.x.x 下载地址:http://xxxxxxxxxxx,这个太多,最好去买光盘。

这里是不是还应该把每个软件的安装过程,及成功之后的样子也简单说一下?

还有,那个RBMANAGER我下载的是0.6的版本,它运行之后,它的软件包里一个.jar的位置放错了,而且也没有rbmanager.bat这个开始文件,只有report.bat,我开始时候弄了好久才发现。还有,它的菜单中“导入”的那个选项(是个二级菜单),点了没有反应,使我没有办法更改国际化文件。不知道是不是因为我的JDK或JRE的版本问题(我的是1.4.2)。

我觉得这很重要,因为看这个教程的大多是初学者,遇到了困难并没有相当的经验知道怎么去排除,一般只能照着教程一步一步地做下去。

这里jdk应该是1.4以上的版本都没有关系的,eclispe2.0以上的版本出没有问题,tomcat当然是4以上的版本,rbManage从http://oss.software.ibm.com/icu4j/demo_tools/RBManager.html下载,这次下载了一个新版本,确实没有发现rbmanage.bat这个文件,那是因为RBManager.jar已经做成可执行的了,如果你的JAVA安装正常的话,双击就可以运行(如果不行,选择用javaw.exe打开它),,当然,这是一个0.6的版本,有很多功能没有完成也很正常

2、5、数据库服务器,目前机器上已经安装好了oracle,因此选择它啦,数据库服务器的安装也ok了(为什么要选择oracle?我光安装就花了一天,装好了还不会用。既然面对初学者,是否还应该提供一个ACCESS的版本?只是数据库的问题,与STRUTS的关系应该不大吧。)

实际上这个教程还到第10篇没有跟数据库真正发生关系,因些不装也罢。至于选择Oracle,理由比较简单,oracle跟java一样是跨平台的。我需要的系统,就是不管哪一个操作系统,都能运行。而Access这样的数据库,其JDBC驱动开发得并不好,所以不考虑。

3、插件怎么安装?应该简单说一下,还有注意的地方,比如要把一个.rar下的东西拷到tomcat的server/classes下

插件的安装很简单,只要copy到eclipse的plug-in目录下就可以了,并没有什么内容需要copy到tomcat目录下的

4、这里有一个大问题!!在你重新规化目录以前,strutsacl目录下只有web-inf和work等几个目录,而且http://localhost:8080/strutsacl 指向的也是strutsacl目录。而你现在把strutsacl/web当作发布目录,也就是说使http://localhost:8080/strutsacl指向了strutsacl/web,这是怎么做到的呢?应该更改哪些东西?还有,要更改的地方应该不止一个吧,这都应该详细地说明一下。。我是琢磨了好久也没有弄好。

 

这里的builderPath,可能不熟悉elcipse的朋友会碰到问题。下面的图应该可以解释这个问题,首先在strutsAcl上点鼠标右键。点最后一项,properties,然后在弹出窗口中选择java builder path看到以下画面

在上面,有source栏,这些在这里列出的目录,都会被编译到web/web-inf/classes目录下。为什么是这个目录,看右下角的default output folder。

还有tomcat的设置,也是一样,只要设置同下图一样,然后在strutsAcl在点右键,选择tomcat菜单下的update context in server.xml.就ok了

还有问我改变了资源文件的路径,为什么能够成功。这是因为struts-config.xml中这一句

<!-- ========== Message Resources Definitions ============================ -->
<message-resources parameter="resources.application" />

这个参数指定了这个资源文件所在目录,以及文件名

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页