使用何种技术
我使用tomcat 4的servlet/jps容器技术实现一个web应用程序。这个过程中仍然有许多不确定的事,在多种可行的技术中选一种可不是一件容易的事。这篇文章中,我选择尽量简单的解决方案,混合使用java server pages(jsps)以及java。
应当尽量避免使用jsps实现复杂逻辑;那种程序也许容易写,但难以调试而且几乎不可能被理解和维护。好的解决方案是用jsps负责web页面的显示(这正是jsps擅长的),把复杂逻辑的实现交给java,如对数据库的访问。这样,程序不但比较容易实现和调试,而且易于理解和维护。
这个web应用程序可以运行在专用web服务器或者是个人电脑上,操作系统可以是windows,linux,或者mac os。所需的软件包有java运行时环境(jdk1.2或以上版本),tomcat最新版本(tomacat 4或以上版本),以及ant开发工具(ant 1.4或以上版本)。ant用于创建web应用程序包(war),以及在tomcat上安装部署war文件,配置关系数据库接口javax.sql.datasource。所有以上提到的软件包都可以通过internet免费获得。
这个web应用程序还需要一个数据库。几乎所有支持sql而且有jdbc驱动的关系数据库都可以。mysql是一个符合条件的不错选择。从mysql.com可以得到mysql的最新版本以及它的jdbc驱动connector/j。
为了让ant与tomact管理软件协同工作,需要将catalina-ant.jar从tomcat的server/lib目录拷贝到ant的lib目录下。将你的数据库jdbc驱动程序复制到tomcat的common/lib目录下使tomcat和其上的web应用程序可以访问数据库。最后,必须建立tomcat的admin和manager角色(roles),及其用户名和密码,编辑tomcat的conf目录下的tomcat-user.xml文件如下:
<?xml version=1.0?> <tomcat-users> <role rolename="admin"/> <role rolename="manager"/> <user username="tomcatusername" password="tomcatpassword" roles="admin,manager"/> </tomcat-users>
我的开发环境是apple cube,运行mac os x 10.2.1操作系统,软件包jdk 1.3.1,tomcat 4.1.12,ant1.5.1,mysql 3.23.52,及connector/j 3.0.1-beta。操作系统升级到mac os x 10.2.2未出现任何问题。同样tomcat升级到4.1.17也没出现问题。
web应用程序
本文通过一个叫做addressbook的简单地址簿程序来说明如何应用将要介绍的技术。这个应用程序的目的不是建立一个地址簿的正式版本,它仅仅是一个例子。
addressbook中的java程序: addressbook中包含三个java程序。图1显示了他们在tomcat中的是如何使用的。
addressbook.contextlistener: addressbook.contextlistener是一个servlet上下文监听器,它在addressbook开始和关闭时被调用,可以使用addressbook的部署描述文件web.xml配置。当addressbook开始运行时,创建一个addressesdb的实例并将它作为一个上下文属性保存;而当addressbook运行结束时,从上下文属性中取出addressesdb对象并关闭数据库连接。在运行过程中,当jsps需要连接数据库,它们将访问上下文属性中的addressesdb对象。细节请看contextlistener.java的完整的带有注释的源代码。
addressbook. addressesdb: addressbook. addressesdb用于操作地址数据库。它的构造函数建立一个数据库连接,这个会话可以被多个web会话共享。这个类提供多个数据库连接函数:
getaddress(id)返回用id标识的地址,如果地址没找到则返回null。
addaddress(address)添加地址并返回改变的行数
deleteaddress(id)删除地址并返回改变的行数
getaddresses()返回数据库中所有地址的聚集,如果数据库无效返回null
close()关闭数据库连接
细节请看addressesdb.java的完整的带有注释的源代码。
addressbook. address: addressbook. address是用于描述地址的类。在用jsps编程时,用类集中描述数据仍不失为一个好的方法。在addressbook中使用的就是这种方法。addressbook. address的构造函数将地址内容保存于对象中。这个类中包括取得单个地址域的普通get函数,两个获取合并的地址域的get函数,以及一个地址比较函数:
address(id,surname,fisrtname,street,district,city,postcode)
getid()
getsurname()
getfirstname()
getstreet()
getdistrict()
getcity()
getpostcode()
getfullname()返回firstname+” ”+surname
getfulladdress()返回street+” ”+district+” ”+city+” ”+postcode
compareto(address)返回负整数、零、正整数,分别对应surname和firstname大于、等于、小于这个surname和firstname,比较是大小写不敏感的。在存储地址聚集时使用。
细节请看address.java的完整的,带有注释的源代码。
addressbook中的jsp页面: addressbook有七个jsp页。每个代表一个对addressbook数据库进行独立操作的web页。每个页面数据库的状态与前页数据库的状态是相互独立的。如果数据库被其他用户改变了,web页面将检测到并产生相应的动作。例如,如果你正在修改一个地址而另一个用户在确认修改之前删除了该地址,jsp将通知你修改不存在的地址失败。
图2显示jsps之间的逻辑关系。
home.jsp: home.jsp是addressbook的主页,也就是用户使用addressbook时见到的第一个页面。它用一个表显示address库中的所有地址。表中的每一行显示一个地址,以及删除或修改地址的连接。页面底部有一个添加新地址的连接。配置信息见web.xml notes。
添加地址连接将控制权交给requestadd.jsp
删除地址连接将控制权交给requestdelete.jsp,同时将要删除的地址id传递给该页
修改地址连接将控制权交给requestmodify.jsp,同时将要修改的地址id传递给该页
细节请看home.jsp的完整的带有注释的源代码。
requestadd.jsp: requestadd.jsp提供一个地址表单用于输入新地址。页面底部的取消连接可以取消这次操作。
提交表单将新的地址域值以及控制权交给doadd.jsp
取消操作将控制权交给home.jsp。细节请看requestadd.jsp的完整的,带有注释的源代码。
doadd.jsp: doadd.jsp显示接收到的地址域值。然后将新地址添加到数据库并显示是否成功的信息。页面底部有一个继续连接。
点击继续连接回到home.jsp。细节请看doadd.jsp的完整的,带有注释的源代码。
requestdelete.jsp: requestdelete.jsp读取得到的id相应的地址。该页面在表中显示地址域以便让你确认删除的是正确的地址。页面底部的两个连接分别是继续请求的继续连接和取消请求的取消连接。
选择继续连接将转到dodelete.jsp并传递要删除的地址id
选择取消连接回到home.jsp。细节请看requestdelete.jsp的完整的带有注释的源代码。
dodelete.jsp: dodelete.jsp读取传递给它的id。在表中显示该id对应的地址,从数据库中删除它,并显示是否成功。页面底部是继续连接。
选择继续连接回到home.jsp。细节请看dodelete.jsp的完整的,带有注释的源代码。
requestmodify.jsp: requestmodify.jsp显示一个表单并填入当前的地址,同时提供提交按钮和取消连接。
提交表单将新的地址域值以及控制权交给domodify.jsp
选择取消连接回到home.jsp。细节请看requestmodify.jsp的完整的,带有注释的源代码。
domodify.jsp: domodify.jsp显示接收到的修改的地址域值。然后修改数据库中的地址记录,并返回成功与否。页面底部是继续连接。
选择继续连接回到home.jsp。细节请看domodify.jsp的完整的,带有注释的源代码。
addressbook的addresses数据库表: 如何建立数据库取决于所用的数据库软件。若你用的不是mysql以下的步骤就需要根据情况修改。addressbook只有一个表addresses,保存在数据库public中。表addresses有七个域:
id,主键,自动增长的数据域
surname,长度为24的字符域,保存联系人的姓
firstname,长度为24的字符域,保存联系人的名
street,长度为80的字符域,保存地址的第一行
district,长度为80的字符域,保存地址的第二行
city,长度为40的字符域,保存城市名
postcode,长度为10的字符域,保存邮政编码
首先建立mysql数据库(mac os x 10.2.1)。用管理员权限启动mysql命令行工具,需要的话输入密码。创建public数据库,以及账号mysqlusername密码mysqlpassord。命令如下:
# mysql -u root -pmysql> create database public;mysql> grant all privileges on public.* to mysqlusername@localhostidentified by mysqlpassword with grant option;mysql> flush privileges;
然后,在public数据库中创建addresses表。
mysql> create table addresses ( id int(8) primary key auto_increment, surname varchar(24) not null, firstname varchar(24) not null, street varchar(80) not null, district varchar(80) not null, city varchar(40) not null, postcode varchar(10) not null );
用命令commit;提交。要检查对表的操作是否正确,键入命令describe addresser;,将得到如下结果:
+———–+————-+——+—–+———+—————-+| field | type | null | key | default | extra |+———–+————-+——+—–+———+—————-+| id | int(8) | | pri | null |auto_increment|| surname | varchar(24) | | | | || firstname | varchar(24) | | | | || street | varchar(80) | | | | || district | varchar(80) | | | | || city | varchar(40) | | | | || postcode | varchar(10) | | | | |+———–+————-+——+—–+———+—————-+
现在可以插入一行来测试数据库
mysql> insert into addresses (surname, firstname, street, district,city, postcode) values ("smith", "john", "1, the high street,","downtown,", "metropolis.", "x99 9xx");mysql> commit;mysql> select * from addresses;
结果如下:
注意地址的id被自动赋予值1。至此addressesbook的数据库创建完成。
配置addresbook的开发目录
现在我们配置存放addressbook源文件的目录,以及web应用程序的war目录,它也是存放web应用程序包的目录。下载addressbook源程序压缩包。解压后将得到ant能够处理的源程序目录结构,如图3所示。不需要对addressbook的开发目录进行特殊改变?建议将其解压至你的home目录(mac os)。
让我们看看这些文件:
addressbook/build.properties: addressbook/build.properties含有若干被addressbook/build.xml读取得设置信息。定制build过程时,尽量编辑较小的build.properties文件,而不要修改复杂的多的build.xml文件。
app.name=addressbooktomcat.home=/usr/local/jakarta-tomcat-4.1.12manager.url=http://localhost:8080/managerusername=tomcatusernamepassword=tomcatpassword
app.name参数不需要修改。如果将来要重用这些文件创建新的web应用程序时把该参数改为新的程序名。如果在本地机器上运行tomcat 4,则manager.url参数也不必修改。
tomcat.home参数为tomcat的安装目录,以上的设置表示tomcat 4.1.12安装在/url/local目录下(mac os)。ant将根据这个参数设置编译addressbook时的classpath变量,使它包含tomcat的common/lib.jar文件。(这样tomcat 和web应用程序都可以直接访问这个文件,而不必将其复制到应用程序的web-inf/lib目录下。)
tomcat也有一个类似的目录:shared/lib,给目录下的.jar文件在运行时对于应用程序是可访问的,但对tomcat是不可访问的。
参数tomcatusrename和tomcatpassword必须与tomcat的配置文件conf/tomcat-users.xml中的设置一致。ant使用它们以及manager.url取得在tomcat环境中安装addressbook应用的权限。
细节请看build.properties的完整带有注释的源代码。
addressbook/build.xml: addressbook/build.xml文件是ant的配置文件,它使用许多ant的标签。以下是比较常用的:
? ant build 创建web应用程序
? ant install 安装web应用程序用于测试
? ant remove 卸载web应用程序
? ant deploy安装部署web应用程序
? ant undeploy卸载web应用程序
细节请看build.xml的完整的带有注释的源代码。
addressbook/context.xml: addressbook/context.xml是在ant处理安装、重新载入、删除、部署、卸载请求时装入tomcat中的。给文件记录的是addressbook的上下文信息,或者是tomcat运行addressbook需要的参数。
logger项定义了tomcat用文本格式记录addressbook的事件日志,并存放于tomcat的logs目录下。以下logger项定义了名为localhost_addressbook_log.yyyy-mm-dd.txt的日志文件,其中yyyy-mm-dd为日志日期:
<logger classname="org.apache.catalina.logger.filelogger" prefix= "localhost_addressbook_log." suffix=".txt" timestamp="true"/>
resource和resourceparams项定义tomcat必须为addressbook创建一个叫jdbc/publicd的javax.sql.datasource。以下设置创建了一个javax.sql.datasource,其值为org.apache.commons.dbcp.basicdatasourcefactory类,url为jdbc:mysql://localhost:3306/public?autoreconnect=true,jdbc驱动:org.gjt.mm.mysql.driver,用户名和密码分别为:mysqlusername和mysqlpassword:
以上代码将javax.sql.datasource作为该web应用程序的上下文变量。如果该变量不止被一个web应用用到,可以将它定义为全局资源。细节请看context.xml的完整的带有注释的源代码。
addressbook/src/*.java: src目录中存放web应用的三个java程序。见address.java,addressesdb.java和contextlistener.java的描述。
addressbook/web/*.jsp: web目录存放web应用的七个jsp页面。见home.jsp, requestadd.jsp, doadd.jsp, requestdelete.jsp, dodelete.jsp, requestmodify.jsp, 和 domodify.jsp的描述。
addressbook/web/web-inf/web.xml: addressbook/web/web-inf/web.xml是addressbook的web应用部署描述文件。其中包含两个主要参数:listener参数定义了java类监听器addressbook.contextlistener,在程序启动和关闭是被调用。
<listener> <listener-class>addressbook.contextlistener</listener-class></listener>
welcom-file-list参数定义了addressbook的首页。如下设置定义addressbook的首页为home.jsp。
<welcome-file-list> <welcome-file>home.jsp</welcome-file></welcome-file-list>
细节请看web.xml的完整的,带有注释的源代码。
创建,安装,部署和运行addressbook: 安装addressbook是先将当前目录改为addressbook目录,然后运行ant的install命令:
$ cd addressbook$ ant install
如果安装失败,检查tomcat的conf目录下的tomcat-user.xml是否配置正确,以及catalina-ant.jar是否从tomcat的server/lib目录下拷贝到ant的lib目录。
注意ant install自动触发运行ant的若干个标签,依次为ant int,ant prepare,ant build和ant package:
? ant init初始化时间戳
? ant prepare在addressbook目录下创建war目录结构,包括:一个war目录,一个war/war-inf目录,一个war/web-inf/classes目录,及一个war/web-inf/lib目录。
? ant build创建web应用程序,包括将jsp文件拷贝到war目录下,将context.xml文件拷贝到war/meta-inf目录下,将web.xml文件拷贝到war/web-inf目录下,并编译java文件结果存入war/web-inf/classes目录下。
? ant package从war目录创建web程序包。web程序包是由jar工具创建的.jar文件。
? 最后,ant install根据war/meta-inf/context.xml的配置信息将web程序包安装到tomcat中。你可能注意到不需要登陆root权限或tomcat就可以安装addressbook,只要你的普通用户账号即可。ant用定义在addressbook/build.properties中的tomcatusername和tomcatpassword即可安全的执行以上过程。
安装过程因配置选项不同而不同,配置信息具体见addressbook/build.xml。图4显示了构成addressbook web应用程序的war目录结构以及各种文件。
在浏览器地址栏中输入地址http://localhost:8080/addressbook测试addressbook程序。应该得到如图5的显示结果。如果应用程序没有正确运行,先检查tomcat的common/lib目录下有没有数据库的jdbc驱动程序。如果数据库驱动正确应用程序仍然不正确运行,则检查tomcat的log目录下的日志文件,这些文本格式的日志文件将帮助你发现错误。
添加地址,然后修改它,再删除它,再添加一个地址。你将看到id持续自动增加。
开发过程
addressbook安装成功之后,tomcat调用addressbook.conextlistener.contextinitialized创建addressbook.addressesdb的一个实例。addressbook.addressesdb通过jdbc/public datasource建立数据库连接。addressbook.conextlistener.contextinitialized将addressbook.addressesdb的实例作为一个servlet属性(addressesdb)保存。
当addressbook的home.jsp第一次被调用,tomcat编译并执行它。home.jsp通过servlet属性addressesdb取得addressbook.addressesdb的实例,然后从数据库中读取地址并显示。其他jsp页面在第一次被使用时也要先编译再执行,读取数据库的方法同home.jsp。
ant stop用于停止addressbook程序。tomcat自动调用addressbook.contextlistener.contextdestroyed取得上面提到的servlet属性addressesdb和addressbook.addressesdb的实例,关闭数据库连接并删除servlet属性addressesdb。
ant start用于启动addressbook程序。tomcat自动调用addressbook.conextlistener.contextinitialized重复启动过程。
整个web应用程序的开发过程是这样的:修改程序;用ant install创建并安装;在浏览器中键入地址http://localhost:8080/applicationname(或点击刷新按钮)以测试web程序;用ant remove删除web应用程序的上下文信息;重复整个过程,直到调试完毕。当程序调试完毕后,使用ant deploy正式将你的程序配置到服务器上。在重起tomcat或者重起服务器后,你的应用程序就开始运行了。要将web应用程序从你的服务器彻底删除用ant undeploy命令。
tomcat manager
使用tomcat manager可以替代ant。在浏览器中键入地址http://localhost:8080/manager/html/list,将出现如图6所示的tomcat manager web页面。
或者,直接使用以下地址:
http://localhost:8080/manager/list
http://localhost:8080/manager/resources
http://localhost:8080/manager/roles
http://localhost:8080/manager/start?path=/addressbook
http://localhost:8080/manager/stop?path=/addressbook
http://localhost:8080/manager/remove?path=/addressbook
http://localhost:8080/manager/sessions?path=/addressbook
tomcat administrator
要登陆tomcat的基于web的administrator,使用地址http://localhost:8080/admin。在登陆画面中输入tomcat”admin”角色的用户名和密码,与”manager” 角色的相同为tomcatusername和tomcatpassword。tomcat-users.xml文件为admin,manager以及provider角色建立了相同的账号。为每个tomcat角色建立不同的账号是不必要的。
控制页面分为三个面板:头面板,树面板和数据面板。如图7所示:
树的分支可被展开和选定。依次展开service{tomcat-standalone} ->host{localhost}->context{/addressbook},最后addressbook的resource分支。注意不要将树底部的全局resource分支与addressbook的resource分支混淆。全局resource分支可以用来定义任何web应用程序都可用的全局资源。
addressbook的上下文面板
选择context{/addressbook}分支,将看到addressbook的上下文面板(如图8)。
你可以随意改变该面板的参数,主要是三种不同的debug level参数。要保存设置点击save按钮然后点击头面板中的commit changes按钮。
addressbook的data source面板
选择context{/addressbook}的resource分支中的data source分支,你将看到addressbook的data source面板。选择数据源jdbc/public显示javax.sql.datasource(如图9)。
你可以随意改变该面板的参数,包括data source url, jdbc driver class, user name, password, max. active connections, max. idle connections和 max. wait for connection参数。要保存设置点击save按钮然后点击头面板中的commit changes按钮。
如果你在你的个人电脑上运行web应用程序,这也许不重要,但是如果在作为公司产品的web服务器,这就很重要。这些参数可以被安全的改变,而且不是只有程序员能够更改,tomcat administrator同样可以。作为一个程序员我要记住许多账号和密码,我不想知道公司的产品数据库的账号和密码。本来这个账号和密码是必要的,但是有了javax.sql.datasource和tomcat 4的administrator服务,我就不必知道它们了。
ant用于在开发软件时建立web应用程序上下文,而tomcat administrator服务用在产品环境中。
addressbook的日志面板
选择树中的context{/addressbook}分支的logger for context{/addressbook}分支,将看到如图10所示的addressbook的日志面板
你可以随意改变该面板的参数,主要是debug level和verbosity level参数。要保存设置点击save按钮然后点击头面板中的commit changes按钮。
