Java界技术名人堂

四月 18, 2005

Java界技术名人堂

上次在某个blog上面看到过一个关于Java人仕的综合介绍,人列了不少,不过讲的比较没有规律并且不是很全.今天在赛迪网上看到了这些资料,但是阅读起来很不方便,这里为其修改了其链接上的错误并将原来分散的资源做了一个综合.想知道更多的人还可以看看TheServerSide上面的这篇文章: Who’s Who in the Enterprise Java World? Make your opinion known! 里面断断续续的也讲了不少人^_^

A



Adrian Colyer

IBM的AspectJ DeveloperJava研究项目负责人,同时也是Eclipse上AspectJ项目的领导者。AspectJ项目起源于帕洛阿尔托(Palo Alto)研究中心(缩写为PARC)。该中心由Xerox集团资助,Gregor Kiczales领导,从1997年开始致力于AspectJ的开发,1998年第一次发布给外部用户,2001年发布1.0 release。为了推动AspectJ技术和社团的发展,PARC在2003年3月正式将AspectJ项目移交给了Eclipse组织,因为AspectJ的发展和受关注程度大大超出了PARC的预期,他们已经无力继续维持它的发展。

AspectJ是用于实现面向方面编程(AOP)的Java语言扩展。通过分离各种正交问题,AspectJ使得开发者能够更轻松地解决大型软件系统中常见的一些棘手问题,如日志记录、错误处理之类。

AspectJ项目经过近十年的发展,对于AOP的具体实现已经相当成熟,但其仍存在诸如过于复杂、破坏封装、需要专门的Java编译器等缺点。在接收了AspectJ项目之后,Eclipse项目组已经把AspectJ集成在Eclipse Java开发环境中。通过这样的集成,Eclipse IDE将可以提供对AOP的支持,并弥补AspectJ本身的一些不足之处。


B



Ben Galbraith

AOP活跃人士,其在TSSJS2005(全称:The ServerSide Java Symposium)上关于“AJax Toy”的演讲被与会者认为颇有新意。AJAX是Asynchronous JavaScript + XML的缩写,而且AJax可以很好的和JSF的Component结合起来,开发者不需要成为AJax方面的专家。


Benjamin Renaud

BEA的副CTO。BEA是企业级平台中间件的领导厂商,其2002财年收入达到9.34亿美元,在全球拥有超过15,000家企业用户。1995年,BEA收购Tuexdo联机事物处理监视器[Tuxedo online transaction processing(OLTP) monitor],并由此而进入平台中间件市场。1998年又收购了WebLogic,而WebLogic技术随后在1999年成为Java应用服务器领域中的领导者。


Bill Burke

JBoss组织首席架构师,EJB 3.0专家组里的JBoss代表。JBoss是一个用纯Java编写、开放源代码、遵循J2EE规范的应用服务器软件,它遵循LGPL开放源码协议。JBoss起初只是一个EJB容器,后来逐步发展成为一个具有复杂功能的应用服务器。现在,最高的版本是4.0。它从创建到现在,经历了4年的发展历程。JBoss组织是一家位于美国亚特兰大的商业服务组织。他们为JBoss应用服务器提供技术支持和顾问咨询。


Bob McWhirter

Groovy和Drools的开发者。Groovy是一种敏捷开发语言,完全以Java编程API为基础,于2004年3月底获得批准。Groovy还是与JRE兼容的一种脚本语言,使用了Java开发人员最熟悉的功能和库(将它们放到了一个敏捷开发框架中)。Groovy具有使开发人员不用编译、允许动态类型、使合成结构容易、脚本可在普通Java应用程序中使用、提供shell解析器等特性,这使其成为一种特别容易学习和使用的语言,不管是有经验的Java开发人员还是刚接触Java平台的人,都可以快速掌握。有些人说它可能会永久性地改变开发人员看待和使用Java平台的方式。

Drools是Bob McWhirter开发的开源项目,是Charles Forgy Rete算法的一个增强的Java语言实现,放在The Codehaus上。Rete算法是Charles Forgy在1979年发明的,是目前用于生产系统的效率最高的算法(除了私有的Rete II)。Rete是唯一的、效率与执行规则数目无关的决策支持算法。Rete应用于生产系统已经有很多年了,但在Java开源软件中并没有得到广泛应用。

除了应用了Rete核心算法、开源软件和100%的Java实现外,Drools还提供了很多有用的特性。其中包括实现了JSR94 API和创新的规则语义系统,这个语义系统可用来编写描述规则的语言。目前,Drools提供了三种语义模块,即Python模块、Java模块和Groovy模块。


C



Cedric Beust

EJB 3专家组的成员,也是其他一些流行的开源项目(例如EJBGen和Doclipse)的创造者。目前是BEA的Weblogic架构师。EJBGen是一种EJB 2.0原始码生成器,它能够从一个EJB bean类生成本地界面/远端界面、本地主界面/远端主界面、主键类和部署描述符。

Doclipse是一个Eclipse下的JavaDoc插件。

Java中的包(Package)其实指的就是目录,它是为了更好地管理Java类(Class)和接口(Interface)。Java语言的包可以被另一个Java开发包所使用。

EJB是SUN公司倡导的基于JAVA的组件构架,是J2EE中的主要技术之一。EJB定义了一个集成的编程模型和标准的API接口,是实现服务器端应用服务的组件。



Cliff Schmidt

Cliff Schmidt,标准和开源策略产品经理,也是Apache蜂巢项目负责人。蜂巢计划(Project Beehive)是一个基于 WebLogic Workshop应用框架的用于 J2EE&SOA 应用的开源项目。该框架本身是独立的,但要依靠WebLogic来运行。 在2004年5月,BEA宣布该框架以后的所有开发都是开放源码的,这就是Apache蜂巢项目,名为Beehive。


Craig McClanahan

Craig McClanahan目前是SUN公司的高级工程师,主要从事J2EE的WEB层的工作。主要参加的开放源码有:

1.Catalina Servlet Container:主要的开发人员,该Servlet Container是Apache软件基金的Jarkarta 中的TOMCAT 4.x和5.x版中的核心部分。

2.Struts: 发起人和主要的开发人员,2000年该项目转为Apache软件基金的Jarkarta项目,该框架目前成为采用Java Servlet 和JSP技术构件Web应用的最流行的框架。

3.Digester:发起人和主要的开发人员,本来该项目是为Struts中处理配置文件的一些功能类,后来觉得不错,就转成Apache软件基金的Jarkarta项目的Common中的一个组件项目。

4.JavaServer Faces(JSR-127), 和SUN公司的Ed Burns一起负责该标准。

著作:《Struts in action》


D



David Nuescheler

Day Software的CTO,也是Java Content Repository(Java内容录仓库)JCR 170专家组负责人。Java Content Repository是一个寻址内容仓库的标准化API建议。2002年2月,该建议提交到JCR(Java Community Process),JCR专家组于2003年下半年完成最终草案。


Dion Almaer

TSS主编、中间件公司首席架构师、JDO专家组成员。Middleware Company (www.middleware-company.com)即中间件公司是领先的EJB/J2EE和B2B技术培训机构之一。Middleware Company还构建和维护一流的在线J2EE社区TheServerSide.com。


Doug Cutting

TSS的CEO、MiddleWare公司CEO、《Mastering EJB》作者。他是一位资深全文索引/检索专家,曾经是V-Twin搜索引擎(Apple的Copland操作系统的成就之一)的主要开发者,后在Excite担任高级系统架构设计师,目前从事于一些INTERNET底层架构的研究。他贡献出的Lucene的目标是为各种中小型应用程序加入全文检索功能。Lucene不是一个完整的全文索引应用,而是是一个用Java写的全文索引引擎工具包,它可以方便的嵌入到各种应用中实现针对应用的全文索引/检索功能。早先发布在作者自己的www.lucene.com,后来发布在SourceForge,2001年年底成为APACHE基金会jakarta的一个子项目:http://jakarta.apache.org/lucene/

E



Erich Gamma

经典书籍《Design Patterns(设计模式)》(Addison-Wesley,1995)的四位作者之一,Eclipse的总设计师,并且是EclipseJava开发工具(JDT)项目的领导人。


Edward Burns

JSF 1.2规格主导者。SPEC是System PerformanceEvaluation Cooperative的缩写,是几十家世界知名计算机大厂商所支持的非盈利的合作组织,旨在开发共同认可的标准基准程序。

SPEC基准程序是由SPEC开发的一组用于计算机性能综合评价的程序。以对VAX11/780机的测试结果作为基数,其他计算机的测试结果以相对于这个基数的比率来表示。SPEC基准程序能较全面地反映机器性能,有一定的参考价值。

SPEC版本1.0是1989年10月宣布的,是一套复杂的基准程序集,主要用于测试与工程和科学应用有关的数值密集型的整数和浮点数方面的计算。源程序超过15万行,包含10个测试程序,使用的数据量比较大,分别测试应用的各个方面。

SPEC基准程序测试结果一般以SPECmark(SPEC分数)、SPECint(SPEC整数)和SPECfp(SPEC浮点数)来表示。其中SPEC分数是10个程序的几何平均值,SPEC整数是4个整数程序的几何平均值,SPEC浮点数是6个浮点程序的集合平均值。

1992年在原来SPECint89和SPECfp89的基础上增加了两个整数测试程序和8个浮点数测试程序,因此SPECint92由6个程序组成,SPECfp92由14个程序组成。这20个基准程序是基于不同的应用写成的,主要测量32位cpu、主存储器、编译器和操作系统的性能。

参加这个组织的主要成员有:IBM,AT&T,BULL,Compag,CDC,DG,DEC,Fujitsu,HP,Intel,MIPS,Motorola,SGI,SUN和Unisys等。1995年,这些厂商又共同推出了SPECint95和SPECfp95作为最新的测试标准程序。


F



Floyd Marinescu

《EJB Design Patterns》作者。他架构、建置并执行了TheServerSide.com(知名J2EE社区)。他写了无以计数的文章并且经常在Java研讨会上发表演说。

G



Gavin King

Hibernate的作者,EJB3.0专家委员会成员,JBoss核心成员之一,也是《Hibernate in Action》一书的作者。Gavin King 1974年出生于澳大利亚,现在居住在澳大利亚默尔本市。Hibernate 是Java平台上的一种流行的、容易使用的开放源代码对象关系(OR)映射框架。Hibernate诞生在2001年11月,在短短的两年多时间就发展成为Java世界主流的持久层框架软件,令人侧目。Hibernate是一个雄心勃勃的项目,它的目标是成为Java中管理持续性数据问题的一种完整的解决方案。它协调应用与关系数据库的交互,让开发者解放出来专注于手边的业务问题。Hibernate是一种非强迫的解决方案。


Gregor Hohpe

《Enterprise Integration Patterns》作者,目前在ThoughtWorks工作。


Gregor Kiczales

人称AOP之父,是其领导了开发AOP和AspectJ的PARC团队。他是知名的AOP传教士,并且致力于建立同时给实践者和研究者服务的社区。他在开发高级编程技术并把这些技术传授给开发者方面有二十年的经验,是ANSI CLOS设计团队的成员,同时还是CLOS参考实现的作者,以及《The Art of Metaobject Protocol》(MIT出版社,1991)的作者之一。他现在是British Columbia大学软件设计方面的Sierra System教授。


Graham Hamilton

Sun副总裁,J2SE1.3、1.4、5.0项目领导人。

H



Hani Suleiman

OSWorkflow开发者,BileBlog的作者。osworkflow是一个轻量级的又极为灵活的工作流引擎,较容易和其它架构做整合。


Howard Lewis Ship

HLS的Blog:http://howardlewisship.com/blog/Tapestry和Hivemind的开发者,《Tapestry in Action》作者。

Tapestry是类似WebForms机制的Java Web Framework,Hivemind是一个微内核的IoC容器,Tapestry和Hivemind都是Apache Foundation Software旗下的软件。


J



James Strachan

Geronimo的发起者之一,Jakarta项目管理委员会成员,N多JSR expert group的成员。Geronimo是Apache软件基金会为了创造一个兼容J2EE的容器,而整理出来的一个新成果。

Jakarta是一个Sun公司支持的开源项目。它的最终目标是创造一个质量达到了那些商业解决方案要求的免费J2EE服务器端解决方案。Tomcat应用程序是整个项目的主要部分,并且在很多人看来,它几乎就等同于整个的Jakarta项目。



Jason Careria

Webwork2,Xwork核心的开发者。WebWork2.x前身是Rickard Oberg开发的WebWork,但现在WebWork已经被拆分成了XWork1和WebWork2两个项目。


Jason Hunter

Apache基金会管理员,《Java Servlet Programming》作者,Xquery,Jdom等的开发者。


John Crupi

Sun Java Center系统架构师,目前专著于SOA。


Jonathan Bruce

JDBC规格主导人。


Joshua Bloch

《Effective Java》作者,前java平台核心开发者,目前在google。


Jon Tirsen

Nanning的首席开发者,PicoContainer和NanoContainer活跃开发者,目前在ThoughtWorks工作,Damage Control的项目领导者之一。


Juergen Hoeler

Juergen Hoeler是SpringFramework的共同作者之一,与Rod Johnson共同发起和创造了SpringFramework。SpringFramework中Hibernate相关的API是由Juergen Hoeler完成的。对于喜欢使用Spring/Hibernate架构的程序员来说一定对这部分非常熟悉。Juergen Hoeler也是《J2EE Development without EJB》一书的共同作者之一。

K



Kent Beck

设计模式的开创者之一。XP[eXtreme Programming(极限编程)]和TDD的创始人,xUnit的开发者。是《The Smalltalk Best Practice Patterns》、《Extreme Programming Explained》和《Planning Extreme Programming(与Martin Fowler合著)》等书的作者。

L


Linda DeMichiel

EJB 3.0规格主导者,在SUN工作。

M



Marc Fleury

博士,1968年生于巴黎。马克原先在太阳微视(法国)从事过销售工作。毕业于法国声名显赫的Ecole Poly技术学院的他,同时也是伞兵队的前任上尉。后来,马克从巴黎高等师范学校获得理论物理专业的硕士和博士学位。在从事物理方面的研究过程中,他也在麻省理工学院做过X射线激光器方面的访问学者。目前,马克是JBoss公司,即一个坐落在亚特兰大郊外的、提供优质服务的公司的总裁。JBoss公司,由Marc Fleury领衔的、由世界范围内100多位开发者组成的公司。他们分发各种用途的J2EE工具,并且致力于将JBoss发展为J2EE平台中的主流企业Java应用服务器。


