top
Loading...
EJB3.0+Beehive開發客戶反饋系統
下載本文源代碼

設計目標

客戶反饋系統作為公司與客戶交流的平臺,幾乎為所有的企業所運用,最近,公司讓我負責客戶反饋系統的開發。由于,公司與國外客戶的業務需要,該系統必須實現中,英,日三國語言的切換(國際化要求)。在接到任務之后,我便決定嘗試使用目前開源社區比較流行的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 ControlBeehive中使用 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="http://www.zhujiangroad.com/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="http://www.zhujiangroad.com/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.zhujiangroad.com/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在技術方面的幫助。

作者:http://www.zhujiangroad.com
來源:http://www.zhujiangroad.com
北斗有巢氏 有巢氏北斗