設計目標
  客戶反饋系統作為公司與客戶交流的平臺,幾乎為所有的企業所運用,最近,公司讓我負責客戶反饋系統的開發。由于,公司與國外客戶的業務需要,該系統必須實現中,英,日三國語言的切換(國際化要求)。在接到任務之后,我便決定嘗試使用目前開源社區比較流行的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 collection feedbacks;  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.survivalescaperooms.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在技術方面的幫助。
新聞熱點
疑難解答