Mark Hapner

Mark Hapner不仅是J2EE 1.4规范的主导者,还是Web Service协同工作组织中Sun公司的代表。简而言之,他是Sun驱动J2EE技术趋势的关键人物。Mark于1996年加 入Sun的Java软件公司,参与JDBC API的开发。接着,他撰写了EJB规范和JMS规范。


Martin Fowler

从80年代开始,他就一直从事软件开发的工作。他写过四本软件开发方面的书。《分析模式》是对他商业建模的经验的收集回顾,其中有很多重要的商业对象开发模式。《UML Disilled》是对UML符号、语意以及迭代式开发过程的概览,在1998年获得软件开发生产力奖金,现在已经出到了第二版。《Refactoring》描述了如何以可控的、快速的方式改变现有软件的设计。他最近与Kent Beck合写的《Planning Extreme Programming》则描述了如何进行XP设计。这四本书被全世界的软件开发者奉为经典,尤其是后两本,让软件开发者们看到了快速、灵活的软件开发的可能性。他所描述的重构技术和基于重构的XP方法,被称为“软件开发史上的一次风暴”。Martin Fowler在许多软件开发的国际性会议上做过演讲。他还是OOPSLA、软件开发委员会、UML世界、XP2001、TOOLS等几个协会的会员。同时,他还是“分布式运算”杂志的专栏作家。现在,他又为“软件开发”杂志做顾问,同时还为“IEEE软件”写一个设计专栏。目前是ThoughtWorks(系统集成及顾问公司)的CEO。


Matt Raible

1997年毕业于Denver大学,获得俄语、国际商业和经济学位。《Spring live》一书的作者。


Mike Cannon-Brookes

OpenSymphony及Atlassian的开发者,《Java Open Source programing》一书的作者。

P



Pat Lightbody

Webwork的开发者。


Patrick Linskey

JDO活跃人士,JDO2.0,EJB3.0专家组成员。

R



Rick Ross

Javalobby公司创建人。


Rickard Oberg

Xdoclet及Webwork的开发者,Jboss的前架构师,目前主要研究AOP。Oberg是瑞典Java编程竞赛“Software Reuse Cafe”的两度折桂者,这使得他成为瑞典最好的Java程序员之一。


Robin Roos

JDO2.0活跃人士,《Java Data Object》一书的作者。


Rod Johnson

Rod Johnson在2002年出版了一本非常畅销的J2EE书籍《Expert One-on-One J2EE Design and Development》,阐述了J2EE开发的方方面面的经验和设计,同时他根据本书的思路,开发了SpringFramework。如今SpringFramework是最流行的轻量级Java IoC容器。Rod本人也是Servlet2.4和JDO2.0专家委员会成员,他同时也是一位经验丰富的技术咨询专家。Rod开了一家咨询公司,提供SpringFramework的技术支持和培训。Rod Johnson也是反EJB方面一个代表人物,他创建的Spring的最终目标是取代EJB。

S



Scott Ambler

Scott W. Ambler是Ronin International的总裁,该公司是一家专门提供面向对象过程指导、体系结构建模和Enterprise JavaBean(EJB)开发的咨询企业。他自己著作了或者与其他人合著了几本有关面向对象开发的书籍,包括最近出版的《The Object Primer 2nd Edition》。可以通过scott.ambler@ronin-intl.com与他联系,他的网站位于www.ambysoft.com。


Sergey Dmitriev

Jetbrains公司CEO,Intelij IDEA的开发者。Sergey Dmitriev认为,我们今天把领域问题映射成通用编程语言如Java、C#,就像把C语言用汇编代码表达出来一样如在石器时代。所以我们需要可以随意制造贴近问题域的DSL。

T


Ted Neward

Ted Neward是Manning出版社元老级的作者,Manning最早出版的书正是Ted Neward的《Core OWL》与《Advanced OWL》(一本入门,一本进阶)。有趣的是,当Ted Neward的OWL这两本书快出版时,Borland居然宣布停止开发OWL。Ted Neward花了这么多心血,居然换来这样的结果。OWL本来市场就很小,又被Borland宣布死刑,Ted Neward的这两本书卖得奇惨。后来,Ted Neward转向Java,在Manning出版了一本很受好评的《Effective Enterprise Java》。接着,Ted Neward开始和O’Reilly合作,出版了好几本与他人合著的.NET书,包括了《C# in a Nutshell》、《VB.NET Core Classes in a Nutshell》、《C# Language Packet Reference》、《Shared Source CLI Essentials》。其中,《Shared Source CLI Essentials》一书是剖析Rotor的第一本书,2003年三月底出版。

V



Vincent Massol

Vincent Massol是Jakarta Cactus测试框架的创建者,也是Maven和MockObjects开发团队的活跃参与者。他是Pivolis的CTO,还是一位敏捷软件开发专家。《Junit in action》一书的作者。

软件维护:Java文件格式之变化

一、概述

二、Java串行化

三、引入版本编号

四、结束语

一、概述

一个程序正式发行出去之后,如果要增加一些新的功能,往往意味着同时要修改用户保存数据的方式,也就是必须更改程序保存文件的格式――通常是增加保存到文件的数据。有些时候,文件格式必须作彻底的改动,以配合实现程序的新功能。从这个意义上看,文件格式的发展/变化总是和程序的功能改进相呼应。

但是,大多数情况下,把原有的数据格式一丢了事是行不通的。动物王国中,不能适应环境意味着死亡;软件领域也相似,新软件是否支持原有的数据格式很大程度上决定了用户是否升级。

不管软件新增/改进了多少功能,不管新的文件格式是多么完美,如果新软件不能利用原来的文件格式,用户一般不太会认可新软件。解决该问题的办法包括:

●保留老代码来读取老文件。采用这种方案一般需要额外编写一些代码,把老文件转换成新的格式(一般地,最简单的办法是先把老文件的数据转换成新的内部对象,然后利用现有的写入新版文件格式的对象)。这种办法的好处是既保留了原有的代码,又使它与新的文件格式兼容。但是,这种办法有时可能导致丢失部分数据,不过总要比丢失全部数据好。

●使新版软件能够读/写老文件格式。这种办法工作量较大,因为程序的新版本一般会增加一些原来没有的功能,老的数据格式中通常缺乏新功能必需的某些数据。

当新版软件对原来执行任务的方式作了根本性的变动时,丢失数据决非难得一见的偶然事件。如果新版软件采用和原来不同的方式达到同样的效果,原来的功能可能不再有保留的必要。例如,如果一个程序原来用Swing做用户界面,现在把它改成了Web(浏览器)用户界面,原来的许多用户界面设置就不再有效。

又如,如果有一个邮件程序,原来用的是以文件夹为基础的索引,现在把它改成了以单词为基础的索引系统,在升级索引文件格式的过程中就有可能丢失许多信息;如果原来的索引文件保存了许多用户配置选项和优化措施,在新的索引系统中这些数据可能无法利用。

这类问题没有绝对完美的解决办法,但是我们可以采取一些措施,使得升级文件格式带来的负面影响尽可能小。Java串行化(Serialization)有着简单易用的特点,日益成为一种保存文件的重要手段,有鉴于此,下面我们就来看看在软件版本变更过程中,通过Java串行化保存的文件如何保持兼容性。

二、Java串行化

Java串行化有许多优点:

●容易使用。

●如果一个对象连接到其他对象,串行化机制会保存所有相关的对象。

●如果某个对象出现多次,串行化机制只保存一次。这一点极为重要,它不仅减小了文件空间,而且即使代码写得不是很老练,也不必担心会出现无限循环(一个不老练的例子是,用递归的方式保存各个对象,却又未能有效审计哪些对象已经保存,这时就有可能陷入永无终止的循环)。

遗憾的是,Java串行化机制定义的文件格式似乎很脆弱,只要稍微改动一下类的定义,原来保存的对象就可能无法读取。例如,下面是一个简单的类定义:

public class Save implements Serializable
{
String name;

public void save() throws IOException
{
FileOutputStream f = new FileOutputStream("foo");
ObjectOutputStream oos = new ObjectOutputStream(f);
oos.writeObject(this);
oos.close();
}
}

如果在这个类定义中增加一个域,例如final int val = 7;,再来读取原来保存的对象,就会出现下面的异常:

java.io.InvalidClassException:
Save; local class incompatible:
stream classdesc serialVersionUID = -2805284943658356093,
local class serialVersionUID = 3419534311899376629

上例异常信息中的数字串表示类定义里各种属性的编码值:

●类的名字(Save)。

●域的名字(name)。

●方法的名字(Save)。

●已实现的接口(Serializable)。

改动上述任意一项内容(无论是增加或删除),都会引起编码值变化,从而引起类似的异常警报。这个数字序列称为“串行化版本统一标识符”(serial version universal identifier),简称UID。解决这个问题的办法是在类里面新增一个域serialVersionUID,强制类仍旧使用原来的UID。新增的域必须是:

●static:该域定义的属性作用于整个类,而非特定的对象。

●final:保证代码运行期间该域不会被修改。

●long:它是一个64位的数值。

也就是说,新增的serialVersionUID必须定义成下面这种形式:static final long serialVersionUID=-2805284943658356093L;。其中数字后面加上的L表示这是一个long值。

当然,改动之后的类不一定能够和原来的对象兼容。例如,如果把一个域的定义从String改成了int,执行逆-串行化操作时系统就不知道如何处理该值,显示出错误信息:java.io.InvalidClassException: Save; incompatible types for field name。

Java串行化规范(http://java.sun.com/j2se/1.4.1/docs/guide/

serialization/spec/serialTOC.doc.html)提供了有关兼容的改动(http://java.sun.com/j2se/1.4.1/docs/

guide/serialization/spec/version.doc7.html)和不兼容改动(http://java.sun.com/j2se/1.4.1/docs/guide/

serialization/spec/version.doc8.html)的清单,这些清单指出了对类作了哪些改动之后仍可能读取原来串行化的数据。具体细节比较复杂,但了解其主要机制还是很容易的:

简而言之,如果文件中确实保存了所有必需的数据,那么仍有可能读取该文件,当然前提是必须处理好串行化的UID。

三、引入版本编号

许多程序都在无意之中作出了这样的假设:这种文件格式是我要用到的最后一种格式,以后不再需要制定新的格式,现在要做的是处理好在此之前的各种格式。这种程序会试图读取格式版本更高的文件,操作进行到一半才发现某些不能识别的数据,然后就是突然崩溃。如果文件包含了大量的元数据(描述文件本身的数据),处理起来就要容易得多。

在Java中,每一个域都由其名称显式标明,只要文件的改动不是很大(只添加了域,没有被删除或作重大更改的域),可以想象,用老软件来读取新文件格式不是什么难事,虽然有可能丢失一些信息,但可以搞清楚文件的基本情况。

文件格式随着程序功能的改变而改变。理想情况下,程序应当做到既向后兼容(新的版本能够按照老版本的格式读取,甚至可能允许更新),同时做到向前兼容(较老的软件能够识别和处理新版的文件格式)。

通常,文件的版本无法从表面上一眼看出。大多数程序不会因为文件的版本不同而更改文件扩展名,而且目前尚无统一的标记文件版本的办法。因此,有关文件格式的版本声明只能在文件本身之内进行。如果你现在使用的文件格式还不包含版本声明,最好在下次把文件升级成一个不兼容的版本时马上加入版本标记,或者寻求一种在当前文件格式中加入版本标记但不会带来负面影响的办法。

版本信息一般在文件的开头声明,这是因为程序必须在处理文件之前首先检查文件的版本,除非确定了文件的版本,否则不必读取文件的其余部分。

按照惯例,文件版本编号包含两个部分:主版本编号和次版本编号。一个特定版本的程序应当有最适合它处理的主-次版本号;主版本号变化意味着文件格式的重大变化,要继续使用已经非常困难,必须作出重大修改才能升级到新的版本。

文件的主次版本号之前往往还可以加入另一项内容,称为“魔术数字”,它的作用就是保证程序处理的文件类型不会有误(因为文件扩展名有可能不能唯一地标明文件类型)。例如,Java的类文件总是以下列字节内容开头(十六进制):CA FE BA BE。目前还没有这类数字的统一注册机构,不过UNIX在/etc/magic下提供了一个清单(但并不完整)。魔术数字一般有四个字节,取值范围很大,所以一般不必担心会出现取值冲突的情形。

在编写和维护必须读/写文件的代码时,注意代码的向前/向后兼容性是非常必要的。在处理文件的代码中首先读取文件版本,然后根据版本号将文件剩余内容传递给适当的处理方法;如果文件的版本太老,已不再支持,程序应当给出明确的提示。

四、结束语

文件格式设计是一个极其重要的话题,但本文还有许多细节问题尚未涉及。例如,对于大型文件,我们需要随机访问,而不是从前向后依次读取文件内容的顺序访问,这样就不必为了访问文件最后几个字节而读取整个文件。无论是XML还是Java串行化对这类随机访问的支持都不是很理想,而且这类文件格式的发展变化比普通文件更难管理,因为他们依赖于字节级的访问,稍微改动一下文件格式就可能导致不兼容。

如果要让文件具有ACID特性――Atomicity、Consistency、Isolation和Durability,即原子性、一致性、隔离性、持久性,问题更加复杂。ACID与事务的概念密切相关,支持多用户同时访问一个文件。对于这类文件,可以考虑采用某种小型的数据库系统,例如Birdstep或Sleepycat。不过这已经进入了文件格式管理的另一个领域,既涉及到数据库管理软件的版本,也涉及到数据模式设计的版本。

撇开这些复杂的问题不谈,在实践中,很多时候我们只需简单的文件来保存数据,而且不会出现多用户并发访问,可以一次性地处理整个文件(或者至少适合使用顺序访问方式)。对于这些情形,最好在设计文件格式时就考虑版本问题,在日后的运行、维护中一定会带来不少方便。

【阅读原文】《
71EA82}/session_id~{A3FE3724-09D8-4AC2-B35E-21558DB8D582}/content/index.

asp”>Software Maintenance File Format Evolution in Java》

