總 攬
      一、Server方組件結(jié)構(gòu)
      EJB是一種Server方的組件結(jié)構(gòu),它可以非常簡單的開發(fā)基于java的企業(yè)級的分布式對象應(yīng)用。使用EJB可以開發(fā)出易升級的、可靠的、安全的應(yīng)用程序,而不用獨立開發(fā)復(fù)雜的分布式對象框架;EJB可以迅速開發(fā)服務(wù)方應(yīng)用程序,快速建立基于java的服務(wù)方組件。EJB被設(shè)計用來實現(xiàn)企業(yè)中間件服務(wù)的可移植和可重用性。

      如果你對企業(yè)計算很陌生,這些概念對你很有用,EJB是個很復(fù)雜的主題,應(yīng)該被充分的解釋。在這一章我們討論有關(guān)EJB的主要概念。首先,我們先討論開發(fā)企業(yè)級軟件是為了干什么?為什么說像EJB的預(yù)包裝的分布式對象體系可以簡化你的生活?在討論中,你將會對服務(wù)端的組件結(jié)構(gòu)有宏觀的了解。


      服務(wù)端組件結(jié)構(gòu)的需要
      我們必須首先了解開發(fā)者在建立和配置服務(wù)端環(huán)境下的組件時通常需要什么?同時,我們將解決圍繞服務(wù)端開發(fā)所出現(xiàn)的問題,將看到建立一套像EJB標準體系結(jié)構(gòu)的必要性。


      軟件組件??一個軟件組件是一段代碼,它用來實現(xiàn)一系列定義好的接口。組件不是完整的
      應(yīng)用程序??它們不能被獨立運行。更貼切的說,它們是看作是許多大型問題分割成的小問題。
      軟件組件的思想非常有用。公司可以買來定義好的可用來解決某一問題的模塊,將它和其他組件一起編譯用以解決大型問題。
      組件結(jié)構(gòu)??為了使組件開發(fā)過程更加容易,需要為建立、管理、維持組件建立規(guī)范。
      開發(fā)組件的開發(fā)工具??建立組件時,應(yīng)該允許開發(fā)者集中精力在組件的背后開發(fā)核心邏輯,使開發(fā)者不需要考慮太多的標準細節(jié)問題,從而快速開發(fā)應(yīng)用程序。例如IDE:Symantec的Visual
      Cafe,IBM的VisualAge for Java,Inprise的Jbuilder 2,這些工具可以幫助你快速的建立和調(diào)試組件。
      管理配置好的組件容器??組件容器為你的組件運行提供了一個運行時環(huán)境。同時也提供了一套供大多數(shù)組件使用的通用服務(wù)。
      配置和維持組件工具??從組件提供商購買了組件后,還需要有一套工具來幫助配置和維持這些組件。

      Java:完美實現(xiàn)組件結(jié)構(gòu)
      對于成功運用在解決商業(yè)問題的一個組件,無論是組件開發(fā)商還是使用組件的客戶都必須遵守調(diào)用組件方法的語法和語義。開發(fā)商出版有關(guān)調(diào)用組件的規(guī)范,客戶代碼必須遵守它們。

      為了防止當(dāng)開發(fā)商提供了一個新版本的組件,或更改了組件規(guī)范,客戶方就必須重新編寫或編譯它們的代碼,因此面向?qū)ο笤O(shè)計提出了一種新的設(shè)計方法:通過從組件的實現(xiàn)中分離出接口。

      組件接口
      組件邏輯
      為了使接口/實現(xiàn)分離這種方式變得更有效,開發(fā)者必須為組件接口寫客戶代碼(這被稱為基于接口的程序設(shè)計),通過這種分離,可以改變服務(wù)方邏輯而不用更改客戶方代碼。


      Java中的組件結(jié)構(gòu)
      現(xiàn)在讓我們了解什么是組件結(jié)構(gòu),看看在Java世界里存在那些組件結(jié)構(gòu)。首先,你應(yīng)該了解什么是JavaBeans,JavaBeans組件是小的應(yīng)用程序塊,可以使用JavaBean去集合成大型的組件,從而編譯完整的應(yīng)用程序。然而,你不能配置一個JavaBean,因為一個JavaBean不是一個完全的應(yīng)用程序。JavaBean可以幫助你構(gòu)建更大的可配置的軟件。因為不需要配置,JavaBean不需要運行時環(huán)境,也不需要容器來對它進行實例化、破壞、提供其他服務(wù)的操作。應(yīng)用程序本身是由許多JavaBean構(gòu)成的。

      相比較,EJB標準定義了一個組件結(jié)構(gòu)來配置組件,被稱為企業(yè)級的Beans。企業(yè)級的Beans是比較大的、粗糙的被用來配置的應(yīng)用程序組件。他們能被破壞,也能被用來和其他組件組合成更大的應(yīng)用程序系統(tǒng)。可配置組件在容器內(nèi)被配置,容器提供了對組件的運行時服務(wù)。例如實例化。

      企業(yè)級Beans和兩種其他的Java組件十分相似:applets和servlets。Applets可以在Web頁中配置,瀏覽器的Appletviewer為其提供了運行時的容器。Servlets可以在Web
      Server中被配置,Webserver的servlet
      engine為提供運行時的容器。企業(yè)級Beans可以在應(yīng)用程序服務(wù)器中被配置,應(yīng)用服務(wù)器為其提供了運行時的容器。
      它們?nèi)咧g真正的不同是每個組件類型可以延伸的域大小。
      Applets是輕便Java程序,它能夠被任意的下載和運行。例如它可以從Web Server中下載到瀏覽器。
      Servlets是可以被用來延伸Web
      server功能的網(wǎng)絡(luò)組件。它是面向請求/回答的,從許多客戶端獲得請求,再給它們發(fā)應(yīng)答。這樣使得它被廣泛用于執(zhí)行Web任務(wù)。
      Applets和servlets適用于客戶方操作。而企業(yè)級Bean不擴展客戶端操作,它是服務(wù)端組件,執(zhí)行服務(wù)端操作;例如執(zhí)行復(fù)雜運算、執(zhí)行大量的商業(yè)傳輸。


      服務(wù)端所需
      一個完整的組件結(jié)構(gòu)遵循以下方式:
      開發(fā)者寫可重用組件
      提供商寫組件容器:用以給組件提供運行時環(huán)境和服務(wù)
      提供商提供開發(fā)、配置和維持工具。
      這些方式保證了可重用性。

      多層結(jié)構(gòu)
      服務(wù)方配置是用來支持用戶同時執(zhí)行并發(fā)、安全、可靠、有效的操作的軟件。
      服務(wù)方配置一般被分成多層。每層實現(xiàn)不同的功能,每層有一個或多個組件。注意:層是個抽象的概念,在物理上它并不存在。下面有個分層的例子:
      代表層:這些容器組件處理用戶接口和用戶交互。例如,代表層是一個單獨的應(yīng)用程序,可以用VB來寫。基于Web配置的代表層可以使用java
      servlets,Java server pages或java applets。
      業(yè)務(wù)邏輯層:是用來解決業(yè)務(wù)問題的容器組件的集合。這些組件常被用來處理高執(zhí)行度的工作,它們常用安全性的語言來寫,例如:Java、C。
      數(shù)據(jù)層:被業(yè)務(wù)邏輯層用來保持狀態(tài)的持久性。數(shù)據(jù)層的中心是一個或多個數(shù)據(jù)庫。
      分層的優(yōu)點是盡量隔離各層。

      兩層結(jié)構(gòu)
      通常,大多數(shù)配置是兩層結(jié)構(gòu)。將業(yè)務(wù)邏輯層和另外兩層的一個合并:可能和代表層合并。可能和數(shù)據(jù)層合并。
      代表層和業(yè)務(wù)邏輯層合并
      如把代表層和業(yè)務(wù)邏輯層這一層作為客戶端,數(shù)據(jù)層作為服務(wù)端,則形成fat客戶端,而thin服務(wù)端的情況。在兩層結(jié)構(gòu)中,客戶端應(yīng)用程序通過數(shù)據(jù)庫橋API與數(shù)據(jù)層相連。例如:ODBC、JDBC。

      這樣的兩層結(jié)構(gòu)有以下特征:
      配置代價非常高。數(shù)據(jù)庫驅(qū)動必須在每一個客戶層上安裝和配置。
      數(shù)據(jù)庫驅(qū)動交換代價高。轉(zhuǎn)接一個數(shù)據(jù)庫驅(qū)動,需要在各個客戶端重新安裝客戶端驅(qū)動。
      數(shù)據(jù)庫類型轉(zhuǎn)型代價高。
      業(yè)務(wù)邏輯移植代價高。
      數(shù)據(jù)庫連接代價高。
      網(wǎng)絡(luò)性能發(fā)揮低。
      將業(yè)務(wù)邏輯層部分并入數(shù)據(jù)層
      形成客戶端thin,而服務(wù)端fat的情況。

      N層結(jié)構(gòu)
      其中將代表層、業(yè)務(wù)邏輯層、數(shù)據(jù)層各自為一層。
      特點:
      配置代價低。
      數(shù)據(jù)庫交換代價低。
      業(yè)務(wù)邏輯移植代價低。
      可以和防火墻結(jié)合配置安全部分。
      資源可以被重用。
      每層都有較大的靈活性

      J2EE技術(shù):
      J2EE是中間件服務(wù)套件,對于服務(wù)端應(yīng)用的開發(fā)者來說,使開發(fā)變得更容易。它包含:
      EJB:它定義了怎樣去寫服務(wù)方組件、提供了一個在組件和管理它們的應(yīng)用服務(wù)器之間的標準。EJB是J2EE的核心。
      RMI和RMI-IIOP:RMI??遠程過程調(diào)用;RMI-IIOP是RMI的擴展,它可以使用IIOP協(xié)議,可以被CORBA整合使用。
      JNDI:JNDI用來在網(wǎng)絡(luò)上區(qū)分組件和其他資源的位置。
      JDBC:是聯(lián)系數(shù)據(jù)庫的橋梁

      推出EJB1.0后的幾個月,第一個基于EJB的應(yīng)用服務(wù)BEA’s WebLogic就誕生了。


      二、EJB總攬

      EJB采用divide-and-conquer的方式來實現(xiàn)服務(wù)方的計算。事實上,EJB標準定義了6個不同的部分協(xié)同工作。每個部分都被作為EJB配置成功的關(guān)鍵。在這兒,我們分別討論它們的作用。


      Who’s Who in EJB
      在EJB的世界里,業(yè)務(wù)解決方案被分為四個階段發(fā)展:
      1、
      業(yè)務(wù)邏輯模塊化。ERP、金融、電子商務(wù)提供商將產(chǎn)品模塊化,集成在可重用EJB組件中,這樣就意味著任何有EJB和J2EE知識的人都可以使用這些組件。
      2、 業(yè)務(wù)組件需要運行在分布式的、企業(yè)級配置的多層環(huán)境中。這樣就需要不同的中間件,推動了中間件的發(fā)展。
      3、 應(yīng)用服務(wù)器和可重用組件捆綁。
      4、 完整的系統(tǒng)配置。

      EJB有好的可移植性,它被分為完全不同的6個部分:

      6個部分之間的關(guān)系

      The relationship between EJB servers and EJB containers.
      EJB規(guī)范定義了完成一個基于EJB組件的分布式應(yīng)用所需要的六個角色,這六個角色可以由不同的廠商來擔(dān)當(dāng),也可以某個廠商擔(dān)當(dāng)多個角色。這六個角色是:
      Enterprise Bean Provider EJB組件開發(fā)者,負責(zé)編寫EJB組件,EJB組件開發(fā)者是應(yīng)用領(lǐng)域的專家。
      Application Assembler 應(yīng)用組合者,負責(zé)將各種EJB組合成一個完整的應(yīng)用系統(tǒng)。
      Deployer 部署者,負責(zé)將包含EJB組件的ejb-jar文件部署到應(yīng)用服務(wù)器中。
      EJB Server Provider EJB服務(wù)器提供者,負責(zé)實現(xiàn)一些底層的系統(tǒng)服務(wù),如交易管理等。EJB服務(wù)器提供者是系統(tǒng)領(lǐng)域的專家。
      EJB Container Provider
      EJB容器提供者,負責(zé)提供EJB組件的運行環(huán)境,EJB容器提供者和EJB服務(wù)器提供者一般是由相同的廠商擔(dān)當(dāng),提供的產(chǎn)品叫應(yīng)用服務(wù)器。
      System Administrator 系統(tǒng)管理員負責(zé)為EJB服務(wù)器和容器提供一個企業(yè)級的計算和網(wǎng)絡(luò)環(huán)境。

      EJB配置的物理部分分為以下幾部分:
      EJB Container
      是裝載Enterprise Beans及Enterprise
      Beans生存的空間,是Beans在運行時相互聯(lián)接的接口,Container必須實現(xiàn)與Beans之間的約定,Continer提供者也應(yīng)該提供配置工具以便能方便地配置Beans,使其適合各種運行環(huán)境。

      EJB Server
      主要處理復(fù)雜的底層任務(wù),如分布式對象、分布式事務(wù)處理的管理、系統(tǒng)OS級的訪問、網(wǎng)絡(luò)、數(shù)據(jù)庫訪問等。EJB Server與EJB
      Container之間的合約在EJB 1.0 規(guī)范中有詳細說明。大多EJB Server提供者也是EJB Container提供者。
      開發(fā)Beans
      一、會話Beans介紹
      按功能可把EJB分為兩類:Session Beans 與 Entity Beans。
      企業(yè)級Bean類
      為了使bean可以在任一容器中工作,bean必須被附在接口中。在EJB中,在enterprise bean
      class中提供了企業(yè)級bean組件的實現(xiàn)。這是個簡單的遵循接口的java類。
      一個enterprise bean class 包含對組件的實現(xiàn)細節(jié)。會話bean的實現(xiàn)不同于實體bean的實現(xiàn),
      一個Session Beans針對單一的客戶完成一次連接或會話,其生存直到客戶完成連接與會話,或系統(tǒng)意外中止。當(dāng)一個新的客戶從EJB
      Server訪到一個Session Beans時,那么EJB Container創(chuàng)建一個新的Session
      Beans實例,其運行直到會話結(jié)束,Session Beans必須實現(xiàn)接口javax.ejb.SessionBean。
      Entity Beans實現(xiàn)接口javax.ejb.EntityBean,其描述了特定數(shù)據(jù)源中的數(shù)據(jù),能長時間存在于EJB
      Container中,不會隨系統(tǒng)的意外中止而消失,并且可以讓多個客戶同時訪問。
      EJB規(guī)范定義了許多bean類能夠?qū)崿F(xiàn)的標準接口。定義了所有的bean類必須有的方法。容器調(diào)用這些方法用來管理bean。
      所有bean類(無論是會話bean還是實體bean)必須實現(xiàn)的最基本的接口是javax.ejb.EnterpriseBean接口。
      public interface javax.ejb.EnterpriseBean extends java.io.Serializable
      {
      }
      值得注意的是:它繼承了java.io.Serializable。
      所有的會話bean必須實現(xiàn)javax.ejb.SessionBean,
      所有的實體bean必須實現(xiàn)javax.ejb.EntityBean.
      EJB對象
      當(dāng)客戶想使用enterprise bean
      class的一個實例時,客戶不必直接在實際的實例上調(diào)用方法,調(diào)用過程被EJB容器截取,bean實例被容器中對象所代表。
      1、Enterprise bean
      class不能通過網(wǎng)絡(luò)直接被調(diào)用,我們知道EJB容器可以操縱網(wǎng)絡(luò),因此它通過容器將bean包裝成可在網(wǎng)絡(luò)上使用的對象。
      2、通過截取請求,容器可以自動執(zhí)行許多必要的管理工作。
      3、EJB容器可以跟蹤哪個方法被調(diào)用,在系統(tǒng)管理者的用戶接口上顯示其用法等等。
      因此,EJB容器可以看作間接的存在于客戶代碼和bean之間的層。這個間接的層使用單獨的網(wǎng)絡(luò)對象來表示自己,這個對象稱為EJB對象。
      EJB對象作為容器物理的部分;所有的EJB對象都有針對容器特殊要求的代碼。因此,容器提供商提供專門工具,用來自動為EJB對象產(chǎn)生類文件。
      遠程接口
      我們前面了解到,bean客戶調(diào)用EJB對象上的方法來代替調(diào)用bean,為了執(zhí)行它,EJB對象必須復(fù)制bean類中的每個業(yè)務(wù)方法。但是,怎樣才能使自動產(chǎn)生的EJB對象知道復(fù)制了哪個方法呢?這就用到了bean提供者寫的一個特殊的接口,這個接口復(fù)制所有的與bean類相關(guān)聯(lián)的業(yè)務(wù)邏輯方法。這個接口被稱為遠程接口。

      這個接口必須遵循EJB規(guī)范的定義,所有的遠程接口必須從sun公司提供的通用接口繼承而來,即javax.ejb.EJBObject。

      EJB對象
      public interface javax.ejb.EJBObject
      extends java.rmi.Remote
      {
      public abstract javax.ejb.EJBHome getEJBHome()
      throws java.rmi.RemoteException;

      public abstract java.lang.Object getPrimaryKey()
      throws java.rmi.RemoteException;

      public abstract void remove()
      throws java.rmi.RemoteException,
      javax.ejb.RemoveException;

      public abstract javax.ejb.Handle getHandle()
      throws java.rmi.RemoteException;

      public abstract boolean isIdentical(javax.ejb.EJBObject)
      throws java.rmi.RemoteException;
      }
      以上是對于所有EJB對象必須擁有的方法,你不需實現(xiàn)這些方法,這些方法的實現(xiàn),生成EJB對象時由容器自動生成。

      客戶端代碼通過調(diào)用javax.ejb.EJBObject的方法來和bean協(xié)同工作。

      Java RMI和EJB對象
      你應(yīng)當(dāng)注意到:java.ejb.EJBObject繼承了Java.rmi.Remote。Java.rmi.Remote接口是java遠程方法調(diào)用(RMI)的一部分,任一個實現(xiàn)java.rmi.Remote的對象都是rmote對象,它可以被另外的java虛擬機所調(diào)用。

      被容器提供的EJB對象實現(xiàn)了遠程接口,同時也間接實現(xiàn)了java.rmi.Remote,這樣也就意味著你的EJB對象是完全符合網(wǎng)絡(luò)需要的,可以被網(wǎng)絡(luò)上的其他java虛擬機調(diào)用。當(dāng)然,EJB接口也必須遵守EJB規(guī)范。

      EJB遠程接口必須遵守java的RMI遠程接口規(guī)范。例如:錯誤處理,二者相同。
      遠程接口同樣也必須遵守java RMI參數(shù)傳遞規(guī)范。不是什么都可以通過VM方法調(diào)用來在網(wǎng)絡(luò)上傳遞,傳遞的參數(shù)必須符合RMI類型。
      EJB也繼承了RMI的優(yōu)點,對于RMI,你正在調(diào)用的遠程對象的物理地址是不可見的。這個特點同樣也適用于EJB。客戶代碼不必關(guān)心正使用的EJB對象是在鄰近的計算機上還是從internat傳遞來的。這樣,EJB對象可以和客戶端處在同一個java
      VM中。
      EJB保證了本地分布式組件的透明度。這種透明對于多層配置來說是非常必要的。客戶端代碼是非常容易移植的,不受限于特殊的多層配置。EJB容器可以以最佳化方式在本地執(zhí)行。


      Home對象
      我們看到,客戶端代碼處理EJB對象,而從不直接操作beans。那么,客戶端如何得到EJB對象的參考呢?
      客戶端不直接將EJB對象實例化。因為EJB對象可以存在于不同的機器中。同樣的,EJB使本地透明化,因此客戶端不知道它的確切所在。
      客戶端代碼通過EJB對象工廠得到EJB對象的參考。EJB規(guī)范里稱這種工廠為home對象。它主要起一下作用:
      建立EJB對象。
      找到已經(jīng)存在的EJB對象。
      刪除EJB對象。
      在一些細節(jié)方面,EJB對象工廠同EJB對象的特征相同。

      Home接口
      Home接口簡單的定義了建立、刪除和尋找EJB對象的方法。容器的home對象實現(xiàn)了home接口。
      通常,EJB定義了所有home接口必須支持的許多方法,這些必須的方法被定義在javax.ejb.EJBHome接口上,home接口必須繼承Java.ejb.EJBHome接口。

      public interface javax.ejb.EJBHome
      extends java.rmi.Remote
      {
      public abstract EJBMetaData getEJBMetaData()
      throws java.rmi.RemoteException;
      public abstract void remove(Handle handle)
      throws java.rmi.RemoteException
      javax.ejb.RemoveException;
      public abstract void remove(Object primaryKey)
      throws java.rmi.RemoteException,
      javax.ejb.RemoveException;
      }
      javax.ejb.EJBHome接口
      注意javax.ejb.EJBHome繼承了java.rmi.Remote,這意味著home接口同樣也支持RMI遠程對象,傳遞的參數(shù)和RMI也相同。

 

      Home對象

      所有home對象所需的方法
      配置描述符
      配置描述符允許EJB容器向企業(yè)級的bean組件提供隱含的中間件服務(wù)。隱含的中間件服務(wù)是bean可以獲得不必將任何中間件API解碼,可以自動獲得服務(wù)的一種服務(wù)。


      Bean的特殊屬性
      最后,你還需要有一個基于java的bean的屬性文件。Bean在運行時讀這些屬性,這些屬性在使用bean函數(shù)時會被用到。

      Ejb-jar文件
      一旦生成bean的類、home接口、遠程接口、配置描述符和bean的屬性,我們就可以把它們打包成一個實體。這個實體稱作Ejb-jar文件。這是個壓縮文件。


      建立Ejb-jar文件

      什么是會話bean
      一個Session Beans針對單一的客戶完成一次連接或會話,其生存直到客戶完成連接與會話,或系統(tǒng)意外中止。Session
      Beans必須實現(xiàn)接口javax.ejb.SessionBean。

      會話bean的生存期
      會話bean和實體bean的主要不同是它們的生存期的長短。會話bean的生存期短。與客戶的會話時間相當(dāng)。在與客戶連接端開時,EJB容器會破壞會話bean。

      相反,實體bean可以存活相當(dāng)長的時間,實體bean是永久存取的一部分,例如:數(shù)據(jù)庫。
      會話bean不能保存永久的存儲數(shù)據(jù),但是,它可以進行數(shù)據(jù)庫操作。
      所有的會話bean都需要管理callback方法,容器定時的調(diào)用它,用來對bean的重要事件發(fā)出警告。這個方法僅能被容器調(diào)用。

      Conversational versus Nonconversational Session Beans

      如何寫會話Bean
      寫會話bean的類,必須實現(xiàn)javax.ejb.SessionBean接口
      public interface javax.ejb.SessionBean
      extends javax.ejb.EnterpriseBean
      {
      public abstract void setSessionContext(SessionContext ctx)
      throws java.rmi.RemoteException;

      public abstract void ejbPassivate()
      throws java.rmi.RemoteException;

      public abstract void ejbActivate()
      throws java.rmi.RemoteException;

      public abstract void ejbRemove()
      throws java.rmi.RemoteException;
      }
      會話bean和實體bean都繼承了javax.ejb.EnterpriseBean接口
      讓我們詳細看看接口中的各種方法:
      setSessionContext(SessionContext ctx)
      容器調(diào)用這個方法來通過會話上下文與bean連接。Bean可以通過會話上下文向容器查詢當(dāng)前事物的狀態(tài)和當(dāng)前的安全狀態(tài)等。
      import javax.ejb.*;
      public class MyBean implements SessionBean {
      private SessionContext ctx;
      public void setSessionContext(SessionContext ctx) {
      this.ctx = ctx;
      }
      ...
      }

      ejbCreate(…)
      用來初始化你的會話bean,可以定義多個不同參數(shù)的ejbCreate()方法來用不同的方法初始化bean。
      import javax.ejb.*;
      public class MyBean implements SessionBean {
      private int memberVariable;
      public void ejbCreate(int initialValue) {
      this.memberVariable = initialValue;
      }
      ...
      }
      ejbCreate()方法是容器可以調(diào)用的callback方法,客戶端代碼不能調(diào)用它,因為客戶端不能直接處理beans??他們必須通過容器,但是客戶端必須采用某種方法向ejbCreate方法傳遞參數(shù),客戶端提供初始化參數(shù)。Home接口是客戶端用來初始化調(diào)用的接口工廠。你必須在home接口中復(fù)制每一個ejbCreate()方法,例如:如果在bean類中你有下面的ejbCreate方法

      public void ejbCreate(int i) throws ...
      那么你必須在你的home接口里有下面的create()方法
      public void create(int i) throws ...
      客戶端調(diào)用home接口中的create()方法,將參數(shù)傳遞給ejbCreate()。
      EjbPassivate()
      如果出現(xiàn)太多的實例bean,EJB容器可以將它們中的一些鈍化,將它們寫到臨時的存出空間例如數(shù)據(jù)庫或文件系統(tǒng)。容器釋放它們所申請的空間。
      import javax.ejb.*;
      public class MyBean implements SessionBean {
      public void ejbPassivate() {
      <close socket connections, etc...>
      }
      ...
      }
      ejbActivate()
      當(dāng)客戶需要使用被鈍化的bean時,容器將被鈍化的bean重新導(dǎo)入內(nèi)存,激活它們。
      Bean又被導(dǎo)致內(nèi)存,這時需要重新得到bean所需要的資源。
      import javax.ejb.*;
      public class MyBean implements SessionBean {
      public void ejbActivate() {
      <open socket connections, etc...>
      }
      ...
      }
      ejbRemove()
      當(dāng)容器將會話bean實例remove掉時,調(diào)用此方法。所有的bean都有這種方法,它沒有參數(shù),它將釋放所有以分配的資源。
      import javax.ejb.*;
      public class MyBean implements SessionBean {
      public void ejbRemove() {
      <prepare for destruction>
      }
      ...
      }
      容器可以在任何時候調(diào)用ejbRemove()方法,但如果遇到異常,則有可能禁止容器調(diào)用此方法。

      業(yè)務(wù)方法
      應(yīng)該定義一些解決業(yè)務(wù)問題的方法:例如:
      import javax.ejb.*;
      public class MyBean implements SessionBean {
      public int add(int i, int j) {
      return (i + j);
      }
      ...
      }
      因為客戶端要調(diào)用這些方法,因此,你必須在bean的遠程接口中列出這些方法。

      如何調(diào)用會話beans
      我們現(xiàn)在來看客戶方,客戶方要通過使用一個或多個bean解決一些現(xiàn)實的問題。
      有兩種不同的客戶方:
      Java RMI-based clients:這種客戶方通過使用JNDI定位對象,使用JTA控制事務(wù)。
      CORBA客戶方:客戶方也可以使用CORBA標準來寫。CORBA客戶方使用CORBA名字服務(wù)(COS
      Naming)在網(wǎng)絡(luò)上定位對象,使用CORBA的OTS控制事務(wù)。
      無論是用CORBA還是RMI,典型的客戶端代碼都必須實現(xiàn):
      1、 定位Home接口
      2、 使用Home接口建立EJB對象
      3、在EJB對象上調(diào)用業(yè)務(wù)方法
      4、清除EJB對象
      定位Home接口
      客戶端使用JNDL定位Home對象。
      J2EE中JNDL的作用
      J2EE的目標之一是使應(yīng)用程序?qū)崿F(xiàn)“write once,run
      anywhere”。任何的運行在企業(yè)級配置的java代碼在多層結(jié)構(gòu)中應(yīng)該是不受約束的。因此必須實現(xiàn)定位的透明化。
      J2EE通過使用JNDL來實現(xiàn)定位的透明化。已有目錄服務(wù)的產(chǎn)品如Netscape的Directory Server,微軟的Active
      Directory,IBM的Lotus Notes。
      通常我們使用目錄服務(wù)存儲用戶名、密碼、機器位置、打印機位置等等。J2EE擴展目錄服務(wù)存儲資源的本地信息,這些資源也可以是Home對象、企業(yè)級bean的環(huán)境屬性、數(shù)據(jù)庫驅(qū)動、信息服務(wù)驅(qū)動和其他資源。使用目錄服務(wù),在些應(yīng)用程序代碼時可以不必關(guān)心確切的機器名字和機器地址,這樣保證了代碼的可移植性。無論資源在何處,都不需要重新編譯代碼。

      JNDL通過為本地用戶、機器、網(wǎng)絡(luò)、對象和服務(wù)提供一個標準接口向企業(yè)級配置中增加值。
      為了在J2EE中定位資源,必須實現(xiàn)以下兩步:
      1、 用配置描述符中的“綽號”關(guān)聯(lián)資源,J2EE將向資源綁定綽號。
      2、 資源的客戶端使用JNDL中的綽號定位資源。

      怎樣使用JNDL定位Home對象
      客戶端不必關(guān)心Home對象在網(wǎng)絡(luò)的什么地方。JNDL為Home對象提供綽號來定位Home對象。通過綽號可以得到Home對象的參考。
      具體點,客戶端代碼必須執(zhí)行以下幾步來通過JNDL得到參考。
      1、 建立環(huán)境。必須配置將要使用目錄服務(wù),包括為驗證身份所需的用戶名、密碼。
      2、 建立初始的上下文。初始上下文是連接目錄結(jié)構(gòu)的本地出發(fā)點。通過初始上下文得到設(shè)定的環(huán)境屬性。
      3、 得到Home對象。執(zhí)行l(wèi)ookup()操作,返回一個RMI遠程對象。

      得到Home對象的參考
      /*
      * Get System properties for JNDI initialization
      */
      Properties props = System.getProperties();
      /*
      * Form an initial context
      */
      Context ctx = new InitialContext(props);
      /*
      * Get a reference to the home object - the
      * factory for EJB objects
      */
      MyHome home = (MyHome) ctx.lookup("MyHome");
      建立EJB對象
      得到Home對象以后,可以將Home對象作為建立EJB對象的工廠。調(diào)用create()方法建立EJB對象。
      MyRemoteInterface ejbObject = home.create();
      無參數(shù)是因為無狀態(tài)beans不需要初始參數(shù)。
      調(diào)用方法
      客戶端有了EJB對象以后就可以通過它來調(diào)用方法。當(dāng)客戶端在EJB對象上調(diào)用方法時,EJB對象必須選擇一個bean實例來應(yīng)答。EJB對象可以建立一個新的實例或是重用已經(jīng)存在的實例。

      ejbObject.add();
      破壞EJB對象
      調(diào)用EJB或Home對象上的remove()方法來破壞EJB對象。
      ejbObject.remove();
      無狀態(tài)會話bean基礎(chǔ)
      無狀態(tài)會話bean是可以模仿業(yè)務(wù)過程的組件,它可以在單獨的方法調(diào)用中被執(zhí)行。Stateless Session
      Bean不能夠維持一個調(diào)用客戶的狀態(tài),在一個方法調(diào)用中,Stateless Session Bean
      可以維持調(diào)用客戶的狀態(tài),當(dāng)方法執(zhí)行完,狀態(tài)不會被保持。在調(diào)用完成后,Stateless Session
      Bean被立即釋放到緩沖池中,所以Stateless Session Bean具有很好的伸縮性,可以支持大量用戶的調(diào)用。
      無狀態(tài)會話beans的特點
      沒有對話狀態(tài)
      無狀態(tài)會話bean可以擁有內(nèi)部狀態(tài),它們的狀態(tài)不能為特殊的客戶端定制。這意味著所有的無狀態(tài)bean對于客戶端是無差別的,客戶端也不能分離它們。客戶端必須將所有的必需的客戶端數(shù)據(jù)作為業(yè)務(wù)邏輯方法的參數(shù)傳給無狀態(tài)bean,無狀態(tài)bean可以從外部資源(例如數(shù)據(jù)庫)獲得所需的數(shù)據(jù)。

      初始化無狀態(tài)bean只有一種方法
      我們知道會話bean的初始化調(diào)用ejbCreate()方法,因為無狀態(tài)會話bean不能夠在方法調(diào)用之間保留狀態(tài),因此它也不能在客戶端給ejbCreate()調(diào)用傳遞數(shù)據(jù)以后保留狀態(tài)。調(diào)用不帶參數(shù)的ejbCreate()或create()。

      容器可以聚集和重用無狀態(tài)會話Bean
      構(gòu)建“Hello,World!”遠程接口
      package com.wiley.compBooks.roman.session.helloworld;
      import javax.ejb.*;
      import java.rmi.RemoteException;
      import java.rmi.Remote;
      /**
      * This is the HelloBean remote interface.
      *
      * This interface is what clients operate on when
      * they interact with EJB objects. The container
      * vendor will implement this interface; the
      * implemented object is the EJB object, which
      * delegates invocations to the actual bean.
      */
      public interface Hello extends EJBObject {
      /**
      * The one method - hello - returns a greeting to the client.
      */
      public String hello() throws java.rmi.RemoteException;
      }
      Source 4.1 Hello.java.
      Hello接口繼承了EJBObject接口,EJBObject繼承Remote接口,因此hello可以拋出rmi異常。
      下面建立bean,實現(xiàn)業(yè)務(wù)方法:hello()。
      他實現(xiàn)了javax.ejb.SessionBean接口
      package com.wiley.compBooks.roman.session.helloworld;
      import javax.ejb.*;
      /**
      * Demonstration stateless session bean.
      */
      public class HelloBean implements SessionBean {
      //
      // EJB-required methods
      //
      public void ejbCreate() {
      System.out.println("ejbCreate()");
      }
      public void ejbRemove() {
      System.out.println("ejbRemove()");
      }
      public void ejbActivate() {
      System.out.println("ejbActivate()");
      }
      public void ejbPassivate() {
      System.out.println("ejbPassivate()");
      }
      public void setSessionContext(SessionContext ctx) {
      System.out.println("setSessionContext()");
      }
      //
      // Business methods
      //
      public String hello() {
      System.out.println("hello()");
      return "Hello, World!";
      }
      }
      Source 4.2 HelloBean.java
      注意:不需要實現(xiàn)自己的遠程接口,初始化方法不帶參數(shù)。破壞bean時,使用比較簡單的ejbRemove()方法。ejbActivate()
      和ejbPassivate()方法不需應(yīng)用在無狀態(tài)會話bean,因此,這兩個方法為空。
      建立“Hello,World!”Home接口
      Home接口繼承了javax.ejb.EJBHome。Home接口為EJB對象擴展了一個不帶參數(shù)的方法??create()方法。
      package com.wiley.compBooks.roman.session.helloworld;
      import javax.ejb.*;
      import java.rmi.RemoteException;
      /**
      * This is the home interface for HelloBean. This interface
      * is implemented by the EJB Server´s glue-code tools - the
      * implemented object is called the Home Object and serves
      * as a factory for EJB Objects.
      *
      * One create() method is in this Home Interface, which
      * corresponds to the ejbCreate() method in HelloBean.
      */
      public interface HelloHome extends EJBHome {
      /*
      * This method creates the EJB Object.
      *
      * @return The newly created EJB Object.
      */
      Hello create() throws RemoteException, CreateException;
      }
      creat方法拋出了a java.rmi.RemoteException和aavax.ejb.CreateException.異常。
      寫配置描述符
      在EJB1.0中,配置描述符是作為文件存儲在磁盤上的java對象。在EJB1.1種,配置描述符是一個XML文檔。EJB容器或IDE環(huán)境應(yīng)該提供生成配置描述符的工具。

      配置描述符的設(shè)置
      bean home的名字
      企業(yè)級bean類名
      home接口類名
      遠程接口類名
      Re-entrant
      狀態(tài)或無狀態(tài)
      會話時間

 

      HelloBean的配置描述符
      環(huán)境屬性
      bean通過使用此信息來適應(yīng)不同的特殊環(huán)境。
      Ejb-jar文件
      我們需要將我們所需要的文件打包成Ejb-jar文件。
      企業(yè)級的bean
      遠程接口
      home接口
      配置描述符,包括屬性
      以上這些必須被包含進Ejb-jar文件。在EJB1.0中,jar文件理有一個文本文件的列表。它表示jar的詳細信息。它用來鑒別哪個企業(yè)bean在Ejb-jar文件。在EJB1.1中,XML文件包含了所有的必要信息。

      生成Ejb-jar文件
      jar cmf ..\manifest HelloWorld.jar *
      配置bean
      最后,我們還需要在Ejb容器中配置bean。常常執(zhí)行一下步驟:
      Ejb-jar文件的檢驗
      容器工具來產(chǎn)生EJB對象和home對象
      容器工具來生成RMI所需的stubs和skeletons
      寫無狀態(tài)bean的客戶代碼
      package com.wiley.compBooks.roman.session.helloworld;
      import javax.ejb.*;
      import javax.naming.*;
      import java.rmi.*;
      import java.util.Properties;
      /**
      * This class is an example of client code that invokes
      * methods on a simple stateless session bean.
      */
      public class HelloClient {
      public static void main(String[] args) {
      try {
      /*
      * Get System properties for JNDI initialization
      */
      Properties props = System.getProperties();
      /*
      * Form an initial context
      */
      Context ctx = new InitialContext(props);
      /*
      * Get a reference to the home object
      * (the factory for EJB objects)
      */
      HelloHome home = (HelloHome) ctx.lookup("HelloHome");
      /*
      * Use the factory to create the EJB Object
      */
      Hello hello = home.create();
      /*
      * Call the hello() method, and print it
      */
      System.out.println(hello.hello());
      /*
      * Done with EJB Object, so remove it
      */
      hello.remove();
      } catch (Exception e) {
      e.printStackTrace();
      }
      }
      }
      客戶端代碼執(zhí)行了一下任務(wù):
      定位home接口
      使用home接口建立EJB對象
      調(diào)用EJB對象上的hello()
      移走EJB對象
      運行
      首先運行應(yīng)用服務(wù)器。對于BEA的WebLogic,執(zhí)行
      t3server
      客戶端執(zhí)行:
      java -Djava.naming.factory.initial=
      weblogic.jndi.TengahInitialContextFactory
      -Djava.naming.provider.url=
      t3://localhost:7001
      com.wiley.compBooks.roman.session.helloworld.HelloClient
      服務(wù)端輸出:
      setSessionContext()
      ejbCreate()
      hello()
      ejbRemove()
      客戶端輸出:
      Hello, World!

 


      狀態(tài)會話Bean基礎(chǔ)

      Stateful Session Bean可以一對一的維持某個調(diào)用客戶的狀態(tài),并且在不同的方法調(diào)用中維持這個狀態(tài),
      由于對于每一個并發(fā)用戶,必須有一個對應(yīng)的Stateful Session Bean,為了提高系統(tǒng)的效率,Stateful Session
      Bean可以在一定的客戶空閑時間后被寫入二級存儲設(shè)備(如硬盤),在客戶發(fā)出新的調(diào)用請求后,再從二級存儲
      設(shè)備恢復(fù)到內(nèi)存中。但是在多用戶下,Stateless Session Bean運行效率高于Stateful Session Bean。
      javax.ejb.EnterpriseBean接口繼承了java.io.Serializable,用以實現(xiàn)寫入讀出操作。
      當(dāng)EJB容器調(diào)用ejbPassivate()方法鈍化了bean之后,就可以把它寫入二級存儲設(shè)備,然后容器調(diào)用ejbActivate()方法激活bean,把它從二級存儲設(shè)備中讀出。


      狀態(tài)bean的鈍化過程
      計數(shù)bean的遠程接口
      遠程接口定義了一個業(yè)務(wù)方法count(),它將在企業(yè)bean類中實現(xiàn)。


      激活狀態(tài)bean
      package com.wiley.compBooks.roman.session.count;
      import javax.ejb.*;
      import java.rmi.RemoteException;
      /**
      * These are CountBean´s business logic methods.
      *
      * This interface is what clients operate on when they
      * interact with EJB objects. The container vendor will
      implement this interface; the implemented object is
      * the EJB Object, which delegates invocations to the
      * actual bean.
      */
      public interface Count extends EJBObject {
      /**
      * Increments the int stored as conversational state
      */
      public int count() throws RemoteException;
      }
      Source Count.java

      package com.wiley.compBooks.roman.session.count;
      import javax.ejb.*;
      /**
      * Demonstration Stateful Session Bean. This bean is
      * initialized to some integer value and has a business
      * method that increments the value.
      *
      * This example shows the basics of how to write a stateful
      * session bean and how passivation/activation works.
      */
      public class CountBean implements SessionBean {
      private SessionContext ctx;
      // The current counter is our conversational state.
      public int val;
      //
      // Business methods
      //
      /**
      * Counts up
      */
      public int count() {
      System.out.println("count()");
      return ++val;
      }
      //
      // EJB-required methods
      //
      public void ejbCreate(int val) throws CreateException {
      this.val = val;
      System.out.println("ejbCreate()");
      }
      public void ejbRemove() {
      System.out.println("ejbRemove()");
      }
      public void ejbActivate() {
      System.out.println("ejbActivate()");
      }
      public void ejbPassivate() {
      System.out.println("ejbPassivate()");
      }
      public void setSessionContext(SessionContext ctx) {
      }
      }
      Source CountBean.java
      Bean實現(xiàn)了javax.ejb.SessionBean。所以,它必須定義所有SessionBean定義的方法。
      OjbCreate()初始化帶了val的參數(shù)。它將作為counter的初始狀態(tài)。在鈍化和激活bean的過程中,val變量被保護。

      計數(shù)bean的home接口
      package com.wiley.compBooks.roman.session.count;
      import javax.ejb.*;
      import java.rmi.RemoteException;
      /**
      * This is the home interface for CountBean. This interface
      * is implemented by the EJB Server´s glue-code tools - the
      * implemented object is called the Home Object and serves
      * as a factory for EJB Objects.
      *
      * One create() method is in this Home Interface, which
      * corresponds to the ejbCreate() method in the CountBean file.
      */
      public interface CountHome extends EJBHome {
      /*
      * This method creates the EJB Object.
      *
      * @param val Value to initialize counter to
      *
      * @return The newly created EJB Object.
      */
      Count create(int val) throws RemoteException, CreateException;
      }
      Source CountHome.java.
      計數(shù)bean的配置描述符

      計數(shù)bean的配置描述符
      計數(shù)bean的環(huán)境屬性
      生成計數(shù)bean的Ejb-jar文件
      計數(shù)bean的客戶端代碼
      package com.wiley.compBooks.roman.session.count;
      import javax.ejb.*;
      import javax.naming.*;
      import java.util.Properties;
      /**
      * This class is a simple example of client code that invokes
      * methods on a simple Stateless Enterprise Bean.
      *
      * We create 3 EJB Objects in this example, but we allow
      * the container to have only 2 in memory. This illustrates how
      * beans are passivated to storage.
      */
      public class CountClient {
      public static void main(String[] args) {
      try {
      /*
      * Get System properties for JNDI initialization
      */
      Properties props = System.getProperties();
      /*
      * Get a reference to the Home Object - the
      * factory for EJB Objects
      */
      Source CountClient.java
      1、需要JNDL初始化上下文
      2、使用JNDL定位home接口
      3、使用home對象建立3個不同的計數(shù)EJB對象,因此也就和三個不同的客戶端建立了會話
      4、配置描述符限制了同時只能有兩個bean工作,因此3個bean中一定有鈍化的。在調(diào)用ejbPassivate()時,打印一條信息。
      5、在每個EJB對象上調(diào)用count()方法,調(diào)用ejbActivate()方法激活bean,該方法打印一條信息。
      6、最后所有的EJB對象被刪除。
      package com.wiley.compBooks.roman.session.count;
      import javax.ejb.*;
      import javax.naming.*;
      import java.util.Properties;
      /**
      * This class is a simple example of client code that invokes
      * methods on a simple Stateless Enterprise Bean.
      *
      * We create 3 EJB Objects in this example, but we allow
      * the container to have only 2 in memory. This illustrates how
      * beans are passivated to storage.
      */
      public class CountClient {
      public static void main(String[] args) {
      try {
      /*
      * Get System properties for JNDI initialization
      */
      Properties props = System.getProperties();
      /*
      * Get a reference to the Home Object - the
      * factory for EJB Objects
      */
      Context ctx = new InitialContext(props);
      CountHome home = (CountHome) ctx.lookup("CountHome");
      /*
      * An array to hold 3 Count EJB Objects
      */
      Count count[] = new Count[3];
      int countVal = 0;
      /*
      * Create and count() on each member of array
      */
      System.out.println("Instantiating beans...");
      for (int i=0; i < 3; i++) {
      /*
      * Create an EJB Object and initialize
      * it to the current count value.
      */
      count[i] = home.create(countVal);
      /*
      * Add 1 and print
      */
      countVal = count[i].count();
      System.out.println(countVal);
      /*
      * Sleep for 1/2 second
      */
      Thread.sleep(500);
      }
      /*
      * Let´s call count() on each EJB Object to
      * make sure the beans were passivated and
      * activated properly.
      */
      System.out.println("Calling count() on beans...");
      for (int i=0; i < 3; i++) {
      /*
      * Add 1 and print
      */
      countVal = count[i].count();
      System.out.println(countVal);
      /*
      * Sleep for 1/2 second
      */
      Thread.sleep(500);
      }
      /*
      * Done with EJB Objects, so remove them
      */
      for (int i=0; i < 3; i++) {
      count[i].remove();
      }
      } catch (Exception e) {
      e.printStackTrace();
      }
      }
      }
      Source CountClient.java
      運行客戶端:
      對于BEA的WebLogic,執(zhí)行:
      java -Djava.naming.factory.initial=
      weblogic.jndi.TengahInitialContextFactory
      -Djava.naming.provider.url=
      t3://localhost:7001
      com.wiley.compBooks.roman.session.count.CountClient
      客戶端輸出:
      Instantiating beans...
      1
      2
      3
      Calling count() on beans...
      2
      3
      4
      服務(wù)端輸出:
      ejbCreate()
      count()
      ejbCreate()
      count()
      ejbCreate()
      ejbPassivate()
      count()
      ejbPassivate()
      ejbActivate()
      count()
      ejbPassivate()
      ejbActivate()
      count()
      ejbPassivate()
      ejbActivate()
      count()
      ejbPassivate()
      ejbActivate()
      ejbRemove()
      ejbActivate()
      ejbRemove()
      ejbRemove()

      為Beans增加功能

      EJB 上下文:通往容器的門戶
      存在如下信息:
      1、關(guān)于bean的home對象和EJB對象的信息
      2、bean的當(dāng)前事務(wù)信息。
      3、 對于客戶授權(quán)的安全信息。Bean可以通過查詢環(huán)境決定客戶執(zhí)行操作所需要的安全層次。
      4、 bean的環(huán)境屬性。
      容器將所有這些信息保存在一個稱為EJB context
      object的對象里。EJB上下文作為容器的物理部分,可以被bean訪問。這些訪問可以讓bean得到當(dāng)前的狀態(tài)和改變當(dāng)前的狀態(tài)。
      上下文可以在bean的生命期中被更改。
      EJB1.0 javax.ejb.EJBContext接口:
      public interface javax.ejb.EJBContext
      {
      public javax.ejb.EJBHome getEJBHome();
      public java.util.Properties getEnvironment();
      public java.security.Identity getCallerIdentity();
      public boolean isCallerInRole(java.security.Identity);
      public javax.jts.UserTransaction getUserTransaction();
      public void setRollbackOnly();
      public boolean getRollbackOnly();
      }
      會話bean的上下文
      上下文根據(jù)bean的不同分為:會話上下文和實體上下文。它們分別被用于會話bean和實體bean

      javax.ejb.EJBContext
      public interface javax.ejb.SessionContext
      extends javax.ejb.EJBContext
      {
      public javax.ejb.EJBObject getEJBObject();
      }
      注意:SessionContext接口繼承了EJBContext接口,在EJBContext中定義的方法提供了對會話bean的存取路徑。
      對于會話bean,調(diào)用setSessionContext,這個方法在javax.ejb.SessionBean接口中被定義。對于實體bean,調(diào)用setEntityContext。

      SessionContext.getEJBObject()
      在EJB中,beans可以作為其他bean的客戶端。如果一個bean需要調(diào)用另外的bean,getEJBObject()方法則是必需的。在java中,對象可以使用this關(guān)鍵字保存自身的參考。在EJB中,bean不能使用this關(guān)鍵字給其他bean傳遞對象,這是因為所有的客戶調(diào)用bean上的方法都是間接調(diào)用bean的EJB對象。Bean可以使用this關(guān)鍵字將自己傳給EJB對象。

      了解EJB的安全性
      首先,客戶端必須是可被鑒別的。
      其次,客戶端必須是已經(jīng)授權(quán)的。
      第一步:鑒別
      不同的EJB容器擁有不同的鑒別客戶端的方法。例如:BEA的WebLogic中,當(dāng)不同客戶端代碼使用JNDL定位Home對象時,提供不同的用戶名和密碼。

      Properties props = System.getProperties();
      props.put(Context.SECURITY_PRINCIPAL, "EmployeeA");
      props.put(Context.SECURITY_CREDENTIALS, "myPassword1");
      Context ctx = new InitialContext(props);
      // Use the initial context to lookup home objects...
      EJB沒有制定如何鑒別的規(guī)范,因此這樣就影響了可移植性。要了解這方面,查看各類容器的文檔。
      當(dāng)運行這段代碼時,應(yīng)用服務(wù)器將驗證你的用戶名和密碼,這是應(yīng)用服務(wù)器規(guī)范。許多應(yīng)用服務(wù)器允許在屬性文件里設(shè)置用戶名和密碼。這個文件將在運行時由應(yīng)用服務(wù)器讀。

      高級點的服務(wù)器支持已經(jīng)存在的驗證系統(tǒng)的整合。例如將用戶名和密碼列表存儲在LDAP服務(wù)器中。
      第二步:授權(quán)
      只有經(jīng)過授權(quán)的客戶端才可以調(diào)用bean中的方法。EJB中有兩種驗證授權(quán)的方法:declaratively和programmatically。即:由容器執(zhí)行所有的授權(quán)檢驗、在程序中進行授權(quán)檢查。

      Declarative授權(quán)檢查時,要在配置描述符中聲明bean的授權(quán)需要。例如使用BEA的WebLogic服務(wù)器的配置描述符的例子:
      (accessControlEntries
      submitPurchaseOrder [employees]
      approvePurchaseOrder [managers]
      DEFAULT [administrators]
      ); end accessControlEntries
      容器將在運行時自動的執(zhí)行安全檢查。拋會出java.lang.SecurityException異常。
      Programmatic授權(quán)檢查,必須查詢EJB上下文得到當(dāng)前客戶端的授權(quán)信息。由兩種方法調(diào)用CallerInRole(Identity
      role)和getCallerIdentity()。
      isCallerInRole()
      import java.security.Identity;
      ...
      public class MyBean implements SessionBean {
      private SessionContext ctx;
      ...
      public void foo() {
      Identity id = new MyIdentity("administrators");
      if (ctx.isCallerInRole(id)) {
      System.out.println("An admin called me");
      return;
      }
      System.out.println("A non-admin called me");
      }
      }

      import java.security.Identity;
      public class MyIdentity extends Identity {
      public MyIdentity(String id) {
      super(id);
      }
      }
      getCallerIdentity()
      import java.security.Identity;
      ...
      public class MyBean implements SessionBean {
      private SessionContext ctx;
      ...
      public void bar() {
      Identity id = ctx.getCallerIdentity();
      String name = id.getName();
      System.out.println("The caller´s name is " + name);
      }
      }
      了解EJB對象的操作
      許多EJB應(yīng)用程序需要客戶端有與bean斷開的能力,還要有與bean重建連接的能力。EJB提供了EJB object
      handles。EJB對象操作對于EJB對象是一個長生命期的代理。可以用它來重建與EJB對象的連接,并保證會話狀態(tài)不被丟失。下面是EJB對象操作的代碼

      // First, get the EJB object handle from the EJB object.
      javax.ejb.Handle myHandle = myEJBObject.getHandle();
      // Next, serialize myHandle, and then save it in
      // permanent storage.
      ObjectOutputStream stream = ...;
      stream.writeObject(myHandle);
      // time passes...
      // When we want to use the EJB object again,
      // deserialize the EJB object handle
      ObjectInputStream stream = ...;
      Handle myHandle = (Handle) stream.readObject();
      // Convert the EJB object handle back into an EJB object
      MyRemoteInterface myEJBObject =
      (MyRemoteInterface) myHandle.getEJBObject();
      // Resume calling methods again
      myEJBObject.callMethod();
      例子:The Puzzle Game “Fazuul”(參考原文)