设计目标
客户反馈系统作为公司与客户交流的平台,几乎为所有的企业所运用,最近,公司让我负责客户反馈系统的开发。由于,公司与国外客户的业务需要,该系统必须实现中,英,日三国语言的切换(国际化要求)。在接到任务之后,我便决定尝试使用目前开源社区比较流行的apache beehive(蜂巢)和下一代ejb,ejb3.0等技术来实现这个系统。
开发环境
选择平台,开发工具
为了支持ejb3.0和beehive,我们选择jboss4.0.3应用服务器作为运行平台,它也是目前唯一提供ejb3.0容器的应用服务器。
下载和安装jboss4.0.3服务器及ejb3.0容器http://www.jboss.com/downloads/index
数据库选用 mysql5.0,由于我们没有针对特定数据库编码,因此数据库的移植也是非常方便的。
下载mysql5.0 http://dev.mysql.com/downloads/mysql/5.0.html
由于要开发ejb3.0和beehive应用,选择ecllipse这个ide,
下载ecllipse sdk 3.1 http://eclipse.org/downloads/
为了支持ejb3.0的开发,下载jboss ecllipse ide这个ecllipse插件
http://www.jboss.com/products/jbosside/downloads
pollinate是另一个ecllipse插件,它也是目前唯一支持beehive项目开发的ide,虽然它远不及weblogic 的 workshop 如此强大,但在拥有一定bea workshop 开发经验的前提下,使用pollinate并不会有太大的问题。
下载并安装pollinate插件 http://www.eclipse.org/pollinate/
beehive简介
在系统设计之前,选择一个优秀的系统框架是非常重要的。beehive是apahce的开放源代码项目。自2004年5月份,bea系统公司宣布将weblogic platform中一系列居于核心地位的运行时框架(runtime framework)开放源代码并贡献给apache项目后,这个beehive的框架就一直成为开源社区关注的焦点之一。
beehive的目标是使j2ee开发更加简单,它是一个可扩展的java应用程序框架,该框架具有针对web服务,web应用程序和资源访问的集成元数据驱动的编程模型。该框架利用了jdk1.5的最新创新,特别是jsr175元数据注解,可以减少开发人员的编码,从而提高开发效率。目前,beehive项目包括java控件,netui,java web服务元数据,能够帮助java开发人员开发出基于组件和标准的java应用。
ejb3.0简介
在客户反馈系统中,尝试使用最新的ejb3.0来实现持久层的开发。众所周知,由于ejb的复杂性使其在j2ee架构中的表现一直不是很好。ejb大概是j2ee架构中唯一一个没有兑现其能够简单开发并提高生产力的组件。而ejb3.0规范在这方面作出努力以减轻其开发的复杂性。ejb3.0取消或最小化了很多(以前这些是必须实现)回调方法的实现,并且降低了实体bean及o/r映射模型的复杂性,从而大大减轻了开发人员进行底层开发的工作量。
ejb3.0中两个重要的改进分别是:使用了java5中的元数据注解功能和基于hibernate的o/r映射模型, 在ejb3.0中,任何类型的企业级bean只是一个加了适当注释的简单java对象(pojo)。注释可以用于定义bean的业务接口、o/r映射信息、资源引用信息,效果与在ejb2.1中定义部署描述符和接口是一样的。在ejb3.0中部署描述符不再是必须的了;home接口也没有了,你也不必实现业务接口(容器可以为你完成这些事情)。
ejb3.0的配置
jboss ejb3.0建立在hibernate 3.0之上。配置数据源,实体bean需要创建hibernate. properties配置文件。在ejb3.0部署包下有一个默认的hibernate配置文件ejb3.deployer/meta-inf/hibernate.properties。修改这个文件,使实体bean使用mysql数据源,修改后的配置文件如下:
hibernate.transaction.manager_lookup_class=org.hibernate.transaction.jbosstransactionmanagerlookup
hibernate.connection.release_mode=after_statement
hibernate.transaction.flush_before_completion=false
hibernate.transaction.auto_close_session=false
hibernate.query.factory_class=org.hibernate.hql.ast.astquerytranslatorfactory
在sessionfactory创建后,自动输出schema创建语句到数据库,使用update可以创建和更新原来的schema,而不影响原来数据库中的数据
hibernate.hbm2ddl.auto=update
#hibernate.hbm2ddl.auto=create
hibernate.show_sql =true
hibernate.cache.provider_class=org.hibernate.cache.hashtablecacheprovider
# clustered cache with treecache
#hibernate.cache.provider_class=org.jboss.ejb3.entity.treecacheproviderhook
#hibernate.treecache.mbean.object_name=jboss.cache:service=ejb3entitytreecache
修改蓝色字体部分使其默认数据源改为mysqlds(jboss的数据源配置参考jboss相关文档)。
hibernate.connection.datasource=java:/mysqlds
hibernate.dialect=org.hibernate.dialect.mysqldialect
hibernate.jndi.java.naming.factory.initial=org.jnp.interfaces.namingcontextfactory
hibernate.jndi.java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
持久层设计
创建ejb3.0项目
首先使用jboss ide,创建一个ejb3.0项目,选择new->project->ejb3.0 project,
选择next按钮,在project name中输入项目名称:feedback,点击next,
接着选择jboss4.0.3 server作为项目默认的服务器。单击完成按钮,这样就生成了一个ejb3.0的项目,其根目录下的jndi.property文件指明了jboss服务器名称服务的一些配置。
数据持久层
使用ejb3.0的实体bean来实现系统的数据持久层.ejb3.0的实体bean也是一个加了注释的简单java对象(pojo)。一旦它被entitymanager访问它就成为了一个持久化对象,并且成为了持久化上下文(context)的一部分。一个持久化上下文与一个事务上下文是松耦合的;严格的讲,它隐含的与一个事务会话共存。 在ejb3.0中开发实体bean非常简单,可以像开发一般的java bean一样编程,只需做少量的注释来定义实体关系,o/r映射等,而在ejb2.1中这些都要通过开发人员自己的设计模式或者其它技术来完成的(比如,自增长主键策略)。如下定义了一个item实体bean表示客户发起的一个主题:
//声明该类为一个实体bean对象,表示客户发起的一个主题public class item implements serializable{ private static final long serialversionuid = -3318132295818643572l; private int itemid; private collectionfeedbacks; private user user; …… //此处定义了实体bean的一对一关系 (optional = false) (name = "userid", unique = false, nullable = false) public user getuser() { return user; } //声明实体bean的主键及增长策略 (generate=generatortype.auto) (name="itemid") public int getitemid() { return itemid; } //声明主题实体与反馈实体之间的的一对多关系,并且制定级联及获取方式等 (cascade=cascadetype.all,fetch=fetchtype.lazy,mappedby="item") (name="itemid") public collection getfeedbacks() { return feedbacks; } public void setfeedbacks(collection feedbacks) { this.feedbacks = feedbacks; } ……}
上例中,蓝色部分便是jdk1.5的元数据注解功能,如 便指名该类是一个ejb3.0 实体bean,这样在编译后便产生了ejb3.0 的实体bean,在部署时,jboss的ejb3.0容器能够识别ejb3.0的实体bean,并将其映射到对应的数据库表中。具体请参考ejb3.0相关技术文档。
业务逻辑层
在客户反馈系统中使用无状态会话bean来实现系统的业务逻辑层,在ejb3.0规范中,写一个无状态回话bean(slsb)只需要一个简单的java文件并在类层加上注释就可以了。这个bean可以扩展javax.ejb.sessionbean接口,但这些不是必须的。一个slsb不再需要home接口,没有哪类ejb再需要它了。bean类可以实现业务接口也可以不实现它。如果没有实现任何业务接口,业务接口会由任意public的方法产生。如果只有几个业务方法会被暴露在业务接口中,这些方法可以使用注释。缺省情况下所有产生的接口都是local(本地)接口,你也可以使用注释来声明这个接口为remote(远程)接口。
使用jboss ide创建会话bean十分方便,选择new->others->ejb3.0->session bean,打开会话bean创建向导,如图所示
session bean type中选择stateless,表明要创建一个无状态会bean。在bean name中输入要创建的session bean的名称,这里我们创建一个处理客户反馈的无状态会话bean:feedbacks。点击finish按钮后,分别产生了会话bean的接口文件和实现文件:feedbacks,无状态会话bean的业务接口:
public interface feedbacks{ public feedback addfeedback(int itemid,string title,string content,int userid,collection feedbackfiles);public void deletefeedback(int feedbackid);……}
在会话bean的业务接口中,添加会话bean的接口。其中声明该bean实现会话bean的remote接口
无状态会话bean的实现:
public class feedbacksbean implements feedbacks {public feedback addfeedback(int itemid,string title,string content,int userid,collection feedbackfiles){//此处添加实现代码……}……}
在实现中完成。
在实现中完成所有的业务逻辑编码。其中声明该bean是一个无状态会话bean。
web层设计
创建beehive项目
首先创建一个beehive项目,选择new->project->beehive project,打开beehive project创建向导,如图所示:在name中输入,应用的名称:feedbackapp。下一步中可以选择以定义的应用模板。完成上述步骤后,便生成了一个beehive project,新生成的项目已经添加了所有需要的beehive资源。接着就可以开发页面流和java控件了。
利用java control在beehive中使用 ejb3.0
beehive提供了ejb control用于获取ejb实例,但不支持ejb3.0,因此在客户反馈系统中考虑使用java control技术编写一个由ejb3.0实例的jndi名称来获取ejb实例的控件。java控件(control)架构是一个基于javabeans的轻量级组件架构,它公开了用于访问各种j2ee资源类型简单而一致的客户机模型。该框架提供了大量的函数,其中包括:基于jsr-175元数据和外部配置数据的配置,自动资源管理,上下文服务和用于创建新控件类型的可扩展设计模型。
开发java控件,首先将ejb 3.0项目引入当前beehive项目中,可以在project->property->build path中设置。接着编写一个名为ejbfinder的java control来实现ejb3.0实列的获取。
java control的开发分为两个步骤,首先要定义java control的接口如下所示:
// 指明该接口是控件ejbfinder的接口部分public interface ejbfinder { public object getejb(string ejbname); } 然后定义java control的实现部分,
// 指明该类是控件ejbfinder的实现部分public class ejbfinderimpl implements ejbfinder, java.io.serializable { //实现业务接口中的方法public object getejb(string ejbname) { try{ context context = new initialcontext(); //根据jndi名称获取ejb3.0实例,并返回该实例return(context.lookup(ejbname)); }catch(namingexception e){ e.printstacktrace(); return null; } }}
java 控件是一种可以在平台应用程序中的任何位置使用的可重用组件。上面的java control 用于由ejb实例的jndi名称,在整个context中查找,并返回该ejb实例。接着我们便可以在beehive web 应用中的pageflow(页面流)中使用这个java控件来获取ejb3.0实例了:
在需要使用ejb3.0实例的pageflow页面流控制文件中添加如下代码,声明使用该控件
.apache.beehive.controls.api.bean.control()
protected ejbfinder _ejbfindercontrol;
接着我们便可以使用这个ejbfinder控件来获取所需的ejb3.0实例了
feedbacksbean= (feedbacks) _ejbfindercontrol.getejb(feedbacks.class.getname());由于pollinate中尚不提供控件的视图,为了说明java control与pageflow(页面流)之间的关系,可以参照上面这张bea workshop中java control的参考视图,图中的主体是一个pageflow(页面流),而右侧users就是在该页面流中使用的一个名为users的java控件。
开发netui页面流
netui page flow(页面流)是一个基于apache sruts的web应用程序框架,具有易于使用,基于jsr-175元数据的单文件编程模型。该页面流构建在模型/视图/控制器元素的核心struts分离的基础之上,比如自动状态管理和与控件,xmlbeans和javaserverfaces的一流集成。页面流使用一种专门设计的批注和方法控制 web 应用程序行为的 java 类,称为“控制器 (controller)”类。在包含控制器类的目录中,也包含了页面流中使用的 java server page (jsp)。一个 jsp 要成为页面流的一部分,它必须位于页面流目录中。jsp 文件使用的特殊标记有助于绑定到数据和业务逻辑操作。控制器文件中的操作方法所实现的代码可以导致站点导航、数据传递或通过控件调用后端业务逻辑。而且控制器类中的业务逻辑与 jsp 文件中定义的表示代码相互独立,使得整个web应用的开发和维护更加清晰高效。
在pollinate中创建页面流,选择new->other->page flow wizard如图所示
单击next,在弹出框中输入页面流的名称。这里我们创建一个处理客户反馈主题的页面流topics,点击完成之后,便生成了一个基础的页面流,打开页面流所在的文件夹,双击页面流的控制文件controller.java,点击flow页打开页面流设计视图,如下所示
它由一个begin action和一个index.jsp页面组成。左侧是设计组件,包括action,page,添加所需的页面和action到页面流中。
在页面流控制文件中添加动作处理代码:
//页面流的控制文件,声明流转的目标.controller(multiparthandler = jpf.multiparthandler.memory, forwards = { .forward(name = "showtopics", path = "topiclist.jsp"), .forward(name = "addtopicaccessories", path = "accessories.jsp"), .forward(name = "showlinkmen", path = "linkmanlist.jsp"), .forward(name = "detailtopic", path = "detailtopic.jsp"), .forward(name = "newtopic", path = "newtopic.jsp") })public class controller extends pageflowcontroller { //在页面流中声明使用的控件.apache.beehive.controls.api.bean.control() protected ejbfinder _ejbfindercontrol;.apache.beehive.controls.api.bean.control() protected filecontrol _filecontrol;…… //每个action都对应一次页面流转的动作,以下action对应一个添加反馈主题的动作.action() public forward addtopic() { this.topicform = null; this.topicform = new topicform(); topicform.setfilelist(new arraylist()); this.loadlinkman(); return new forward("newtopic"); }……在页面文件中可以使用netui标记,实现绑定数据,资源声明,模板使用等功能,这样在页面文件中可以最大限度的减少java编码,使得页面更容易维护和管理。以下是一个显示主题列表的页面 topiclist.jsp:
<netui-template:template templatepage="../web/template/template.jsp"> <netui-template:section name="leftcol" > <jsp:include page="newtopicnav.jsp" /> </netui-template:section> <netui-template:section name="centercol" > <ejar-ui:window width="70%" title=""> <netui:form action="/newtopic"> <% file="../web/template/errors.jspf"%> <table> <tr> <td> <netui:span value=""></netui:span> </td> <td> <netui:select datasource="pageflow.topicform.receivername" optionsdatasource=""></netui:select> </td> </tr> <tr> <td> <netui:span value=""></netui:span> </td> <td> <netui:textbox datasource="pageflow.topicform.title"></netui:textbox> </td> </tr> <tr> <td> <netui:span value=""></netui:span> </td> <td> <netui:textbox datasource="pageflow.topicform.deadline"></netui:textbox> <netui:span value=""></netui:span> </td> </tr> <tr> <td> <netui:span value=""></netui:span> </td> <td> <netui-data:repeater datasource="pageflow.topicform.filelist"> <netui-data:repeateritem> <netui:image src="http://www.VeVb.com/htmldata/resources/images/i.p.attach.gif"></netui:image> <netui:span value=" "/> </netui-data:repeateritem> </netui-data:repeater> </td> </tr> <tr> <td> <netui:span value=""></netui:span> </td> <td> <netui:textarea datasource="pageflow.topicform.content" cols="50" rows="10"></netui:textarea> </td> </tr> <tr> <td colspan="2" height="40"> <netui:button value="" type="submit" action="/savetopicform"></netui:button> <netui:button type="submit" value=""></netui:button> </td> </tr> </table> </netui:form> <br> </ejar-ui:window> </netui-template:section> </netui-template:template>最终的页面流文件在pollinate中对应的设计视图如下所示:
在对整个工程进行build之后,页面流控制文件被编译为一个class文件和一个对应的配置文件web-inf.pageflow-struts-generated jpf-struts-config-[页面流名称].xml,该配置文件定义了一系列控制文件中注释所对应的配置,如下为其中一部分:
<struts-config> <form-beans> <form-beanname="uploadfileform" type="org.form.uploadfileform"classname="org.apache.beehive.netui.pageflow.config.pageflowactionformbean"> <set-property property="actualtype" value="org.form.uploadfileform"/> </form-bean> </form-beans> <global-exceptions/> <global-forwards> <forward name="showtopics" path="/topiclist.jsp"/> <forward name="addfeedbackaccessories" path="/feedbackaccessories.jsp"/> </global-forwards> <action-mappings><actionpath="/addaccessories" name="uploadfileform" type="org.apache.beehive.netui.pageflow.internal.flowcontrolleraction" input="addtopicaccessories"
parameter="topics.controller" scope="request" validate="true"> <forward name="newtopic" path="/newtopic.jsp"/> <!--forward "addtopicaccessories" (validationerrorforward)--> <forward name="addtopicaccessories" path="/accessories.jsp"/></action> …… <message-resources key="_defaultvalidationmessages" parameter="org.apache.beehive.netui.pageflow.validation.defaultmessages" null="true"/></struts-config>由于篇幅所限,无法详细阐述pollinate的使用,读者可以参考相关文章:用pollinate可视化开发页面流(jpf) http://dev2dev.bea.com.cn/techdoc/200504503.html
实现国际化
由于要实现中英日文的显示,采取以下步骤:
开发和编译代码时指定字符集为utf-8。eclipse可以在项目属性中设置。 使用过滤器,如果所有请求都经过一个servlet控制分配器,那么使用servlet的filter执行语句,将所有来自浏览器的请求(request)转换为utf-8,因为浏览器发过来的请求包根据浏览器所在的操作系统编码,可能是各种形式编码。request.setcharacterencoding("utf-8")。需要配置web.xml 激活该filter。在jsp头部声明:
<%@ page contenttype="text/html;charset= utf-8" %>。
在jsp的html代码中,声明utf-8:
<meta http-equiv="content-type" content="text/html; charset=utf-8">
设定数据库连接方式是utf-8。例如连接mysql时配置url如下:
jdbc:mysql://localhost:3306/feedback_db?useunicode=true&characterencoding=utf-8
其他和外界交互时能够设定编码时就设定utf-8,例如读取文件,操作xml等。不同时区时间显示
由于客户端可能处于不同的时区,因此应该显示不同服务器时间。由于与客户端有关,因此需利用一段javascript代码,从客户端获取其所在的时区偏移量。该偏移量是针对gmt时间而言的,也就是格林威治时间,以分钟为单位。function gettimezone(){ var d = new date(); document[getnetuitagname("loginform", this)][getnetuitagname("timezone", this)].value= d.gettimezoneoffset(); }获取之后将他传回服务器端并保存在session中。显示时间时根据这个偏移量来计算显示的时间。
总结
技术
ejb3.0基于hibernate3.0,大大简化了开发的难度与复杂度,使得开发人员能够快速高效的进行开发,beehive作为apache的开源项目,获得了bea的大力支持,使开发人员实现基于java组件的开发,是开源项目中构建soa的理想选择。工具
eclipse pollinate作为唯一支持beehive开发的工具,可以快速创建beehive项目,但是开发过程中,代码与设计视图的同步对应仍然存在一些问题,比如在.controller中定义的.forward,在设计视图中就不能被识别。因此与bea的workshop相比,pollinate仍然任重而道远。因此对于大型应用的开发来说,bea workshop 仍然是不二选择,对于那些初学者来说,也可以先使用bea的workshop并结合其自带帮助系统,来学习pageflow,java control,java webservice,包括门户,集成应用等高级技术。在对这些技术有了一定的理解之后,学习beehive的开发会更加快速有效。本文通过一个实例,介绍了如何结合ejb3.0与beehive快速开发j2ee应用。由于采用了一些新的技术,因此文中难免有疏漏及错误之处,欢迎大家加以纠正。同时非常感谢kevin kevman在技术方面的帮助。
新闻热点
疑难解答