1 定义头和根元素部署描述符文件就像所有XML文件一样,必须以一个XML头开始。这个头声明可以使用的XML版本并给出文件的字符编码。
DOCYTPE声明必须立即出现在此头之后。这个声明告诉服务器适用的servlet规范的版本(如2.2或2.3)并指定管理此文件其余部分内容的语法的DTD(Document Type Definition,文档类型定义)。
所有部署描述符文件的顶层(根)元素为web-app。请注意,XML元素不像HTML,他们是大小写敏感的。因此,web-App和WEB-APP都是不合法的,web-app必须用小写。

2 部署描述符文件内的元素次序

XML元素不仅是大小写敏感的,而且它们还对出现在其他元素中的次序敏感。例如,XML头必须是文件中的第一项,DOCTYPE声明必须是第二项,而web-app元素必须是第三项。在web-app元素内,元素的次序也很重要。服务器不一定强制要求这种次序,但它们允许(实际上有些服务器就是这样做的)完全拒绝执行含有次序不正确的元素的Web应用。这表示使用非标准元素次序的web.xml文件是不可移植的。
下面的列表给出了所有可直接出现在web-app元素内的合法元素所必需的次序。例如,此列表说明servlet元素必须出现在所有servlet-mapping元素之前。请注意,所有这些元素都是可选的。因此,可以省略掉某一元素,但不能把它放于不正确的位置。
l icon icon元素指出IDE和GUI工具用来表示Web应用的一个和两个图像文件的位置。
l display-name display-name元素提供GUI工具可能会用来标记这个特定的Web应用的一个名称。
l description description元素给出与此有关的说明性文本。
l context-param context-param元素声明应用范围内的初始化参数。
l filter 过滤器元素将一个名字与一个实现javax.servlet.Filter接口的类相关联。
l filter-mapping 一旦命名了一个过滤器,就要利用filter-mapping元素把它与一个或多个servlet或JSP页面相关联。
l listener servlet API的版本2.3增加了对事件监听程序的支持,事件监听程序在建立、修改和删除会话或servlet环境时得到通知。Listener元素指出事件监听程序类。
l servlet 在向servlet或JSP页面制定初始化参数或定制URL时,必须首先命名servlet或JSP页面。Servlet元素就是用来完成此项任务的。
l servlet-mapping 服务器一般为servlet提供一个缺省的URL:http://host/webAppPrefix/servlet/ServletName。但是,常常会更改这个URL,以便servlet可以访问初始化参数或更容易地处理相对URL。在更改缺省URL时,使用servlet-mapping元素。
l session-config 如果某个会话在一定时间内未被访问,服务器可以抛弃它以节省内存。可通过使用HttpSession的setMaxInactiveInterval方法明确设置单个会话对象的超时值,或者可利用session-config元素制定缺省超时值。
l mime-mapping 如果Web应用具有想到特殊的文件,希望能保证给他们分配特定的MIME类型,则mime-mapping元素提供这种保证。
l welcom-file-list welcome-file-list元素指示服务器在收到引用一个目录名而不是文件名的URL时,使用哪个文件。
l error-page error-page元素使得在返回特定HTTP状态代码时,或者特定类型的异常被抛出时,能够制定将要显示的页面。
l taglib taglib元素对标记库描述符文件(Tag Libraryu Descriptor file)指定别名。此功能使你能够更改TLD文件的位置,而不用编辑使用这些文件的JSP页面。
l resource-env-ref resource-env-ref元素声明与资源相关的一个管理对象。
l resource-ref resource-ref元素声明一个资源工厂使用的外部资源。
l security-constraint security-constraint元素制定应该保护的URL。它与login-config元素联合使用
l login-config 用login-config元素来指定服务器应该怎样给试图访问受保护页面的用户授权。它与sercurity-constraint元素联合使用。
l security-role security-role元素给出安全角色的一个列表,这些角色将出现在servlet元素内的security-role-ref元素的role-name子元素中。分别地声明角色可使高级IDE处理安全信息更为容易。
l env-entry env-entry元素声明Web应用的环境项。
l ejb-ref ejb-ref元素声明一个EJB的主目录的引用。
l ejb-local-ref ejb-local-ref元素声明一个EJB的本地主目录的应用。

3 分配名称和定制的UL

在web.xml中完成的一个最常见的任务是对servlet或JSP页面给出名称和定制的URL。用servlet元素分配名称,使用servlet-mapping元素将定制的URL与刚分配的名称相关联。
3.1 分配名称
为了提供初始化参数,对servlet或JSP页面定义一个定制URL或分配一个安全角色,必须首先给servlet或JSP页面一个名称。可通过servlet元素分配一个名称。最常见的格式包括servlet-name和servlet-class子元素(在web-app元素内),如下所示:
<servlet>
<servlet-name>Test</servlet-name>
<servlet-class>moreservlets.TestServlet</servlet-class>
</servlet>

这表示位于WEB-INF/classes/moreservlets/TestServlet的servlet已经得到了注册名Test。给servlet一个名称具有两个主要的含义。首先,初始化参数、定制的URL模式以及其他定制通过此注册名而不是类名引用此servlet。其次,可在URL而不是类名中使用此名称。因此,利用刚才给出的定义,URL http://host/webAppPrefix/servlet/Test 可用于 http://host/webAppPrefix/servlet/moreservlets.TestServlet 的场所。
请记住:XML元素不仅是大小写敏感的,而且定义它们的次序也很重要。例如,web-app元素内所有servlet元素必须位于所有servlet-mapping元素(下一小节介绍)之前,而且还要位于5.6节和5.11节讨论的与过滤器或文档相关的元素(如果有的话)之前。类似地,servlet的servlet-name子元素也必须出现在servlet-class之前。5.2节”部署描述符文件内的元素次序”将详细介绍这种必需的次序。
例如,程序清单5-1给出了一个名为TestServlet的简单servlet,它驻留在moreservlets程序包中。因为此servlet是扎根在一个名为deployDemo的目录中的Web应用的组成部分,所以TestServlet.class放在deployDemo/WEB-INF/classes/moreservlets中。程序清单5-2给出将放置在deployDemo/WEB-INF/内的web.xml文件的一部分。此web.xml文件使用servlet-name和servlet-class元素将名称Test与TestServlet.class相关联。图5-1和图5-2分别显示利用缺省URL和注册名调用TestServlet时的结果。

程序清单5-1 TestServlet.java
package moreservlets;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

/** Simple servlet used to illustrate servlet naming
* and custom URLs.
* <P>
* Taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
* http://www.moreservlets.com/.
* © 2002 Marty Hall; may be freely used or adapted.
*/

public class TestServlet extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType(”text/html”);
PrintWriter out = response.getWriter();
String uri = request.getRequestURI();
out.println(ServletUtilities.headWithTitle(”Test Servlet”) +
“<BODY BGCOLOR=\”#FDF5E6\”>\n” +
“<H2>URI: ” + uri + “</H2>\n” +
“</BODY></HTML>”);
}
}

程序清单5-2 web.xml(说明servlet名称的摘录)
<?xml version=”1.0″ encoding=”ISO-8859-1″?>
<!DOCTYPE web-app
PUBLIC “-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN”
http://java.sun.com/dtd/web-app_2_3.dtd“>

<web-app>
<!– … –>
<servlet>
<servlet-name>Test</servlet-name>
<servlet-class>moreservlets.TestServlet</servlet-class>
</servlet>
<!– … –>
</web-app>

3.2 定义定制的URL
大多数服务器具有一个缺省的serlvet URL:
http://host/webAppPrefix/servlet/packageName.ServletName。虽然在开发中使用这个URL很方便,但是我们常常会希望另一个URL用于部署。例如,可能会需要一个出现在Web应用顶层的URL(如,http://host/webAppPrefix/Anyname),并且在此URL中没有servlet项。位于顶层的URL简化了相对URL的使用。此外,对许多开发人员来说,顶层URL看上去比更长更麻烦的缺省URL更简短。
事实上,有时需要使用定制的URL。比如,你可能想关闭缺省URL映射,以便更好地强制实施安全限制或防止用户意外地访问无初始化参数的servlet。如果你禁止了缺省的URL,那么你怎样访问servlet呢?这时只有使用定制的URL了。
为了分配一个定制的URL,可使用servlet-mapping元素及其servlet-name和url-pattern子元素。Servlet-name元素提供了一个任意名称,可利用此名称引用相应的servlet;url-pattern描述了相对于Web应用的根目录的URL。url-pattern元素的值必须以斜杠(/)起始。
下面给出一个简单的web.xml摘录,它允许使用URL http://host/webAppPrefix/UrlTest而不是http://host/webAppPrefix/servlet/Test
http://host/webAppPrefix/servlet/moreservlets.TestServlet。请注意,仍然需要XML头、DOCTYPE声明以及web-app封闭元素。此外,可回忆一下,XML元素出现地次序不是随意的。特别是,需要把所有servlet元素放在所有servlet-mapping元素之前。
<servlet>
<servlet-name>Test</servlet-name>
<servlet-class>moreservlets.TestServlet</servlet-class>
</servlet>
<!– … –>
<servlet-mapping>
<servlet-name>Test</servlet-name>
<url-pattern>/UrlTest</url-pattern>
</servlet-mapping>
URL模式还可以包含通配符。例如,下面的小程序指示服务器发送所有以Web应用的URL前缀开始,以..asp结束的请求到名为BashMS的servlet。
<servlet>
<servlet-name>BashMS</servlet-name>
<servlet-class>msUtils.ASPTranslator</servlet-class>
</servlet>
<!– … –>
<servlet-mapping>
<servlet-name>BashMS</servlet-name>
<url-pattern>/*.asp</url-pattern>
</servlet-mapping>
3.3 命名JSP页面
因为JSP页面要转换成sevlet,自然希望就像命名servlet一样命名JSP页面。毕竟,JSP页面可能会从初始化参数、安全设置或定制的URL中受益,正如普通的serlvet那样。虽然JSP页面的后台实际上是servlet这句话是正确的,但存在一个关键的猜疑:即,你不知道JSP页面的实际类名(因为系统自己挑选这个名字)。因此,为了命名JSP页面,可将jsp-file元素替换为servlet-calss元素,如下所示:
<servlet>
<servlet-name>Test</servlet-name>
<jsp-file>/TestPage.jsp</jsp-file>
</servlet>
命名JSP页面的原因与命名servlet的原因完全相同:即为了提供一个与定制设置(如,初始化参数和安全设置)一起使用的名称,并且,以便能更改激活JSP页面的URL(比方说,以便多个URL通过相同页面得以处理,或者从URL中去掉.jsp扩展名)。但是,在设置初始化参数时,应该注意,JSP页面是利用jspInit方法,而不是init方法读取初始化参数的。
例如,程序清单5-3给出一个名为TestPage.jsp的简单JSP页面,它的工作只是打印出用来激活它的URL的本地部分。TestPage.jsp放置在deployDemo应用的顶层。程序清单5-4给出了用来分配一个注册名PageName,然后将此注册名与http://host/webAppPrefix/UrlTest2/anything 形式的URL相关联的web.xml文件(即,deployDemo/WEB-INF/web.xml)的一部分。

程序清单5-3 TestPage.jsp
<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.0 Transitional//EN”>
<HTML>
<HEAD>
<TITLE>
JSP Test Page
</TITLE>
</HEAD>
<BODY BGCOLOR=”#FDF5E6″>
<H2>URI: <%= request.getRequestURI() %></H2>
</BODY>
</HTML>

程序清单5-4 web.xml(说明JSP页命名的摘录)
<?xml version=”1.0″ encoding=”ISO-8859-1″?>
<!DOCTYPE web-app
PUBLIC “-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN”
http://java.sun.com/dtd/web-app_2_3.dtd“>

<web-app>
<!– … –>
<servlet>
<servlet-name>PageName</servlet-name>
<jsp-file>/TestPage.jsp</jsp-file>
</servlet>
<!– … –>
<servlet-mapping>
<servlet-name> PageName </servlet-name>
<url-pattern>/UrlTest2/*</url-pattern>
</servlet-mapping>
<!– … –>
</web-app>

4 禁止激活器servlet

对servlet或JSP页面建立定制URL的一个原因是,这样做可以注册从init(servlet)或jspInit(JSP页面)方法中读取得初始化参数。但是,初始化参数只在是利用定制URL模式或注册名访问servlet或JSP页面时可以使用,用缺省URL http://host/webAppPrefix/servlet/ServletName 访问时不能使用。因此,你可能会希望关闭缺省URL,这样就不会有人意外地调用初始化servlet了。这个过程有时称为禁止激活器servlet,因为多数服务器具有一个用缺省的servlet URL注册的标准servlet,并激活缺省的URL应用的实际servlet。
有两种禁止此缺省URL的主要方法:
l 在每个Web应用中重新映射/servlet/模式。
l 全局关闭激活器servlet。
重要的是应该注意到,虽然重新映射每个Web应用中的/servlet/模式比彻底禁止激活servlet所做的工作更多,但重新映射可以用一种完全可移植的方式来完成。相反,全局禁止激活器servlet完全是针对具体机器的,事实上有的服务器(如ServletExec)没有这样的选择。下面的讨论对每个Web应用重新映射/servlet/ URL模式的策略。后面提供在Tomcat中全局禁止激活器servlet的详细内容。
4.1 重新映射/servlet/URL模式
在一个特定的Web应用中禁止以http://host/webAppPrefix/servlet/ 开始的URL的处理非常简单。所需做的事情就是建立一个错误消息servlet,并使用前一节讨论的url-pattern元素将所有匹配请求转向该servlet。只要简单地使用:
<url-pattern>/servlet/*</url-pattern>
作为servlet-mapping元素中的模式即可。
例如,程序清单5-5给出了将SorryServlet servlet(程序清单5-6)与所有以http://host/webAppPrefix/servlet/ 开头的URL相关联的部署描述符文件的一部分。

程序清单5-5 web.xml(说明JSP页命名的摘录)
<?xml version=”1.0″ encoding=”ISO-8859-1″?>
<!DOCTYPE web-app
PUBLIC “-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN”
http://java.sun.com/dtd/web-app_2_3.dtd“>

<web-app>
<!– … –>
<servlet>
<servlet-name>Sorry</servlet-name>
<servlet-class>moreservlets.SorryServlet</servlet-class>
</servlet>
<!– … –>
<servlet-mapping>
<servlet-name> Sorry </servlet-name>
<url-pattern>/servlet/*</url-pattern>
</servlet-mapping>
<!– … –>
</web-app>

程序清单5-6 SorryServlet.java
package moreservlets;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

/** Simple servlet used to give error messages to
* users who try to access default servlet URLs
* (i.e., http://host/webAppPrefix/servlet/ServletName)
* in Web applications that have disabled this
* behavior.
* <P>
* Taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
* http://www.moreservlets.com/.
* © 2002 Marty Hall; may be freely used or adapted.
*/

