Turbine项目介绍:
Turbine是一个以servlet为基础的Web Application Framework,是一个开发Web Application的工具,致力于解决一些在软件开发中重复出现的问题。这个Framework使有经验的Java开发者快速建立安全的Web Application成为可能。当然,Turbine的某些部分也可以作为独立的组件被用在其他的非Web应用中。
一个MVC架构的Web应用系统被称作Model 2。Sun公司创建了这个词,来描述使用Controller Servlet管理View与后端Model交互的J2EE应用系统。
使用Turbine开发应用系统应遵循Model 2架构。在MVC设计模式中,Model代表了业务逻辑和业务数据;View代表了用户接口;Controller处理业务流,管理Model和View。应用这个架构的好处是把业务逻辑处理和业务数据的表现分离开,使我们系统的代码更容易维护。例如:当用户界面发生变化后,应用系统业务逻辑代码不必重新经过编译,就能够和新的用户界面一起很好的工作。
Turbine是以Servlet作为基础的框架,支持多种表现层技术。Jsp未能很好的遵循MVC范例,使有些应用系统把逻辑层也混合在Jsp中,所以另外一种表现层技术¾Velocity¾-受到很多Turbine使用者关注。在任何情况下,MVC框架要求不把业务逻辑嵌入表现层。
Turbine的使用方式之一是作为MVC架构的控制器。在Turbine中,控制器的核心是Turbine Servlet 和 Action Event Handles。Turbine Servlet直接和客户接口交互,是所用用户请求的入口和用户响应的出口,管理着系统的业务流程、View和后端的Model。Action代表了对业务数据进行何种处理,Action Handle是针对不同Action所做出的处理。
Turbine能够和EJB集成在一起,把EJB作为Web Application的业务处理层,这样Turbine将成为EJB系统的客户端。我们需要做的是创建新的Turbine Service访问EJB系统。当然,Turbine也可以不使用EJB,把Turbine提供的Action、Screen或服务作为业务处理的场所。
Turbine有三种使用方式:作为MVC架构的中心控制器 ;作为被其他应用使用的组件;作为Object-Relational 工具。根据实际需要,我们可以选择其中一种使用方式。Turbine能够在开发中起到重要作用,是因为到目前为止它已经提供了200多个类。这些类能够被分成六个大的部分:
1.org.apache.turbine.modules 系统的所有模块类存放在这个包路径下,在它的下面又可以划分为五个小的子包:Page、Action、Screen、Layout、Navigation。这些子包代表了五个不同的模块。
2.org.apache.turbine.om OM代表的是Object Module,Turbine使用的所有的对象模型保存在这个包路径下。这些对象模型代表现实世界中的一些实体,例如权限系统中的用户、组、角色、权限等。
3.org.apache.turbine.services 系统所有的服务类保存在这个包路径下。Turbine为使用者提供了丰富的服务类,是turbine的核心组成部分之一。例如数据库连接池服务、日志服务、文件上传服务、XML-RPC服务等。
4.org.apache.turbine.torque Torque是一个被Turbine使用的操纵数据库的工具,支持各种开放原码和关系型的数据库,实现一个Object-Relational系统。我们用XML文件定义数据库表,然后使用Torque自动生成所有的数据库表和相对应的Java对象。Torque能够被Turbine使用,也能作为一个独立的组件被其他需要使用数据库的系统使用。
5.org.apache.turbine.util 这个路径下面保存了Turbine使用的一些辅助代码。例如Upload 服务使用的FileHandler、FileItem、MultipartStream等被保存在这个包路径的Upload子包中。
6.Org.apache.turbine Turbine Servlet保存在这里,它是Turbine的一个核心。当Turbine被当作控制器使用时,Turbine Servlet是所有请求的唯一入口和所有响应的唯一出口。
Turbine 的开始:
Jakarta Turbine 可能大家不是非常熟悉,但是它是一个很好的Web Framework(也有缺点,在以后为大家指出),先介绍一下 Turbine 的整体结构吧。
Turbine 的开发包叫:TDK(Turbine Developer Kit),它有一组Jakarta Turbine子项目组成(Turbine 项目的子项目数仅次于 Jakarta Common 项目位于第二位),列举如下:
1.Turbine:核心框架
2.Torque:JDO 的Turbine实现,利用XML技术将关系性数据库和Java Class OO 映射,足以让你耳目一新。
3.Fulcrum:服务框架,提供了大量的Web系统服务功能,个人认为Intake Service,Cache Service很棒。
4.Maven:Java 项目更新工具,基于POM(project object model)概念,目前只支持Turbine。
5.JCS(Java Caching System):是服务器段Java程序的分布式缓存系统,极棒的概念。
6.Stratum:Turbine 和 Avalon 的移植工具。
还有一个和 Turbine 轻密如战友的 Web 模版项目 Jakarta Velocity.
1.Turbine 的历史:
我用Turbine的时候是 2001年1月(很早吧),当时的 TDK 版本是1.1.A13,非常的不稳定,并且有相当多的 Bug.当时促使我们用Turbine的原因是其良好的MVC架构.当时EJB还根本没有火起来,Struts也只是一个雏形而已,而当时的Turbine已经发展的不错了(当然还有很多的问题).
我当时选择了Turbine来开发一套物流管理系统,遇到的最大的困难是帮助文档奇缺,这是Microsoft的开发组件远远胜于当时的Java的一点(注1).
但是我觉得当时的Turbine News Group的氛围非常的好,基本上问题都能得到解决,而且Turbine的升级速度要比现在快的多(我觉的最近的Turbine组有点懒,TDK(Turbine)3.0 Released 版本都快等了一年了,我的天).虽然文档很缺(注2),但是有这个新闻组的帮助,项目做的还比较顺利,一个附带的好处是让我习惯了看E文文档.
Turbine 的发起人之一是Brett McLaughlin,我比较崇敬的程序员之一(我顶礼膜拜的是伟大的Anders).Turbine的开发宗旨是:"Turbine is a servlet based framework that allows experienced Java developers to quickly build secure web applications."(Turbine 提供给有经验的Java开发者一个快速开发安全Web应用的Servlet平台),我深有感触的觉得的确如此.
日志:
2000/9 Turbine 诞生了
2001/3 Turbine 1.1 Released
2001/6 Turbine 2.1 Released
2002/12 Turbine 2.2 Released
期待 3.0
2.一些看法
我现在在做 EJB + Struts 的开发,结合现在的经验谈谈我作为第一线的软件设计开发人员的想法.
EJB 吸引大家的是它的标准中的容器(Container),组件(Component),分布式概念.我说一句实话,EJB 真的只适合于做大项目(ERP,SCM,CRM 等等),如果你只是做一个 Web Application(1000个人使用,提供100个动态页面的话)真的没必要自己给自己找麻烦.
EJB 和数据层的结合用的是 CMP (EJB 2.0 & 2.1 建议尽量使用CMP来取代BMP以提高性能),但是你要用EJB进行抽象、多态是在是太困难了.IBM DeveloperWorks 上的一句话是我感同身受:"CMP 是开发针对于意志薄弱的开发者来说是不适合的".
而 Turbine 的 Torque OM 机制使得你能充分发挥你的想象力和创造力,我一直认为计算机语言有好坏之分,标准就是它是否能使你的想象得到实现并且十分漂亮.Turbine Framework 对于开发者来说是十分优秀的.
(注3)
3.TDK 的相关技术
Ant:Turbine 项目的编译技术使用的都是Ant.熟悉Ant至关重要.TDK 的核心编译文件是../webapps/yourproject/WEB-INF/build/build.xml 和 build.properties.熟悉Ant的人一看,拷,太熟悉了!!!
Log4j:Turbine 项目的日志纪录用的是 Jakarta 组的Log4j.
---------------------------------------------------------------------------------
# -------------------------------------------------------------------
#
# L O G G I N G
#
# -------------------------------------------------------------------
# We use Log4J for all Turbine logging and we embed the log4j
# properties within our application configuration.
#
# NOTE:
# The presence of ${webapp} in the logging configuration
# is not a mistake. Internally the value of ${webapp}
# is set so that you can use it with standard log4j
# properties to get logs to appear in your
# webapp space.
# -------------------------------------------------------------------
log4j.category.default = INFO, default
log4j.appender.default = org.apache.log4j.FileAppender
log4j.appender.default.file = ${webapp}/logs/turbine.log
log4j.appender.default.layout = org.apache.log4j.PatternLayout
log4j.appender.default.layout.conversionPattern = %d [%t] %-5p %c - %m%n
log4j.appender.default.append = false
log4j.category.sql = DEBUG, sql
log4j.appender.sql = org.apache.log4j.FileAppender
log4j.appender.sql.file = ${webapp}/logs/sql.log
log4j.appender.sql.layout = org.apache.log4j.PatternLayout
log4j.appender.sql.layout.conversionPattern = %d [%t] %-5p %c - %m%n
log4j.appender.sql.append = false
log4j.category.vdebug = DEBUG, vdebug
log4j.appender.vdebug = org.apache.log4j.FileAppender
log4j.appender.vdebug.file = ${webapp}/logs/vlog.log
log4j.appender.vdebug.layout = org.apache.log4j.PatternLayout
log4j.appender.vdebug.layout.conversionPattern = %d [%t] %-5p %c - %m%n
log4j.appender.vdebug.append = false
---------------------------------------------------------------------------------
以上的配置是位于../WEB-INF/conf/下的TurbineResources.properties文件中,看看文件名就知道他有多重要了,我会在以后的文章中详细介绍这个文件的.:-)
Velocity:Jakarta 项目组极棒的模版项目,它的配置信息也在TurbineResources.properties中
---------------------------------------------------------------------------------
# -------------------------------------------------------------------
#
# V E L O C I T Y S E R V I C E
#
# -------------------------------------------------------------------
# The location of Velocity configuration file, relative to webapp root
# These properties will override the default properties set by Velocity.
# You should specify the path to the templates directories as well as
# the path to the log file and they should also be relative to webapp root
services.VelocityService.template.extension=vm
services.VelocityService.default.page.template = /Default.vm
services.VelocityService.default.layout.template = /Default.vm
services.VelocityService.runtime.log=/logs/velocity.log
# 注意定义为中文 GB2312
services.VelocityService.input.encoding=GB2312
services.VelocityService.velocimacro.library = GlobalMacros.vm
services.VelocityService.resource.loader = file
services.VelocityService.file.resource.loader.description = Velocity File Resource Loader
services.VelocityService.file.resource.loader.class = org.apache.velocity.runtime.resource.loader.FileResourceLoader
services.VelocityService.file.resource.loader.path = /templates
services.VelocityService.file.resource.loader.cache = false
services.VelocityService.file.resource.loader.modificationCheckInterval = 2
services.VelocityService.resource.loader = classpath
services.VelocityService.classpath.resource.loader.description = Velocity Classpath Resource Loader
services.VelocityService.classpath.resource.loader.class = org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader
services.VelocityService.earlyInit = true
---------------------------------------------------------------------------------
Velocity 使用的详细介绍可参看我转贴的<>,这篇文章我很早就收集了,实在忘了是那位高人之作(如果是你,请指正,我也真的感谢你的文章对我的帮助).
Turbine有五个模块:
Turbine有五个模块组成:Page、Action、Layout、Navigation、Screen,每一个模块服务于特定的目的,它们是执行用户的请求和生成所需页面的真正场所。
从对象封装的角度看,Page对象包含Layout对象,Layout对象又包含Navigation对象和Screen对象。实际上,这种封装关系非常符合网页页面的结构特征。以下是对每个模块的介绍:
1.Page模块:Page模块是产生页内容的一系列事件链中的第一个模块,包括了其他Action、Layout、Screen、Navigation模块。在执行中,Page对象首先检查它是否有Action需要执行。有Action,执行Action。之后,Page向Screen对象请求它的Layout,并执行Layout,最终返回Html页面的内容。
2.Action模块:Action代表用户想怎样处理请求。例如,用户提交一个Html表单,表单的一个Hidden字段将保存用户想怎样处理表单数据的Action信息,系统能够根据Action的信息判断是把表单的数据保存到数据库,或是验证数据的有效性,或是其他的处理。Action有利于把用户对数据的不同处理划分为不同的可重用的模块,有利于把用户请求的处理从Turbine Servlet中分离出来,也有利于Turbine和EJB的集成,因为Action能够仅仅简单的调用EJB中的逻辑处理。Page负责执行Action。
3.Layout模块:这个模块定义了Web Page实际的物理布局。在Turbine中,网页被定义为Screen和Navigation这两个部分,如图2所示。Layout执行Screen完成构建网页的Body部分,执行Navigation完成构建网页的其余部分。
4.Screen模块:这个模块代表了网页的Body。Layout执行这个模块,产生一个网页的主体Html。
5.Navigation模块:这个模块代表网页的Header和Footer。Layout执行这个模块,产生网页主体之外的Html。
Page,Action,Layout,Screen,Navigation都被定义为抽象类,使用时需要有实现类。Turbine为这些抽象类提供了缺省实现,例如:DefaultAction就是Action抽象类的缺省实现。我们可以根据自己的需要,创建自己的抽象类实现,修改TurbineResources.properties文件中的配置,替换抽象类的缺省实现。
Turbine为每一个模块提供了专门的装载器,装载器负责动态地装载每一个模块。因为装载器继承了HashTable的特性,所以它能缓存模块,提高装载模块的速度。装载器采用了Factory设计模式,因此在TurbineResource.property文件中定义”Loader.Classpath”属性之后,装载器根据这个属性值,在指定的路径下找到正确的文件,装载并创建合适的类实例。
装载类是静态类,整个系统中只存在一个类实例,不需要在TurbineResources.properties文件中注册。所有的静态类都不需要注册。
Turbine安全服务:
Turbine提供了一个简单、强大的安全服务。Turbine基于安全对象模型构建安全服务:用户、组、角色、权限。
一个角色是一个或多个不同权限的集合;一个或多个角色对应一个用户;组是被分配了权限的用户的集合。Turbine为User、Group、Role、Permission等实体提供了对应的User、Group、Role、Permission接口,并为这些接口提供了缺省的实现类。这些接口和实现类存在于org.apache.turbine.om包路径下。
Turbine提供了BaseSecurityService、SecurityService、TurbineSecurity三个安全服务抽象类,我们只需要实现BaseSecurityService抽象类。根据存储安全信息的介质是DB或LDAP,Turbine为BaseSecurityService提供了DBSecurityService和LDAPSecurityService两个实现类。这两个类分别实现了自己的getACL(User )方法,用于得到一个用户的访问权限列表AccessControlList。
当用户请求提交给Turbine后,Turbine验证用户的有效性。对于有效的用户,Turbine执行DBSecurityService或LDAPSecurityService的getACL(User)方法,得到这个有效用户的AccessControlList,并缓存AccessControlList对象在Run Object中。有了AccessControlList数据后,Turbine后端的Modules能够很方便地检查用户能做什么,不能做什么。
Turbine提供了UserManager抽象类管理系统的用户。根据安全信息的存储介质是LDAP或DB,Turbine提供了LDAPUserManager和DBUserManager两个实现类,分别管理LDAP或数据库中的用户信息。
Turbine工作机制:
当一个新的请求到来后,Turbine Servlet首先检查发出请求的用户是否已经存在一个对应的Session对象。如果用户还不存在Session对象, Turbine将请求重新定向到网站的HomePage ( 缺省情况下,HomePag是用户的登录页面,也可以在TurbineResources.properties文件中配置为其它页面 )。这样每一个用户的Session信息将能够被包含在URL中,系统可以追踪每一个用户的会话信息。如果HomePage在TurbineResources.properties文件中配置为非登录页面,新创建的Session对象仅仅能够存储一些临时性的数据,而当用户真正登录系统之后,Session中可以通过保存一些长久性的数据。
为用户创建了Session之后,Turbine创建RunData Object,并把诸如Action的名字、Screen的名字或其他信息缓存到RunData object中。RunData Object将在整个系统中被传递,为Servlet后端的模块提供信息。Turbine会为每一个请求创建一个新的RunData Object。
然后,Turbine Servlet检查RunData Object中Action的值是否是LoginUser或是LogoutUser。这个检查必须发生在SessionValidator执行之前,确保SessionValidator执行之前用户已经登录系统或者用户已经从系统中退出。如果Action的值是”LoginUser”,Turbine执行LoginUser Action,鉴别用户是否是一个已经注册过的用户。对于已经注册过的用户,更新用户的访问记录;对于未注册的用户,将Screen设定为注册用户页面。如果Action的值LogoutUser,Turbine执行LogoutUser Action,从Session中删除用户的登录信息,使用户成为未登录状态。
之后,Turbine Servlet将执行SessionValidator。SessionValidator从Session中获取User Object,将检查用户是否已经登录系统。如果用户还没有登录系统,用户的请求被拒绝,Screen将被定向为Login;如果是已经登录系统的用户,更新用户的访问记录;如果用户第一次登录系统,Screen被定向到系统的HomePage。如果用户不首先经过登录系统就能够浏览一些页面,我们必须实现自己的SessionValidator,让它什么也不做。对于有安全性要求的页面,我们应当定义一个执行SessionValidator的Layout。当Page执行Layout的时候,Layou将调用SessionValidator作一些安全检查。
确认发出请求的用户是一个有效用户后,Turbine Servlet把这个用户的访问控制列表AccessControlList保存在RunData Object和Session中,Servlet后端的Model能够很容易访问权限方面的信息。
做完这些工作之后,Turbine Servlet把控制权转移给Page,然后Page引发执行一个事件链,直到得到整个Web页内容。首先,Page检查是否有Action被定义,有就执行Action;然后,Page向Screen请求Layou,并执行Screen返回的Layout,Layout又会执行Navigation和Screen。最后,Page得到整个页面的Html内容,并把控制还给Turbine Servlet。Turbine Servlet把得到的Html内容作为响应发送给用户。