public class SorryServlet extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType(”text/html”);
PrintWriter out = response.getWriter();
String title = “Invoker Servlet Disabled.”;
out.println(ServletUtilities.headWithTitle(title) +
“<BODY BGCOLOR=\”#FDF5E6\”>\n” +
“<H2>” + title + “</H2>\n” +
“Sorry, access to servlets by means of\n” +
“URLs that begin with\n” +
http://host/webAppPrefix/servlet/\n” +
“has been disabled.\n” +
“</BODY></HTML>”);
}

public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}

4.2 全局禁止激活器:Tomcat
Tomcat 4中用来关闭缺省URL的方法与Tomcat 3中所用的很不相同。下面介绍这两种方法:
1.禁止激活器: Tomcat 4
Tomcat 4用与前面相同的方法关闭激活器servlet,即利用web.xml中的url-mapping元素进行关闭。不同之处在于Tomcat使用了放在install_dir/conf中的一个服务器专用的全局web.xml文件,而前面使用的是存放在每个Web应用的WEB-INF目录中的标准web.xml文件。
因此,为了在Tomcat 4中关闭激活器servlet,只需在install_dir/conf/web.xml中简单地注释出/servlet/* URL映射项即可,如下所示:
<!–
<servlet-mapping>
<servlet-name>invoker</servlet-name>
<url-pattern>/servlet/*</url-pattern>
</servlet-mapping>
–>
再次提醒,应该注意这个项是位于存放在install_dir/conf的Tomcat专用的web.xml文件中的,此文件不是存放在每个Web应用的WEB-INF目录中的标准web.xml。
2.禁止激活器:Tomcat3
在Apache Tomcat的版本3中,通过在install_dir/conf/server.xml中注释出InvokerInterceptor项全局禁止缺省servlet URL。例如,下面是禁止使用缺省servlet URL的server.xml文件的一部分。
<!–
<RequsetInterceptor
className=”org.apache.tomcat.request.InvokerInterceptor”
debug=”0″ prefix=”/servlet/” />
–>

5 初始化和预装载servlet与JSP页面

这里讨论控制servlet和JSP页面的启动行为的方法。特别是,说明了怎样分配初始化参数以及怎样更改服务器生存期中装载servlet和JSP页面的时刻。
5.1 分配servlet初始化参数
利用init-param元素向servlet提供初始化参数,init-param元素具有param-name和param-value子元素。例如,在下面的例子中,如果initServlet servlet是利用它的注册名(InitTest)访问的,它将能够从其方法中调用getServletConfig().getInitParameter(”param1″)获得”Value 1″,调用getServletConfig().getInitParameter(”param2″)获得”2″。
<servlet>
<servlet-name>InitTest</servlet-name>
<servlet-class>moreservlets.InitServlet</servlet-class>
<init-param>
<param-name>param1</param-name>
<param-value>value1</param-value>
</init-param>
<init-param>
<param-name>param2</param-name>
<param-value>2</param-value>
</init-param>
</servlet>
在涉及初始化参数时,有几点需要注意:
l 返回值。GetInitParameter的返回值总是一个String。因此,在前一个例子中,可对param2使用Integer.parseInt获得一个int。
l JSP中的初始化。JSP页面使用jspInit而不是init。JSP页面还需要使用jsp-file元素代替servlet-class。
l 缺省URL。初始化参数只在通过它们的注册名或与它们注册名相关的定制URL模式访问Servlet时可以使用。因此,在这个例子中,param1和param2初始化参数将能够在使用URL http://host/webAppPrefix/servlet/InitTest 时可用,但在使用URL http://host/webAppPrefix/servlet/myPackage.InitServlet 时不能使用。
例如,程序清单5-7给出一个名为InitServlet的简单servlet,它使用init方法设置firstName和emailAddress字段。程序清单5-8给出分配名称InitTest给servlet的web.xml文件。
程序清单5-7 InitServlet.java
package moreservlets;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

/** Simple servlet used to illustrate servlet
* initialization parameters.
* <P>
* Taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
* http://www.moreservlets.com/.
* © 2002 Marty Hall; may be freely used or adapted.
*/

public class InitServlet extends HttpServlet {
private String firstName, emailAddress;

public void init() {
ServletConfig config = getServletConfig();
firstName = config.getInitParameter(”firstName”);
emailAddress = config.getInitParameter(”emailAddress”);
}

public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType(”text/html”);
PrintWriter out = response.getWriter();
String uri = request.getRequestURI();
out.println(ServletUtilities.headWithTitle(”Init Servlet”) +
“<BODY BGCOLOR=\”#FDF5E6\”>\n” +
“<H2>Init Parameters:</H2>\n” +
“<UL>\n” +
“<LI>First name: ” + firstName + “\n” +
“<LI>Email address: ” + emailAddress + “\n” +
“</UL>\n” +
“</BODY></HTML>”);
}
}

程序清单5-8 web.xml(说明初始化参数的摘录)
<?xml version=”1.0″ encoding=”ISO-8859-1″?>
<!DOCTYPE web-app
PUBLIC “-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN”
http://java.sun.com/dtd/web-app_2_3.dtd“>

<web-app>
<!– … –>
<servlet>
<servlet-name>InitTest</servlet-name>
<servlet-class>moreservlets.InitServlet</servlet-class>
<init-param>
<param-name>firstName</param-name>
<param-value>Larry</param-value>
</init-param>
<init-param>
<param-name>emailAddress</param-name>
<param-value>Ellison@Microsoft.com</param-value>
</init-param>
</servlet>
<!– … –>
</web-app>

5.2 分配JSP初始化参数
给JSP页面提供初始化参数在三个方面不同于给servlet提供初始化参数。
1)使用jsp-file而不是servlet-class。因此,WEB-INF/web.xml文件的servlet元素如下所示:
<servlet>
<servlet-name>PageName</servlet-name>
<jsp-file>/RealPage.jsp</jsp-file>
<init-param>
<param-name>…</param-name>
<param-value>…</param-value>
</init-param>

</servlet>
2)几乎总是分配一个明确的URL模式。对servlet,一般相应地使用以http://host/webAppPrefix/servlet/ 开始的缺省URL。只需记住,使用注册名而不是原名称即可。这对于JSP页面在技术上也是合法的。例如,在上面给出的例子中,可用URL http://host/webAppPrefix/servlet/PageName 访问RealPage.jsp的对初始化参数具有访问权的版本。但在用于JSP页面时,许多用户似乎不喜欢应用常规的servlet的URL。此外,如果JSP页面位于服务器为其提供了目录清单的目录中(如,一个既没有index.html也没有index.jsp文件的目录),则用户可能会连接到此JSP页面,单击它,从而意外地激活未初始化的页面。因此,好的办法是使用url-pattern(5.3节)将JSP页面的原URL与注册的servlet名相关联。这样,客户机可使用JSP页面的普通名称,但仍然激活定制的版本。例如,给定来自项目1的servlet定义,可使用下面的servlet-mapping定义:
<servlet-mapping>
<servlet-name>PageName</servlet-name>
<url-pattern>/RealPage.jsp</url-pattern>
</servlet-mapping>
3)JSP页使用jspInit而不是init。自动从JSP页面建立的servlet或许已经使用了inti方法。因此,使用JSP声明提供一个init方法是不合法的,必须制定jspInit方法。
为了说明初始化JSP页面的过程,程序清单5-9给出了一个名为InitPage.jsp的JSP页面,它包含一个jspInit方法且放置于deployDemo Web应用层次结构的顶层。一般,http://host/deployDemo/InitPage.jsp 形式的URL将激活此页面的不具有初始化参数访问权的版本,从而将对firstName和emailAddress变量显示null。但是,web.xml文件(程序清单5-10)分配了一个注册名,然后将该注册名与URL模式/InitPage.jsp相关联。

程序清单5-9 InitPage.jsp
<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.0 Transitional//EN”>
<HTML>
<HEAD><TITLE>JSP Init Test</TITLE></HEAD>
<BODY BGCOLOR=”#FDF5E6″>
<H2>Init Parameters:</H2>
<UL>
<LI>First name: <%= firstName %>
<LI>Email address: <%= emailAddress %>
</UL>
</BODY></HTML>
<%!
private String firstName, emailAddress;

public void jspInit() {
ServletConfig config = getServletConfig();
firstName = config.getInitParameter(”firstName”);
emailAddress = config.getInitParameter(”emailAddress”);
}
%>

程序清单5-10 web.xml(说明JSP页面的init参数的摘录)
<?xml version=”1.0″ encoding=”ISO-8859-1″?>
<!DOCTYPE web-app
PUBLIC “-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN”
http://java.sun.com/dtd/web-app_2_3.dtd“>

<web-app>
<!– … –>
<servlet>
<servlet-name>InitPage</servlet-name>
<jsp-file>/InitPage.jsp</jsp-file>
<init-param>
<param-name>firstName</param-name>
<param-value>Bill</param-value>
</init-param>
<init-param>
<param-name>emailAddress</param-name>
<param-value>gates@oracle.com</param-value>
</init-param>
</servlet>
<!– … –>
<servlet-mapping>
<servlet-name> InitPage</servlet-name>
<url-pattern>/InitPage.jsp</url-pattern>
</servlet-mapping>
<!– … –>
</web-app>

5.3 提供应用范围内的初始化参数
一般,对单个地servlet或JSP页面分配初始化参数。指定的servlet或JSP页面利用ServletConfig的getInitParameter方法读取这些参数。但是,在某些情形下,希望提供可由任意servlet或JSP页面借助ServletContext的getInitParameter方法读取的系统范围内的初始化参数。
可利用context-param元素声明这些系统范围内的初始化值。context-param元素应该包含param-name、param-value以及可选的description子元素,如下所示:
<context-param>
<param-name>support-email</param-name>
<param-value>blackhole@mycompany.com</param-value>
</context-param>
可回忆一下,为了保证可移植性,web.xml内的元素必须以正确的次序声明。但这里应该注意,context-param元素必须出现任意与文档有关的元素(icon、display-name或description)之后及filter、filter-mapping、listener或servlet元素之前。
5.4 在服务器启动时装载servlet
假如servlet或JSP页面有一个要花很长时间执行的init(servlet)或jspInit(JSP)方法。例如,假如init或jspInit方法从某个数据库或ResourceBundle查找产量。这种情况下,在第一个客户机请求时装载servlet的缺省行为将对第一个客户机产生较长时间的延迟。因此,可利用servlet的load-on-startup元素规定服务器在第一次启动时装载servlet。下面是一个例子。
<servlet>
<servlet-name> … </servlet-name>
<servlet-class> … </servlet-class> <!– Or jsp-file –>
<load-on-startup/>
</servlet>
可以为此元素体提供一个整数而不是使用一个空的load-on-startup。想法是服务器应该在装载较大数目的servlet或JSP页面之前装载较少数目的servlet或JSP页面。例如,下面的servlet项(放置在Web应用的WEB-INF目录下的web.xml文件中的web-app元素内)将指示服务器首先装载和初始化SearchServlet,然后装载和初始化由位于Web应用的result目录中的index.jsp文件产生的servlet。
<servlet>
<servlet-name>Search</servlet-name>
<servlet-class>myPackage.SearchServlet</servlet-class> <!– Or jsp-file –>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>Results</servlet-name>
<servlet-class>/results/index.jsp</servlet-class> <!– Or jsp-file –>
<load-on-startup>2</load-on-startup>
</servlet>

6 声明过滤器

servlet版本2.3引入了过滤器的概念。虽然所有支持servlet API版本2.3的服务器都支持过滤器,但为了使用与过滤器有关的元素,必须在web.xml中使用版本2.3的DTD。
过滤器可截取和修改进入一个servlet或JSP页面的请求或从一个servlet或JSP页面发出的相应。在执行一个servlet或JSP页面之前,必须执行第一个相关的过滤器的doFilter方法。在该过滤器对其FilterChain对象调用doFilter时,执行链中的下一个过滤器。如果没有其他过滤器,servlet或JSP页面被执行。过滤器具有对到来的ServletRequest对象的全部访问权,因此,它们可以查看客户机名、查找到来的cookie等。为了访问servlet或JSP页面的输出,过滤器可将响应对象包裹在一个替身对象(stand-in object)中,比方说把输出累加到一个缓冲区。在调用FilterChain对象的doFilter方法之后,过滤器可检查缓冲区,如有必要,就对它进行修改,然后传送到客户机。
例如,程序清单5-11帝国难以了一个简单的过滤器,只要访问相关的servlet或JSP页面,它就截取请求并在标准输出上打印一个报告(开发过程中在桌面系统上运行时,大多数服务器都可以使用这个过滤器)。

程序清单5-11 ReportFilter.java
package moreservlets;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;

/** Simple filter that prints a report on the standard output
* whenever the associated servlet or JSP page is accessed.
* <P>
* Taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
* http://www.moreservlets.com/.
* © 2002 Marty Hall; may be freely used or adapted.
*/

public class ReportFilter implements Filter {
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws ServletException, IOException {
HttpServletRequest req = (HttpServletRequest)request;
System.out.println(req.getRemoteHost() +
” tried to access ” +
req.getRequestURL() +
” on ” + new Date() + “.”);
chain.doFilter(request,response);
}

public void init(FilterConfig config)
throws ServletException {
}

public void destroy() {}
}

一旦建立了一个过滤器,可以在web.xml中利用filter元素以及filter-name(任意名称)、file-class(完全限定的类名)和(可选的)init-params子元素声明它。请注意,元素在web.xml的web-app元素中出现的次序不是任意的;允许服务器(但不是必需的)强制所需的次序,并且实际中有些服务器也是这样做的。但这里要注意,所有filter元素必须出现在任意filter-mapping元素之前,filter-mapping元素又必须出现在所有servlet或servlet-mapping元素之前。
例如,给定上述的ReportFilter类,可在web.xml中作出下面的filter声明。它把名称Reporter与实际的类ReportFilter(位于moreservlets程序包中)相关联。
<filter>
<filter-name>Reporter</filter-name>
<filter-class>moresevlets.ReportFilter</filter-class>
</filter>
一旦命名了一个过滤器,可利用filter-mapping元素把它与一个或多个servlet或JSP页面相关联。关于此项工作有两种选择。
首先,可使用filter-name和servlet-name子元素把此过滤器与一个特定的servlet名(此servlet名必须稍后在相同的web.xml文件中使用servlet元素声明)关联。例如,下面的程序片断指示系统只要利用一个定制的URL访问名为SomeServletName的servlet或JSP页面,就运行名为Reporter的过滤器。
<filter-mapping>
<filter-name>Reporter</filter-name>
<servlet-name>SomeServletName</servlet-name>
</filter-mapping>
其次,可利用filter-name和url-pattern子元素将过滤器与一组servlet、JSP页面或静态内容相关联。例如,相面的程序片段指示系统只要访问Web应用中的任意URL,就运行名为Reporter的过滤器。
<filter-mapping>
<filter-name>Reporter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
例如,程序清单5-12给出了将ReportFilter过滤器与名为PageName的servlet相关联的web.xml文件的一部分。名字PageName依次又与一个名为TestPage.jsp的JSP页面以及以模式http://host/webAppPrefix/UrlTest2/ 开头的URL相关联。TestPage.jsp的源代码已经JSP页面命名的谈论在前面的3节”分配名称和定制的URL”中给出。事实上,程序清单5-12中的servlet和servlet-name项从该节原封不动地拿过来的。给定这些web.xml项,可看到下面的标准输出形式的调试报告(换行是为了容易阅读)。
audit.irs.gov tried to access
http://mycompany.com/deployDemo/UrlTest2/business/tax-plan.html
on Tue Dec 25 13:12:29 EDT 2001.

程序清单5-12 Web.xml(说明filter用法的摘录)
<?xml version=”1.0″ encoding=”ISO-8859-1″?>
<!DOCTYPE web-app
PUBLIC “-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN”
http://java.sun.com/dtd/web-app_2_3.dtd“>

<web-app>
<filter>
<filter-name>Reporter</filter-name>
<filter-class>moresevlets.ReportFilter</filter-class>
</filter>
<!– … –>
<filter-mapping>
<filter-name>Reporter</filter-name>
<servlet-name>PageName</servlet-name>
</filter-mapping>
<!– … –>
<servlet>
<servlet-name>PageName</servlet-name>
<jsp-file>/RealPage.jsp</jsp-file>
</servlet>
<!– … –>
<servlet-mapping>
<servlet-name> PageName </servlet-name>
<url-pattern>/UrlTest2/*</url-pattern>
</servlet-mapping>
<!– … –>
</web-app>

7 指定欢迎页

假如用户提供了一个像http://host/webAppPrefix/directoryName/ 这样的包含一个目录名但没有包含文件名的URL,会发生什么事情呢?用户能得到一个目录表?一个错误?还是标准文件的内容?如果得到标准文件内容,是index.html、index.jsp、default.html、default.htm或别的什么东西呢?
Welcome-file-list元素及其辅助的welcome-file元素解决了这个模糊的问题。例如,下面的web.xml项指出,如果一个URL给出一个目录名但未给出文件名,服务器应该首先试用index.jsp,然后再试用index.html。如果两者都没有找到,则结果有赖于所用的服务器(如一个目录列表)。
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
虽然许多服务器缺省遵循这种行为,但不一定必须这样。因此,明确地使用welcom-file-list保证可移植性是一种良好的习惯。

8 指定处理错误的页面

现在我了解到,你在开发servlet和JSP页面时从不会犯错误,而且你的所有页面是那样的清晰,一般的程序员都不会被它们的搞糊涂。但是,是人总会犯错误的,用户可能会提供不合规定的参数,使用不正确的URL或者不能提供必需的表单字段值。除此之外,其它开发人员可能不那么细心,他们应该有些工具来克服自己的不足。
error-page元素就是用来克服这些问题的。它有两个可能的子元素,分别是:error-code和exception-type。第一个子元素error-code指出在给定的HTTP错误代码出现时使用的URL。第二个子元素excpetion-type指出在出现某个给定的Java异常但未捕捉到时使用的URL。error-code和exception-type都利用location元素指出相应的URL。此URL必须以/开始。location所指出的位置处的页面可通过查找HttpServletRequest对象的两个专门的属性来访问关于错误的信息,这两个属性分别是:javax.servlet.error.status_code和javax.servlet.error.message。
可回忆一下,在web.xml内以正确的次序声明web-app的子元素很重要。这里只要记住,error-page出现在web.xml文件的末尾附近,servlet、servlet-name和welcome-file-list之后即可。

8.1 error-code元素
为了更好地了解error-code元素的值,可考虑一下如果不正确地输入文件名,大多数站点会作出什么反映。这样做一般会出现一个404错误信息,它表示不能找到该文件,但几乎没提供更多有用的信息。另一方面,可以试一下在www.microsoft.comwww.ibm.com 处或者特别是在www.bea.com 处输出未知的文件名。这是会得出有用的消息,这些消息提供可选择的位置,以便查找感兴趣的页面。提供这样有用的错误页面对于Web应用来说是很有价值得。事实上,http://www.plinko.net/404/ 就是把整个站点专门用于404错误页面这个内容。这个站点包含来自全世界最好、最糟和最搞笑的404页面。
程序清单5-13给出一个JSP页面,此页面可返回给提供位置程序名的客户机。程序清单5-14给出指定程序清单5-13作为返回404错误代码时显示的页面的web.xml。请注意,浏览器中显示的URL仍然是客户机所提供的。错误页面是一种后台实现技术。
最后一点,请记住IE5的缺省配置显然不符合HTTP规范,它忽略了服务器生成的错误消息,而是显示自己的标准出错信息。可转到其Tools菜单,选择Internet Options,单击Advanced,取消Show Friendly HTTP Error Message来解决此问题。

程序清单5-13 NotFound.jsp
<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.0 Transitional//EN”>
<HTML>
<HEAD><TITLE>404: Not Found</TITLE></HEAD>
<BODY BGCOLOR=”#FDF5E6″>
<H2>Error!</H2>
I’m sorry, but I cannot find a page that matches
<%= request.getRequestURI() %> on the system. Maybe you should
try one of the following:
<UL>
<LI>Go to the server’s <A xhref=”http://blogger.org.cn/blog//” mce_href=”http://blogger.org.cn/blog//”>home page</A>.
<LI>Search for relevant pages.<BR>
<FORM ACTION=”http://www.google.com/search“>
<CENTER>
Keywords: <INPUT TYPE=”http://blogger.org.cn/blog/TEXT” NAME=”q”><BR>
<INPUT TYPE=”SUBMIT” VALUE=”Search”>
</CENTER>
</FORM>
<LI>Admire a random multiple of 404:
<%= 404*((int)(1000*Math.random())) %>.
<LI>Try a <A xhref=”http://blogger.org.cn/blog/http://www.plinko.net/404/rndindex.asp
TARGET=”http://blogger.org.cn/blog/_blank”>
random 404 error message</A>. From the amazing and
amusing plinko.net <A xhref=”http://blogger.org.cn/blog/http://www.plinko.net/404/“>
404 archive</A>.
</UL>
</BODY></HTML>

程序清单5-14 web.xml(指出HTTP错误代码的错误页面的摘录)
<?xml version=”1.0″ encoding=”ISO-8859-1″?>
<!DOCTYPE web-app
PUBLIC “-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN”
http://java.sun.com/dtd/web-app_2_3.dtd“>

<web-app>
<error-page>
<error-code>404</error-code>
<location>/NotFound.jsp</location>
</error-page>
<!– … –>
</web-app>

8.2 exception-type元素
error-code元素处理某个请求产生一个特定的HTTP状态代码时的情况。然而,对于servlet或JSP页面返回200但产生运行时异常这种同样是常见的情况怎么办呢?这正是exception-type元素要处理的情况。只需提供两样东西即可:即提供如下的一个完全限定的异常类和一个位置:
<error-page>
<exception-type>packageName.className</exception-type>
<location>/SomeURL</location>
</error-page>
这样,如果Web应用中的任何servlet或JSP页面产生一个特定类型的未捕捉到的异常,则使用指定的URL。此异常类型可以是一个标准类型,如javax.ServletException或java.lang.OutOfMemoryError,或者是一个专门针对你的应用的异常。
例如,程序清单5-15给出了一个名为DumbDeveloperException的异常类,可用它来特别标记经验较少的程序员(不是说你的开发组中一定有这种人)所犯的错误。这个类还包含一个名为dangerousComputation的静态方法,它时不时地生成这种类型的异常。程序清单5-16给出对随机整数值调用dangerousCompution的一个JSP页面。在抛出此异常时,如程序清单5-18的web.xml版本中所给出的exception-type所指出的那样,对客户机显示DDE.jsp(程序清单5-17)。图5-16和图5-17分别给出幸运和不幸的结果。

程序清单5-15 DumbDeveloperException.java
package moreservlets;

/** Exception used to flag particularly onerous
programmer blunders. Used to illustrate the
exception-type web.xml element.
* <P>
* Taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
* http://www.moreservlets.com/.
* © 2002 Marty Hall; may be freely used or adapted.
*/

public class DumbDeveloperException extends Exception {
public DumbDeveloperException() {
super(”Duh. What was I *thinking*?”);
}

public static int dangerousComputation(int n)
throws DumbDeveloperException {
if (n < 5) {
return(n + 10);
} else {
throw(new DumbDeveloperException());
}
}
}

程序清单5-16 RiskyPage.jsp
<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.0 Transitional//EN”>
<HTML>
<HEAD><TITLE>Risky JSP Page</TITLE></HEAD>
<BODY BGCOLOR=”#FDF5E6″>
<H2>Risky Calculations</H2>
<%@ page import=”moreservlets.*” %>
<% int n = ((int)(10 * Math.random())); %>
<UL>
<LI>n: <%= n %>
<LI>dangerousComputation(n):
<%= DumbDeveloperException.dangerousComputation(n) %>
</UL>
</BODY></HTML>

程序清单5-17 DDE.jsp
<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.0 Transitional//EN”>
<HTML>
<HEAD><TITLE>Dumb</TITLE></HEAD>
<BODY BGCOLOR=”#FDF5E6″>
<H2>Dumb Developer</H2>
We’re brain dead. Consider using our competitors.
</BODY></HTML>

程序清单5-18 web.xml(为异常指定错误页面的摘录)
<?xml version=”1.0″ encoding=”ISO-8859-1″?>
<!DOCTYPE web-app
PUBLIC “-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN”
http://java.sun.com/dtd/web-app_2_3.dtd“>

<web-app>
<!– … –>
<servlet> … </servlet>
<!– … –>
<error-page>
<exception-type>
moreservlets.DumbDeveloperException
</exception-type>
<location>/DDE.jsp</location>
</error-page>
<!– … –>
</web-app>

9 提供安全性

利用web.xml中的相关元素为服务器的内建功能提供安全性。
9.1 指定验证的方法
使用login-confgi元素规定服务器应该怎样验证试图访问受保护页面的用户。它包含三个可能的子元素,分别是:auth-method、realm-name和form-login-config。login-config元素应该出现在web.xml部署描述符文件的结尾附近,紧跟在security-constraint元素之后。
l auth-method
login-config的这个子元素列出服务器将要使用的特定验证机制。有效值为BASIC、DIGEST、FORM和CLIENT-CERT。服务器只需要支持BASIC和FORM。
BASIC指出应该使用标准的HTTP验证,在此验证中服务器检查Authorization头。如果缺少这个头则返回一个401状态代码和一个WWW-Authenticate头。这导致客户机弹出一个用来填写Authorization头的对话框。此机制很少或不提供对攻击者的防范,这些攻击者在Internet连接上进行窥探(如通过在客户机的子网上执行一个信息包探测装置),因为用户名和口令是用简单的可逆base64编码发送的,他们很容易得手。所有兼容的服务器都需要支持BASIC验证。
DIGEST指出客户机应该利用加密Digest Authentication形式传输用户名和口令。这提供了比BASIC验证更高的防范网络截取得的安全性,但这种加密比SSL(HTTPS)所用的方法更容易破解。不过,此结论有时没有意义,因为当前很少有浏览器支持Digest Authentication,所以servlet容器不需要支持它。
FORM指出服务器应该检查保留的会话cookie并且把不具有它的用户重定向到一个指定的登陆页。此登陆页应该包含一个收集用户名和口令的常规HTML表单。在登陆之后,利用保留会话级的cookie跟踪用户。虽然很复杂,但FORM验证防范网络窥探并不比BASIC验证更安全,如果有必要可以在顶层安排诸如SSL或网络层安全(如IPSEC或VPN)等额外的保护。所有兼容的服务器都需要支持FORM验证。
CLIENT-CERT规定服务器必须使用HTTPS(SSL之上的HTTP)并利用用户的公开密钥证书(Pulic Key Certificat)对用户进行验证。这提供了防范网络截取的很强的安全性,但只有兼容J2EE的服务器需要支持它。
l realm-name
此元素只在auth-method为BASIC时使用。它指出浏览器在相应对话框标题使用的、并作为Authorization头组成部分的安全域的名称。
l form-login-config
此元素只在auth-method为FORM时适用。它指定两个页面,分别是:包含收集用户名及口令的HTML表单的页面(利用form-login-page子元素),用来指示验证失败的页面(利用form-error-page子元素)。由form-login-page给出的HTML表单必须具有一个j_security_check的ACTION属性、一个名为j_username的用户名文本字段以及一个名为j_password的口令字段。
例如,程序清单5-19指示服务器使用基于表单的验证。Web应用的顶层目录中的一个名为login.jsp的页面将收集用户名和口令,并且失败的登陆将由相同目录中名为login-error.jsp的页面报告。

程序清单5-19 web.xml(说明login-config的摘录)
<?xml version=”1.0″ encoding=”ISO-8859-1″?>
<!DOCTYPE web-app
PUBLIC “-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN”
http://java.sun.com/dtd/web-app_2_3.dtd“>

<web-app>
<!– … –>
<security-constraint> … </security-constraint>
<login-config>
<auth-method> FORM </auth-method>
<form-login-config>
<form-login-page>/login.jsp</form-login-page>
<form-error-page>/login-error.jsp</form-error-page>
</form-login-config>
</login-config>
<!– … –>
</web-app>

9.2 限制对Web资源的访问
现在,可以指示服务器使用何种验证方法了。”了不起,”你说道,”除非我能指定一个来收到保护的URL,否则没有多大用处。”没错。指出这些URL并说明他们应该得到何种保护正是security-constriaint元素的用途。此元素在web.xml中应该出现在login-config的紧前面。它包含是个可能的子元素,分别是:web-resource-collection、auth-constraint、user-data-constraint和display-name。下面各小节对它们进行介绍。
l web-resource-collection
此元素确定应该保护的资源。所有security-constraint元素都必须包含至少一个web-resource-collection项。此元素由一个给出任意标识名称的web-resource-name元素、一个确定应该保护的URL的url-pattern元素、一个指出此保护所适用的HTTP命令(GET、POST等,缺省为所有方法)的http-method元素和一个提供资料的可选description元素组成。例如,下面的Web-resource-collection项(在security-constratint元素内)指出Web应用的proprietary目录中所有文档应该受到保护。
<security-constraint>
<web-resource-coolection>
<web-resource-name>Proprietary</web-resource-name>
<url-pattern>/propritary/*</url-pattern>
</web-resource-coolection>
<!– … –>
</security-constraint>
重要的是应该注意到,url-pattern仅适用于直接访问这些资源的客户机。特别是,它不适合于通过MVC体系结构利用RequestDispatcher来访问的页面,或者不适合于利用类似jsp:forward的手段来访问的页面。这种不匀称如果利用得当的话很有好处。例如,servlet可利用MVC体系结构查找数据,把它放到bean中,发送请求到从bean中提取数据的JSP页面并显示它。我们希望保证决不直接访问受保护的JSP页面,而只是通过建立该页面将使用的bean的servlet来访问它。url-pattern和auth-contraint元素可通过声明不允许任何用户直接访问JSP页面来提供这种保证。但是,这种不匀称的行为可能让开发人员放松警惕,使他们偶然对应受保护的资源提供不受限制的访问。
l auth-constraint
尽管web-resource-collention元素质出了哪些URL应该受到保护,但是auth-constraint元素却指出哪些用户应该具有受保护资源的访问权。此元素应该包含一个或多个标识具有访问权限的用户类别role-name元素,以及包含(可选)一个描述角色的description元素。例如,下面web.xml中的security-constraint元素部门规定只有指定为Administrator或Big Kahuna(或两者)的用户具有指定资源的访问权。
<security-constraint>
<web-resource-coolection> … </web-resource-coolection>
<auth-constraint>
<role-name>administrator</role-name>
<role-name>kahuna</role-name>
</auth-constraint>
</security-constraint>
重要的是认识到,到此为止,这个过程的可移植部分结束了。服务器怎样确定哪些用户处于任何角色以及它怎样存放用户的口令,完全有赖于具体的系统。
例如,Tomcat使用install_dir/conf/tomcat-users.xml将用户名与角色名和口令相关联,正如下面例子中所示,它指出用户joe(口令bigshot)和jane(口令enaj)属于administrator和kahuna角色。
<tomcat-users>
<user name=”joe” password=”bigshot” roles=”administrator,kahuna” />
<user name=”jane” password=”enaj” roles=”kahuna” />
</tomcat-users>
l user-data-constraint
这个可选的元素指出在访问相关资源时使用任何传输层保护。它必须包含一个transport-guarantee子元素(合法值为NONE、INTEGRAL或CONFIDENTIAL),并且可选地包含一个description元素。transport-guarantee为NONE值将对所用的通讯协议不加限制。INTEGRAL值表示数据必须以一种防止截取它的人阅读它的方式传送。虽然原理上(并且在未来的HTTP版本中),在INTEGRAL和CONFIDENTIAL之间可能会有差别,但在当前实践中,他们都只是简单地要求用SSL。例如,下面指示服务器只允许对相关资源做HTTPS连接:
<security-constraint>
<!– … –>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
l display-name
security-constraint的这个很少使用的子元素给予可能由GUI工具使用的安全约束项一个名称。
9.3 分配角色名
迄今为止,讨论已经集中到完全由容器(服务器)处理的安全问题之上了。但servlet以及JSP页面也能够处理它们自己的安全问题。
例如,容器可能允许用户从bigwig或bigcheese角色访问一个显示主管人员额外紧贴的页面,但只允许bigwig用户修改此页面的参数。完成这种更细致的控制的一种常见方法是调用HttpServletRequset的isUserInRole方法,并据此修改访问。
Servlet的security-role-ref子元素提供出现在服务器专用口令文件中的安全角色名的一个别名。例如,假如编写了一个调用request.isUserInRole(”boss”)的servlet,但后来该servlet被用在了一个其口令文件调用角色manager而不是boss的服务器中。下面的程序段使该servlet能够使用这两个名称中的任何一个。
<servlet>
<!– … –>
<security-role-ref>
<role-name>boss</role-name> <!– New alias –>
<role-link>manager</role-link> <!– Real name –>
</security-role-ref>
</servlet>
也可以在web-app内利用security-role元素提供将出现在role-name元素中的所有安全角色的一个全局列表。分别地生命角色使高级IDE容易处理安全信息。

10 控制会话超时

如果某个会话在一定的时间内未被访问,服务器可把它扔掉以节约内存。可利用HttpSession的setMaxInactiveInterval方法直接设置个别会话对象的超时值。如果不采用这种方法,则缺省的超时值由具体的服务器决定。但可利用session-config和session-timeout元素来给出一个适用于所有服务器的明确的超时值。超时值的单位为分钟,因此,下面的例子设置缺省会话超时值为三个小时(180分钟)。
<session-config>
<session-timeout>180</session-timeout>
</session-config>

11 Web应用的文档化

越来越多的开发环境开始提供servlet和JSP的直接支持。例子有Borland Jbuilder Enterprise Edition、Macromedia UltraDev、Allaire JRun Studio(写此文时,已被Macromedia收购)以及IBM VisuaAge for Java等。
大量的web.xml元素不仅是为服务器设计的,而且还是为可视开发环境设计的。它们包括icon、display-name和discription等。
可回忆一下,在web.xml内以适当地次序声明web-app子元素很重要。不过,这里只要记住icon、display-name和description是web.xml的web-app元素内的前三个合法元素即可。
l icon
icon元素指出GUI工具可用来代表Web应用的一个和两个图像文件。可利用small-icon元素指定一幅16 x 16的GIF或JPEG图像,用large-icon元素指定一幅32 x 32的图像。下面举一个例子:
<icon>
<small-icon>/images/small-book.gif</small-icon>
<large-icon>/images/tome.jpg</large-icon>
</icon>
l display-name
display-name元素提供GUI工具可能会用来标记此Web应用的一个名称。下面是个例子。
<display-name>Rare Books</display-name>
l description
description元素提供解释性文本,如下所示:
<description>
This Web application represents the store developed for
rare-books.com, an online bookstore specializing in rare
and limited-edition books.
</description>

12 关联文件与MIME类型

服务器一般都具有一种让Web站点管理员将文件扩展名与媒体相关联的方法。例如,将会自动给予名为mom.jpg的文件一个image/jpeg的MIME类型。但是,假如你的Web应用具有几个不寻常的文件,你希望保证它们在发送到客户机时分配为某种MIME类型。mime-mapping元素(具有extension和mime-type子元素)可提供这种保证。例如,下面的代码指示服务器将application/x-fubar的MIME类型分配给所有以.foo结尾的文件。
<mime-mapping>
<extension>foo</extension>
<mime-type>application/x-fubar</mime-type>
</mime-mapping>
或许,你的Web应用希望重载(override)标准的映射。例如,下面的代码将告诉服务器在发送到客户机时指定.ps文件作为纯文本(text/plain)而不是作为PostScript(application/postscript)。
<mime-mapping>
<extension>ps</extension>
<mime-type>application/postscript</mime-type>
</mime-mapping>

13 定位TLD

JSP taglib元素具有一个必要的uri属性,它给出一个TLD(Tag Library Descriptor)文件相对于Web应用的根的位置。TLD文件的实际名称在发布新的标签库版本时可能会改变,但我们希望避免更改所有现有JSP页面。此外,可能还希望使用保持taglib元素的简练性的一个简短的uri。这就是部署描述符文件的taglib元素派用场的所在了。Taglib包含两个子元素:taglib-uri和taglib-location。taglib-uri元素应该与用于JSP taglib元素的uri属性的东西相匹配。Taglib-location元素给出TLD文件的实际位置。例如,假如你将文件chart-tags-1.3beta.tld放在WebApp/WEB-INF/tlds中。现在,假如web.xml在web-app元素内包含下列内容。
<taglib>
<taglib-uri>/charts.tld</taglib-uri>
<taglib-location>
/WEB-INF/tlds/chart-tags-1.3beta.tld
</taglib-location>
</taglib>
给出这个说明后,JSP页面可通过下面的简化形式使用标签库。
<%@ taglib uri=”/charts.tld” prefix=”somePrefix” %>

14 指定应用事件监听程序

应用事件监听器程序是建立或修改servlet环境或会话对象时通知的类。它们是servlet规范的版本2.3中的新内容。这里只简单地说明用来向Web应用注册一个监听程序的web.xml的用法。
注册一个监听程序涉及在web.xml的web-app元素内放置一个listener元素。在listener元素内,listener-class元素列出监听程序的完整的限定类名,如下所示:
<listener>
<listener-class>package.ListenerClass</listener-class>
</listener>
虽然listener元素的结构很简单,但请不要忘记,必须正确地给出web-app元素内的子元素的次序。listener元素位于所有的servlet元素之前以及所有filter-mapping元素之后。此外,因为应用生存期监听程序是serlvet规范的2.3版本中的新内容,所以必须使用web.xml DTD的2.3版本,而不是2.2版本。
例如,程序清单5-20给出一个名为ContextReporter的简单的监听程序,只要Web应用的Servlet-Context建立(如装载Web应用)或消除(如服务器关闭)时,它就在标准输出上显示一条消息。程序清单5-21给出此监听程序注册所需要的web.xml文件的一部分。

程序清单5-20 ContextReporterjava
package moreservlets;

import javax.servlet.*;
import java.util.*;

/** Simple listener that prints a report on the standard output
* when the ServletContext is created or destroyed.
* <P>
* Taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
* http://www.moreservlets.com/.
* © 2002 Marty Hall; may be freely used or adapted.
*/

public class ContextReporter implements ServletContextListener {
public void contextInitialized(ServletContextEvent event) {
System.out.println(”Context created on ” +
new Date() + “.”);
}

public void contextDestroyed(ServletContextEvent event) {
System.out.println(”Context destroyed on ” +
new Date() + “.”);
}
}

程序清单5-21 web.xml(声明一个监听程序的摘录)
<?xml version=”1.0″ encoding=”ISO-8859-1″?>
<!DOCTYPE web-app
PUBLIC “-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN”
http://java.sun.com/dtd/web-app_2_3.dtd“>

<web-app>
<!– … –>
<filter-mapping> … </filter-mapping>
<listener>
<listener-class>package.ListenerClass</listener-class>
</listener>
<servlet> … </servlet>
<!– … –>
</web-app>

15 J2EE元素

本节描述用作J2EE环境组成部分的Web应用的web.xml元素。这里将提供一个简明的介绍,详细内容可以参阅http://java.sun.com/j2ee/j2ee-1_3-fr-spec.pdf的Java 2 Plantform Enterprise Edition版本1.3规范的第5章。
l distributable
distributable元素指出,Web应用是以这样的方式编程的:即,支持集群的服务器可安全地在多个服务器上分布Web应用。例如,一个可分布的应用必须只使用Serializable对象作为其HttpSession对象的属性,而且必须避免用实例变量(字段)来实现持续性。distributable元素直接出现在discription元素之后,并且不包含子元素或数据,它只是一个如下的标志。
<distributable />
l resource-env-ref
resource-env-ref元素声明一个与某个资源有关的管理对象。此元素由一个可选的description元素、一个resource-env-ref-name元素(一个相对于java:comp/env环境的JNDI名)以及一个resource-env-type元素(指定资源类型的完全限定的类),如下所示:
<resource-env-ref>
<resource-env-ref-name>
jms/StockQueue
</resource-env-ref-name>
<resource-env-ref-type>
javax.jms.Queue
</resource-env-ref-type>
</resource-env-ref>
l env-entry
env-entry元素声明Web应用的环境项。它由一个可选的description元素、一个env-entry-name元素(一个相对于java:comp/env环境JNDI名)、一个env-entry-value元素(项值)以及一个env-entry-type元素(java.lang程序包中一个类型的完全限定类名,java.lang.Boolean、java.lang.String等)组成。下面是一个例子:
<env-entry>
<env-entry-name>minAmout</env-entry-name>
<env-entry-value>100.00</env-entry-value>
<env-entry-type>minAmout</env-entry-type>
</env-entry>
l ejb-ref
ejb-ref元素声明对一个EJB的主目录的应用。它由一个可选的description元素、一个ejb-ref-name元素(相对于java:comp/env的EJB应用)、一个ejb-ref-type元素(bean的类型,Entity或Session)、一个home元素(bean的主目录接口的完全限定名)、一个remote元素(bean的远程接口的完全限定名)以及一个可选的ejb-link元素(当前bean链接的另一个bean的名称)组成。
l ejb-local-ref
ejb-local-ref元素声明一个EJB的本地主目录的引用。除了用local-home代替home外,此元素具有与ejb-ref元素相同的属性并以相同的方式使用。

Java异常处理最佳实践

原文:

http://www.onjava.com/pub/a/onjava/2003/11/19/exceptions.html

作者:Gunjan Doshi 2003-11-19

译文:

http://dev.csdn.net/article/19/article/22/22546.shtm

2003年12月4日星期四 Jplateau 译于精博

http://plateau.sicool.com

译者注:本文算是一篇学习笔记,仅供学习参考使用,有不妥之处,还请指出。2003-12-04

“本文是Exception处理的一篇不错的文章,从Java Exception的概念介绍起,依次讲解了Exception的类型(Checked/Unchecked),Exception处理的最佳实现:

1. 选择Checked还是Unchecked的几个经典依据

2. Exception的封装问题

3. 如无必要不要创建自己得Exception

4. 不要用Exception来作流程控制

5. 不要轻易的忽略捕获的Exception

6. 不要简单地捕获顶层的Exception”

――选自JAVADigest.Net对原文的介绍

“JAVADigest.Net这个站点不知道大家是否经常上,就像它的名字一样,它让我们更加有效的消化Java,或者它就像个中转站一样,至少对我是这样的,有些好的可以说是非常经典的技术文章,我都是通过它第一次获得,更多的时候我是为了偷懒才上JAVADigest.Net,因为如果是近期比较经典的文章,它上边都有介绍文字和原文连接。”

――小插曲并非常荣幸地推荐JAVADigest.Net给你

关于作者:

Gunjan Doshi works with agile methodologies and its practices and is a Sun certified Java programmer.

Related Resources

Java语言中两种异常的差别

eugene 翻译 来源:Yesky

Java提供了两类主要的异常:runtime exception和checked exception。所有的checked exception是从java.lang.Exception类衍生出来的,而runtime exception则是从java.lang.RuntimeException或java.lang.Error类衍生出来的。
它们的不同之处表现在两方面:机制上和逻辑上。

一、机制上

它们在机制上的不同表现在两点:1.如何定义方法;2. 如何处理抛出的异常。请看下面CheckedException的定义:

public class CheckedException extends Exception
{
public CheckedException() {}
public CheckedException( String message )
{
super( message );
}
}

以及一个使用exception的例子:

public class ExceptionalClass
{
public void method1()
throws CheckedException
{
// … throw new CheckedException( “…出错了” );
}
public void method2( String arg )
{
if( arg == null )
{
throw new NullPointerException( “method2的参数arg是null!” );
}
}
public void method3() throws CheckedException
{
method1();
}
}

你可能已经注意到了,两个方法method1()和method2()都会抛出exception,可是只有method1()做了声明。另外,method3()本身并不会抛出exception,可是它却声明会抛出CheckedException。在向你解释之前,让我们先来看看这个类的main()方法:

public static void main( String[] args )
{
ExceptionalClass example = new ExceptionalClass();
try
{
example.method1();
example.method3();
}
catch( CheckedException ex ) { } example.method2( null );
}

在main()方法中,如果要调用method1(),你必须把这个调用放在try/catch程序块当中,因为它会抛出Checked exception。

相比之下,当你调用method2()时,则不需要把它放在try/catch程序块当中,因为它会抛出的exception不是checked exception,而是runtime exception。会抛出runtime exception的方法在定义时不必声明它会抛出exception。

现在,让我们再来看看method3()。它调用了method1()却没有把这个调用放在try/catch程序块当中。它是通过声明它会抛出method1()会抛出的exception来避免这样做的。它没有捕获这个exception,而是把它传递下去。实际上main()方法也可以这样做,通过声明它会抛出Checked exception来避免使用try/catch程序块(当然我们反对这种做法)。

小结一下:

* Runtime exceptions:

在定义方法时不需要声明会抛出runtime exception;

在调用这个方法时不需要捕获这个runtime exception;

runtime exception是从java.lang.RuntimeException或java.lang.Error类衍生出来的。

* Checked exceptions:

定义方法时必须声明所有可能会抛出的checked exception;

在调用这个方法时,必须捕获它的checked exception,不然就得把它的exception传递下去;

checked exception是从java.lang.Exception类衍生出来的。
二、逻辑上

从逻辑的角度来说,checked exceptions和runtime exception是有不同的使用目的的。checked exception用来指示一种调用方能够直接处理的异常情况。而runtime exception则用来指示一种调用方本身无法处理或恢复的程序错误。

checked exception迫使你捕获它并处理这种异常情况。以java.net.URL类的构建器(constructor)为例,它的每一个构建器都会抛出MalformedURLException。MalformedURLException就是一种checked exception。设想一下,你有一个简单的程序,用来提示用户输入一个URL,然后通过这个URL去下载一个网页。如果用户输入的URL有错误,构建器就会抛出一个exception。既然这个exception是checked exception,你的程序就可以捕获它并正确处理:比如说提示用户重新输入。

再看下面这个例子:

public void method()
{
int [] numbers = { 1, 2, 3 };
int sum = numbers[0] + numbers[3];
}

在运行方法method()时会遇到ArrayIndexOutOfBoundsException(因为数组numbers的成员是从0到2)。对于这个异常,调用方无法处理/纠正。这个方法method()和上面的method2()一样,都是runtime exception的情形。上面我已经提到,runtime exception用来指示一种调用方本身无法处理/恢复的程序错误。而程序错误通常是无法在运行过程中处理的,必须改正程序代码。

总而言之,在程序的运行过程中一个checked exception被抛出的时候,只有能够适当处理这个异常的调用方才应该用try/catch来捕获它。而对于runtime exception,则不应当在程序中捕获它。如果你要捕获它的话,你就会冒这样一个风险:程序代码的错误(bug)被掩盖在运行当中无法被察觉。因为在程序测试过程中,系统打印出来的调用堆栈路径(StackTrace)往往使你更快找到并修改代码中的错误。有些程序员建议捕获runtime exception并纪录在log中,我反对这样做。这样做的坏处是你必须通过浏览log来找出问题,而用来测试程序的测试系统(比如Unit Test)却无法直接捕获问题并报告出来。

在程序中捕获runtime exception还会带来更多的问题:要捕获哪些runtime exception?什么时候捕获?runtime exception是不需要声明的,你怎样知道有没有runtime exception要捕获?你想看到在程序中每一次调用方法时,都使用try/catch程序块吗?

[转]Portal实现原理

三月 6, 2005

Portal实现原理

1.Portal用例
读者可以在下面三个网站上注册自己的用户,体会Portal的功能。
http://my.msn.com
http://my.yahoo.com
http://my.liferay.com

My MSN的功能最灵活强大,用户可以任意拖放操作栏目(column)和内容版块(content)的位置和个数。
My Liferay只能选择固定的栏目(column)布局,但可以在本栏目(column)内移动内容版块(content)的位置。
My Yahoo只能选择固定的栏目(column)布局,而且不能移动内容版块(content)的位置。

Portal的结构分为三层。
(1) Page
(2) Column,或者称为Pane
(3) Content,或者称为Portlet

我们来看看Portal的整个操作流程。
(1) 每个Column的下方都有一个[Add Content]按钮,让用户选择加入自己喜欢的内容。
从这里,我们知道,Portal系统里面有一个公用的Common Portlet Repository,供用户选用。

JSR168 Portlet规范里面定义了Portlet Deployment Discriptor。Common Portlet Repository以这个Portlet Deployment Discriptor的格式存放。

开源项目JetSpeed的XReg文件用来存放Common Portlet Repository的定义。

(2) 加入Content之后,用户的Page和Column里面就多了这个Content。下次用户登陆的时候,就会看到自己订制的Portal版面。
从这里,可以看出,Portal系统会纪录用户的个人Portal配置信息 � User Portal Config。

开源项目JetSpeed的PSML文件用来存放User Portal Config的定义。

——- 综上。
Add Content的整个流程为:
Common Portlet Repository –> Add Content –> Personal Portal Config

Display Portal的整个流程为:
从Personal Portal Config读取用户配置的Portlet ID –> 根据Portlet ID,从Common Portlet Repository查找详细的Portlet定义 –> 根据这个详细的Portlet定义显示这个Portlet。

2.Portal实现
我们考虑如何用Java来实现Portal。

2.1 Dynamic Include
首先,我们采用最简单的思路,我们用100个JSP文件(1.jsp, 2.jsp, 3.jsp, … 100.jsp等),代表100个Portlet。
用户页面MyPage.jsp包含用户选定的多个Portlet。
现在,假设用户选取的Portlet为1.jsp, 3.jsp, 7.jsp等3个Portlet,那么我们如何在MyPage.jsp中显示这些Portlet?最直观的做法是,用jsp:include。比如:

<table>
<tr><td>
<jsp:include page=”1.jsp” />
</td></tr>
<tr><td>
<jsp:include page=”3.jsp” />
</td></tr>
<tr><td>
<jsp:include page=”7.jsp” />
</td></tr>
</table>

由于<jsp:include>只能指定固定的jsp文件名,不能动态指定jsp文件名。我们需要把<jsp:include>翻译为Java code � RequestDispatcher.include();
下面我们换成这种写法。

java代码:
<table>
<tr><td>
<% request.getRequestDispatcher(”1.jsp”).include(request, response); />
</td></tr>
<tr><td>
<% request.getRequestDispatcher(”3.jsp”).include(request, response); />
</td></tr>
<tr><td>
<% request.getRequestDispatcher(”7.jsp”).include(request, response); />
</td></tr>
</table>

进一步改进MyPage.jsp。

java代码:
<% String[] fileNames = {“1.jsp”, “3.jsp”, “7.jsp”}; %>
<table>
<% for(int i = 0; i < fileNames.length; i++) {
String fileName = fileName s[i]; %>
<tr><td>
<% request.getRequestDispatcher(fileName).include(request, response); />
</td></tr>
<% } // end for %>
</table>

其中的fileNames的内容可以各种各样,只要RequestDispatcher能够处理。
比如Velocity,fileNames = {“1.vm”, “3.vm”, “7.vm”};
比如URL,fileNames = {“/portlet1.do”, “/portlet3.do”, “/portlet4.do”};
我们可以看到,如果我们从用户配置中读取fileNames的内容,这就是一个简单的Portal实现。

java代码:
<% String[] fileNames = (String[])session.getAttribute(“portlets.config”); %>
<table>
<% for(int i = 0; i < fileNames.length; i++) {
String fileName = fileNames[i]; %>
<tr><td>
<% request.getRequestDispatcher(fileName).include(request, response); />
</td></tr>
<% } // end for %>
</table>

2.2 Portlet Interface
下面我们来扩展这个例子。
假设每个Portlet都规定实现一个Portlet接口。

java代码:
interface Portlet {
void render(request, response);
};

MyPage.jsp如下:

<% String[] portletClassNames = (String[])session.getAttribute(“portlets.config”); %>
<table>
<% for(int i = 0; i < portletClassNames.length; i++) {
String className = portletClassNames[i];
Portlet portlet = (Portlet)Class.forName(className).newInstance(); %>
<tr><td>
<% portlet. render (request, response); />
</td></tr>
<% } // end for %>
</table>

Portlet类的示例代码如下:

public class Portlet7{
public void render(request, response){
request.getRequestDispatcher(“7.jsp”).include(request, response);
}
};

上述代码是Portal显示Portlet的核心流程的一个简化版本。
JSR168 Portlet规范里面定义了真正的Portlet接口定义。

2.3 Portlet Action
Portlet的操作包括,最大化/最小化/恢复/关闭/编辑/帮助/上下移动,等等。
这些操作都有对应的Action类。
开源项目JetSpeed的module/actions/controls目录下面包含Maximize, Minimize, Close等Action类。
开源项目Liferay的portal/action目录下面包含Maximize, Minimize, Close等Action类。

Portal的操作不仅包括上述Portlet的操作,而且包括其它更高级别的操作。
比如,Add/Move Page, Add/Move Column, 换Layout, 换Skin,之类。

2.4 Portlet Cache
我们操作Portlet的时候,往往只操作某个特定的Portlet,或者只是变化Portlet的位置。这时候,页面中大多数的Porlet的内容是不变的,只有一小块Portlet变化。
我们需要把Portlet的内容缓存起来。Portlet接口有一个render(request, response)方法,我们可以订制定制response类,截获portlet的输出,保存到Portal系统的内容Cache当中。
比如,前面提到liferay开源项目,其StringServletResponse类把Portlet的输出保存到一个String当中。

相关摘要
  • (一蓑烟雨任平生)Portal Server的机制与一般的Web Framework是独立的,Portal Server有自己的容器或者引擎来对Portlet进行处理,每个Portlet类似于Servlet。Portlet现在有两种标准,一种是以Jetspeed为主的老版本,IBM的Websphere Portal Server原先的核心API也采用Jetspeed的API,另一种是目前JCP组织制订的JSR 168标准,BEA和IBM都在自己的产品里实现了该标准,但还没有成熟。IBM的WSAD开发工具里对Portal有两种项目类型,分别支持这两种标准。

    可以将Portal做为表现层的一种类型集成到你的Web Framework中。

  • (一蓑烟雨任平生)Jetspeed是一个门户的应用管理系统,应用程序是构建在Turbine这个Web Framework上面的,可以认为Jetspeed是个用Turbine Framework开发的一个应用程序。
  • (whitehorse)portal 包括 portal server 和 portlet container ;JSR-168定义了portlet 通用api ,portlet container 的提供商需要遵循这个API,这样开发出的portlet 可以在任何一个实现jsr-168规范的portlet container下顺利移植。
    portal server 是各个开发商自行提供的,用来接受用户用求转发给相应的portlet,调整portal page 布局,单点登录等等;
    portlet container就是在servlet container上又包装了一层,portlet 类似于servlet;
    portal server 的实现一般采用 web framework 技术来构建; 比如liferay采用了struts + tiles;exo poral 采用了 jsf 等等。portlet开发在遵循规范的基础上可以采用web framework .
  • (kingkii)free portal server: Jetspeed, liferay, Jportal, etc
  • (flyisland)
  • baichenhong 写道:
    我觉的Portal的作用就是整合,故名意思 门户 嘛就是把所有的集成起来放到一起,你有oa,erp,crm 但是你觉得分开使用很不方便,那么好我可以把他们整合到一起,只要你登陆一次就可以使用所有的系统,这应该是一个很有用的东西

    你短短一句话包含的东西可是超级多。

    所谓集成分为很多层次的,Portal关注的是用户集成,包括访问界面、访问手段等。访问界面的集成并不要求应用程序一定要部署在一起。

    至于“只要你登陆一次就可以使用所有的系统”,一般称之为单点登陆“Single Sign-on”。Portal服务器基本都提供了认证框架,在此框架下开发的新应用实现SSO是很简单的;但如果对异构的系统进行SSO,这又是一个复杂而庞大的话题了。

  • (tommy_kin)拖放布局不是Portal的核心技术,只是一个personalize的功能而已,Portal功能在于应用的集成。所谓一站式访问。
参考资料:

在Struts中reset方法有什么作用(转)

创建人:王艺
创建时间:2003年6月15日星期日

第一步:
对象的可视范围:request、session、application、page。
Request:在一个请求周期内有效。就是从你点击页面上的一个按钮开始到服务器返回响应页面为止(包括响应页面)。
Session:在一个用户与服务器建立连接的整个过程中有效。
Application:在整个web应用程序内有效。
Page:仅在一个jsp页面内有效。

第二步:
ActionForm在你确定的有效期(可视范围)内是唯一的。

第三步:
在每次为ActionForm赋值前调用它的reset方法。作用是使ActionForm中的值恢复初始状态。在应用中我们可以通过在reset中为变量赋初值的方式,使得页面上的某个对象有显示值。

第四步:
可视范围与赋值前的初始化结合。
由于第二步所述特性,如果可视范围是request,则reset方法并不是很重要,因为你每次调用时都会产生一个新的ActionForm实例,所以你所操作的ActionForm不会与别人分享同时也就不会受别人的影响;如果可视范围是session,由于在session范围内此ActionForm是唯一的,所以你在session范围内需要用到此ActionForm的地方调用的都是同一个ActionForm,要是你没有在reset中对变量赋初值那么前一次调用ActionForm是为它赋的值将在此次调用时有效,这到也没什么。但是,如果恰巧再次调用时你仅仅需要为ActionForm中的一部分变量赋值,那么其余的变量将保持上一次得到的值,这样你就得到了一个“新旧混合体”,我想这多半不是你所期望的;如果可视范围是application,那其影响就更是不难理解了,这时不但是你自己会影响你自己,使用应用的其他用户的操作也会影响到你。

第五步:
知道了reset方法的作用和ActionForm在scope内唯一的特性后就为我们灵活处理ActionForm的行为提供了基础。比如说你现在需要跨过多个页面收集数据信息,这时你就可以把scope设置为session,并且不实现reset方法��这样在每个页面put数据时都不会将之前收集的数据清空,最后在你收集完数据后在Action中调用ActionForm中你自定义的初始化方法,如:resetField。
在具体的我也想不出了,还是要大家在应用时多多体会这些特性,这样才能把架构的威力发挥到最大。

《I Love Lucene》总结

一月 18, 2005

前几天看到了TheServerSide上面的一篇文章I Love Lucene,感到对自己很有帮助,稍微总结了一下理理思路。

I Love Lucene

by Dion Almaer January 2005

Introduction

简要介绍了TheServerSite原来使用的搜索的方案,并由此引出Lucene

High level infrastructure

从高层介绍Lucene的方案,这一方案主要分成了两大部分,一部分是建立索引,另一部分是对索引进行搜索。分别介绍了这两部分的主要的接口IndexBuilderIndexSearch

Building the Index: Details of the index building process

全文最重要的一个组成部分。该部分介绍了以下四个内容:

1. 应该索引的字段

2. 索引的方式:增量索引、批量索引

3. 索引源的类型

4. 索引结果的rank

What fields should compromise our index?

讲了一下不同的索引字段使用的数据类型的问题

What types of indexing?

采用了增量索引和批量索引结合的方式,定义了一个增量索引的间隔,每隔这一间隔进行一次批量索引,在间隔内的时间内进行增量索引。同时还介绍了一下Lucene中如何删除索引记录。

What to index?

ThreadIndexSource

介绍对不同的索引源索引的问题,如对数据库中的数据索引以及对文件系统中的文件的索引。还介绍了一下索引TheServerSide的论坛中的帖子时引出的一个小技巧。

How to tweak the ranking of records?

对不同的字段赋以不同的权值来对一个文档进行较合理的rank

Searching the index

Lucene的使用主要看来是建立索引比较复杂,搜索索引极其简单,这里稍微花了一点篇幅就讲清了都,主要介绍了IndexSearch类中的search方法和查询解析类CustomQueryParser

Configuration: One place to rule them all

这部分主要介绍如何使用XML文件对搜索中的一些参数(如索引存放位置、字段权值等)进行动态配置,和Lucene其实没什么关系,主要还是说的是IoC(控制反转)的东西,讲了一下Apache Digester的使用。

XML Configuration File

Digester Rules File

Web Tier: TheSeeeeeeeeeeeerverSide?

用户搜索使用的Web界面,MVC结构。

SearchAssembler Web Action

根据用户输入构造查询语句,并讲查询语句交给IndexSearch处理,同时还负责封装查询结果。

Search View

表示层使用JSP ( for legacy reason)。根据TheServerSide上面的帖子回复,似乎说TheServerSide以后要用ApacheTapestry

Conclusion

总结,就是说Lucene很好。

复杂性的计量算法的复杂性是算法运行所需要的计算机资源的量,需要的时间资源的量称作时间复杂性,需要的空间(即存储器)资源的量称作空间复杂性。这个量应该集中反映算法中所采用的方法的效率,而从运行该算法的实际计算机中抽象出来。换句话说,这个量应该是只依赖于算法要解的问题的规模、算法的输入和算法本身的函数。如果分别用N、IA来表示算法要解问题的规模、算法的输入和算法本身,用C表示算法的复杂性,那么应该有:

C =F(N,I,A)

其中F(N,I,A)N,I,A的一个确定的三元函数。如果把时间复杂性和空间复杂性分开,并分别用TS来表示,那么应该有:

T =T(N,I,A) (2.1)

S =S(N,I,A) (2.2)

通常,我们让A隐含在复杂性函数名当中,因而将(2.1)和(2.2)分别简写为

T =T(N,I)

S =S(N,I)

由于时间复杂性和空间复杂性概念类同,计算方法相似,且空间复杂性分析相对地简单些,所以下文将主要地讨论时间复杂性。

下面以T(N,I)为例,将复杂性函数具体化。

根据T(N,I)的概念,它应该是算法在一台抽象的计算机上运行所需的时间。设此抽象的计算机所提供的元运算有k种,他们分别记为O1,O2 ,..,Ok;再设这些元运算每执行一次所需要的时间分别为t1,t2,..,tk 。对于给定的算法A,设经过统计,用到元运算Oi的次数为eii=1,2,..,k ,很明显,对于每一个i,1<=i<=k,eiNI的函数,即ei=ei(N,I)。那么有:

(2.3)

其中ti,i=1,2,..,k,是与N,I无关的常数。

显然,我们不可能对规模N的每一种合法的输入I都去统计ei(N,I),i=1,2,…,k。因此T(N,I)的表达式还得进一步简化,或者说,我们只能在规模为N的某些或某类有代表性的合法输入中统计相应的ei , i=1,2,…,k,评价时间复杂性。

下面只考虑三种情况的复杂性,即最坏情况、最好情况和平均情况下的时间复杂性,并分别记为Tmax(N )、Tmin(N)和Tavg(N )。在数学上有:

(2.4)

(2.5)

(2.6)

其中,DN是规模为N的合法输入的集合;I *DN中一个使T(N,I *)达到Tmax(N)的合法输入,DN中一个使T(N,)到Tmin(N)的合法输入;而P(I)是在算法的应用中出现输入I 的概率。

以上三种情况下的时间复杂性各从某一个角度来反映算法的效率,各有各的用处,也各有各的局限性。但实践表明可操作性最好的且最有实际价值的是最坏情况下的时间复杂性。下面我们将把对时间复杂性分析的主要兴趣放在这种情形上。

一般来说,最好情况和最坏情况的时间复杂性是很难计量的,原因是对于问题的任意确定的规模N达到了Tmax(N)的合法输入难以确定,而规模N的每一个输入的概率也难以预测或确定。我们有时也按平均情况计量时间复杂性,但那时在对P(I)做了一些人为的假设(比如等概率)之后才进行的。所做的假设是否符合实际总是缺乏根据。因此,在最好情况和平均情况下的时间复杂性分析还仅仅是停留在理论上。

[转]为何不用MySQL?

一月 4, 2005

注意:这篇文档写于20005月。因此,它并不能说明MySQL的最新特性。但从中我们仍然可以了解RDBMS的一些基本概念、原理,从而在实践中更好地应用数据库,同时也才能对一些不实的炒作保持必要的警惕。

为何不用MySQL

作者:Ben Adida  译者:马维达

几乎每周、有时甚至更为频繁,总有人会问我们为何不采用MySQL作为OpenACS的RDBMS(关系式数据库管理系统)。ACS Classic team(ArsDigita)也一再地在他们的论坛上遇到同样的提问。如果MySQL对于Slashdot来说足够好的话,它也一定能够用于OpenACS,不是吗?

不对。 这篇简短的论文将尝试解释为何MySQL不仅对OpenACS来说是错误的选择,它也不应被用于任何处理关键数据的系统。

RDBMS的目的

RDBMS的目的是提供一种可靠的永久存储机制,在ACID测试中具体表述了这种机制的非常严格的特性。我将直接引用Philip Greenspun的精彩解释(以Oracle作为RDBMS的代表):

原子性(Atomicity

事务的执行结果或者被全部提交、或者被全部回滚(roll back)。要么所有的变动都生效,要么就没有变动生效。假定一个用户正在编辑一条注释,Web脚本告诉数据库“将旧注释值拷贝到审计表中,并用新文本更新活动表”。如果在拷贝之后、更新之前硬盘变满的话,审计表插入就将被回滚。

一致性(Consistency

数据库从一种有效状态转换到另一种有效状态。仅在服从用户定义的完整性约束时,一个事务才是合法的。不允许非法的事务,而且,如果不能满足某完整性约束的话,该事务会被回滚。例如,假定你定义了一条规则:论坛表中的帖子必须与有效的用户ID相关联。然后你雇用了Joe Novice来编写管理页面。Joe编写了一个删除用户页面,它不会检查删除是否会产生一些无主的论坛帖子。然而像Oracle这样的RDBMS将会进行检查,并中止任何事务,如果它产生的论坛帖子为已被删除的用户所拥有的话。

隔离性(Isolation

一个事务的结果对于其他事务是不可见的,直到该事务完成为止。例如,假定你有一个显示新用户和他们的照片的页面。按照出版者的要求,在页面中每个用户都有一张面部照片,如果用户没有照片的话就显示一幅表示无照片的图像。在新用户Jane在你的站点注册的同时,老用户Bill正在查看新用户页面。处理Jane的注册的脚本会对若干表进行插入:users、mugshots、users_demographics。如果Jane的面部照片很大的话,上述插入可能会需要一些时间。如果Bill的查询在Jane的事务提交之前开始的话,Bill根本不会在他的新用户页面上看到Jane,即使在Jane的事务中,对某些表的插入已经完成。

持久性(Durability

一旦提交(完成),事务的结果将是永久性的,并能免于未来的系统和介质故障。假设你的电子商务系统将来自某消费者的定单插入数据库表中,并指示CyberCash收取该消费者500美元的费用。突然间,在你的服务器收到CyberCash的回复之前,有人绊掉了机器的电源线。在这样的情况下,Oracle将不会忘记该定单。而且,如果有程序员将咖啡洒进了磁盘驱动器中,安装一个新磁盘,并将事务回复到咖啡泼洒时为止是可能的;数据将显示你曾试图收取某人500美元,并且还不清楚在CyberCash那里发生了什么。

如果你所要的是快速的裸存储,去使用文件系统。如果你想要在多台机器间进行共享,去使用NFS。如果你想要简单的可靠性,以对付过于简单的故障,去使用镜像。想要给它们加上SQL接口?去使用MySQL

现在,如果你所要的是这样的数据存储,它能够使你的数据集的若干方面保持恒定,能够对这些数据进行复杂的操作、而永不违反上述的那些约束,能够将多个用户同时进行的局部工作彼此隔离开来,并且能够从任何种类的故障中进行平稳的恢复,那就给你自己找一个真正的RDBMS。是的,它会比MySQL文件系统慢,就像TCP比UDP慢一样,但它们却提供了更好的服务担保。

MySQL的现状与未来

构建真正的 RDBMS是一项艰巨的任务,也许比任何其他系统问题都要艰巨。市场上的大多数产品(Oracle、Sybase、PostgreSQL、Interbase)已经进行了多年的开发,有些还超过了10或15年。

MySQL的开发人员声称他们牺牲了某些特性,以保证更好的性能。尽管这或许是一种追踪非关键数据(比如点击率追踪)的有趣方法,在处理关键数据时,牺牲完全的数据完整性是不可接受的,即使是为了速度也同样如此。

MySQL成熟时,OpenACS团队很高兴对其进行距离更近的考察。但是,MySQL团队似乎并不理解真正的ACID能力的概念和重要性:MySQL Todo在一个很长的列表中提到了“事务”,其中包括了诸如“睡眠进程占用CPU吗”这样的问题。此外,MySQL手册声称MySQL将很快通过表锁的使用来实现“原子操作”,但却“没有回滚”。这是对术语“原子的”明目张胆的误用:“原子操作”意味着或者所有操作都完成,或者没有操作完成。如果没有回滚能力的话,在一组语句的中间发生的硬件或电力故障将破坏块的原子性。

回滚不只是一种便利的特性,它是可靠的数据存储的关键性基础。

有许多很好的理由使用MySQL,但对可靠的、顺从ACID的数据存储的需求却并非是其中之一。

更多的细节

l MySQL没有子查询。

对于复杂的查询, MySQL用户必须执行两次或更多的系列查询,每一次都需要在应用和数据库间进行进程间通信或网络通信。这显著地降低了MySQL的速度优势。

l MySQL没有存储过程

l MySQL没有触发器或外键约束。

l MySQL只有表级锁定。

结语

企业级系统不会为了速度而牺牲特定的特性。RDBMS的ACID属性对于任何关键数据来说都是绝对必需品。在非 ACID顺从的系统上运行的关键网站是在自找麻烦。

OpenACS项目拒绝打破ACID测试的重要法则。我们要构建的是企业级的开放源码 Web工具包。PostgreSQL、很快还有InterBase将成为这一项目合适的候选RDBMS。而MySQL只是一个有着SQL接口的被美化的文件系统。

读后感:一直没有太深入的使用过MySQL,看到的介绍也一般都是说MySQL如何如何好的,今天看到这篇文章才发现MySQL甚至连存储过程都不支持!(不过据说最新的MySQL 5.0已经开始支持存储过程了,只是功能很弱)。原来用MS SQL Server 2000也没觉得有什么特点了,现在对比一下发现原来MS SQL Server 2000也超出了MySQL一大截,更不用说Oracle了。sigh,看来开源还有很长一段路要走(不过PostgreSQL似乎满令人振奋^_^)

« 上一页下一页 »