1. 程式人生 > >編寫Enterprise bean的客戶端

編寫Enterprise bean的客戶端

下面從五個方面來介紹如何編寫EntERPrise bean的客戶端:

一、Enterprise JavaBean的客戶檢視:

  Enterprise JavaBean客戶是一個獨立的應用程式,或者是一個Servlet,或者一個Applet,還或者另一個Enterprise JavaBean。無論如何,客戶要使用一個Enterprise JavaBean必須要做如下事情:

首先要JavaBean的home介面:EJB 規範裡面要求客戶應該使用Java的名字和目錄介面JNDI(Java Naming and Direcotry Interface)API來定位Bean的home介面。 然後獲得Enterprise JavaBean的remote介面的引用:可以使用Enterprise bean的home介面中定義的方法。可以產生一個會話bean,也可以產生或尋找(find)一個實體bean。 呼叫Enterprise JavaBean中定義的方法:客戶並不直接呼叫Enterprise JavaBean中定義的方法。而是通過呼叫在Enterprise JavaBean的remote介面中暴露給使用者的方法來使用JavaBean類中的方法。在remote介面中定義的方法就是在JavaBean中希望暴露給使用者,讓使用者使用的方法。

初始化客戶:

  在IAS所帶的例子裡面,作為客戶的SortClient應用程式引入了必要的JNDI類、還引入了SortBean的home介面和remote介面。客戶使用JNDI的API來定位Enterprise JavaBean的Home介面。

定位Home介面:

  客戶用JNDI定位home介面。首先需要獲得一個JNDI的初始化的上下文context。下面的程式碼例項化了一個新的javax.naming.Context類。在我們這個例子中叫做initialContext。然後,客戶就可以使用上下文的lookup()方法從一個名字對應到home介面。注意,初始的命名上下文工廠的初始化是由EJB容器/服務指定的。

  上下文的lookup()方法返回了一個java.lang.Object的物件。程式碼必須把返回的物件轉成所希望的型別。下面的程式碼是SortClient例子中的一段程式碼。Main()從使用JNDI服務開始,並用上下文的lookup()方法來定位home介面。將remote介面的名字(在這個例子中是sort)傳遞給context.loopup()方法。注意,程式最後將context.lookup()方法的結果傳遞給了SortHome,home介面的型別。

用JNDI定位home介面:

// SortClient java

import javax.naming.InitialContext;

import SortHome; // 引入bean的home介面

import Sor // 引入bean的remote介面

public class SortClient {

……

public static void main(String[] args) throws Exception

javax.naming.Context context;

{ // 用命名服務獲得JNDI上下文

context = new.javax.naming.InitialContext();

}

Object objref = context.lookup(sort);

SortHome home = (SortHome)javax.rmi.PortableRemoteObject.narrow(objref,

SortHome class);

Sort sort = home.create();

//做排序工作

sort remove();

}

maint()首先丟擲一個普通的異常Excepion。當代碼執行到這兒的時候,就算可能會導致程式的終止,客戶也需要捕獲任何發生的異常。

獲得remote介面:

  現在我們獲得了Enterprise JavaBean的home介面,然後,使用home介面的create()方法或者finder()方法就可以獲得其remote介面了。實際使用哪個方法取決於Enterprise JavaBean的型別和Enterprise JavaBean的提供者在home介面定義的方法。

  例如:上面的程式碼顯示了客戶SortClient如何獲得Sort的remote介面的引用。一旦SortClient獲得了home介面的引用並將之轉化為適當的型別(這裡是SortHome),客戶就可以產生bean的例項,並呼叫它的方法。本例子中呼叫了home介面的create()方法,方法返回了remote介面的引用。(因為這個例子裡面SortBean是一個無狀態的會話bean,其home介面只有一個create()方法,這個方法沒有任何引數)。然後,SortClient就能夠呼叫定義在remote介面中的方法:sort()和merge()來進行排序工作。當排序工作完成的時候,客戶呼叫remote介面的remove方法來刪除Enterprise bean的例項。

會話Bean:

  客戶通過呼叫home介面中的Create()方法獲得Enterprise JavaBean的remote介面的引用。

  每一個會話Bean都必須至少有一個Create()方法。無狀態的會話bean只能有一個Create()方法,並且這個Create()方法不能有引數。有狀態的Session bean可以有一個不帶引數的Create()方法,也可以有另外的帶有不同引數的create()方法。Create()方法中的引數是用來初始化的會話bean的。

  預設的create()方法沒有引數。例如,Sort例子使用了一個無狀態的會話bean。根據定義,它有一個沒有引數的create()方法。

   Sort sort=home.create();

  我們比較一下另一個例子:Cart例子,使用的是有狀態的會話bean。在其home介面中實現了多個的create()方法。其中一個create()方法帶了三個引數,三個引數用來標識cart的購買者,返回了Cart的home介面的引用。Cart客戶先將三個引數的值給cartHoldName,creditCartNumber和expirationDate這三個變數,然後呼叫create()方法。其程式碼如下:

如何呼叫create()方法的程式碼:

Cart cart;

{

String cartHolderName=”Jack B.Quick”

String creditCartNumber = “345-346-32525252”

Date expirationDate = new GregorianCalendar(2001, Calendar JULY, 1) getTime();

Cart = home.create(cartHolderName,creditCartNumber,expirationDate);

}

另外,要注意的是,會話Bean沒有finder方法。

實體 bean:

  通過finder方法,或者通過create方法,客戶得到實體物件的引用。我們在前面提到,實體物件代表了儲存在資料庫中的某些資料。既然Entity bean代表了一些持久的資料,那麼通常實體bean存在相對長的一段時間,肯定比客戶呼叫它的時間要長,這樣,客戶通常需要尋找一個代表了資料庫中它感興趣的那一段資料的實體bean,而不是建立(create)一個實體bean。Create操作將會產生一個新的資料,並將新的資料存在底層的資料庫中。

  客戶通過find操作來定位一個已經存在的實體bean。可以是相關資料庫裡面指定的行。也就是說,finder操作定位一個原來已經插入到資料庫中的資料實體。這個資料也許已經被實體bean加入到資料庫中,也許已經被加入到EJB上下文的外部(例如是資料庫管理系統DBMS)。或者是在原有系統中,這資料的在安裝EJB之前就已經存在。

  客戶使用create方法產生一個新的資料實體,並將資料實體存放在底層的資料庫中。實體bean的create方法將實體資料插入到資料庫中,用create方法的引數初始化實體的變數。實體bean的create方法總是返回一個remote介面。但是,對應的ejbcreate()方法是返回實體Bean例項的主鍵。

  每一個實體bean都必須有一個主鍵值來唯一標識它。一個實體bean也可以有次鍵值,用來定位特定的實體物件。

Find方法和主鍵類:

  一個實體bean的預設的find方法是findByPrimaryKey()方法。它通過主鍵來定位實體bean。其語法如下:

$#@60;remote interface$#@62; findByPrimaryKey($#@60;key type$#@62; primary key)

  每一個實體bean必須實現findByPrimaryKey方法。PrimaryKey引數是一個獨立的定義在配置描述器中的主鍵類。鍵的型別是主鍵,而且必須是RMI-IIOP合法的值。鍵類可以是任何Java類或者你自己寫的類。

  例如,你可能有一個名字為Acount的實體bean。已經將它的主鍵類定義為AccountPK。AccountPK是一個字串型別。它擁有Account bean的標識。我們將account bean的標識設為AccountPK,然後呼叫findByPrimaryKey方法,就可以得到account bean例項的引用。如下程式碼所示:

Finding a Entity bean instance using the primary key

AccountPK accountPK =new AccountPK(“1234-56-789”);

Account source = accountHome.findByPrimaryKey(accountPK);

Bean的提供者可以定義另外的finder方法給客戶使用。

Create()方法和remove()方法:

  客戶也能夠用home介面中定義的create()方法來建立一個實體bean。當客戶呼叫實體bean的create()方法的時候,一個新的實體bean的例項就存到資料倉庫中。新例項總是有一個標識它的主鍵值。它的狀態可以用create方法傳遞的引數值來初始化。

  注意:實體bean存在的時間與它在資料庫中代表的資料是一樣的。其生命並不由與客戶的會話來決定。通過呼叫實體bean的remove方法,可以將實體bean刪除。Remove方法不僅將實體bean刪除,也將它所代表的資料從資料庫中刪除。另外也可以直接刪除實體bean。例如,可以通過用原來的應用程式或用DBMS刪除資料庫中的一條記錄的方法來刪除。

呼叫方法:

  一旦客戶獲得了bean的remote介面的引用。就可以呼叫在remote介面中為enterprise bean定義的方法。屬於此bean的應用邏輯的方法才是客戶感興趣的。也有一些方法是用來獲得enterprise bean的資訊或者介面資訊的。例如:獲得bean物件的handle,檢查一個bean是否與另一個bean相同,刪除bean的方法。

  下面的程式碼解釋了客戶如何呼叫enterprise bean中的方法。這個例子呼叫的是Cart 這個會話bean。程式碼從產生一個新的Cart bean例項並重新找到一個cart bean例項的remote介面的引用開始。從這裡開始,客戶就準備好可以呼叫bean的方法。

  首先,客戶建立了一個新的book物件,設定了其title 和price引數。然後,呼叫了enterprise bean的應用方法addItem()來將一個book物件加到shopping cart中去。AddItem()方法是在CartBean 這個會話bean中定義的,通過Cart的remote介面把它變成公共的方法。客戶增加了一項其它的東西,然後用summairze()方法列出shopping cart裡面的所有的Items。最後,就呼叫remove方法來刪除bean的例項。注意,客戶用呼叫其他的方法也是通過同樣的途徑,即使是自己定義的方法summarize()。

呼叫Bean的方法

………

Cart cart;

{

//獲得bean的remote介面的例項。

Cart = home.create(cartHodlerName,creditCardNumber,expirationDate);

}

//建立一個新的book物件。

Book knuthBook = new Book(“The Art of Computer Programming”,49.95f);

//將新書加到Cart中。

Cart.addItem(knutBook);

………

//列舉當前Cart中的書

summarize(cart);

cart.removeItem(knutBook);

……

刪除bean的例項:

  remove方法對會話bean的操作和對實體bean的操作是不同的。因為對於客戶來說,會話物件的存在是不持久的。會話物件的客戶在完成了會話之後需要呼叫remove方法。對客戶來說,有兩個remove方法可以呼叫:可以用javax.ejb.EJBObject.remove()方法來刪除會話bean;也可以用javax.ejb.EJBHome.remove(Handle handle)方法來刪除會話bean的handle。

  一個好的程式設計風格是不需要由客戶刪除一個會話bean物件。如果客戶沒有刪除一個有狀態的會話bean,容器在過了一段時間(指定的超時值timeout value)之後會自動刪除這個物件。Timeout value是一個配置屬性。但是,客戶能夠為以後的引用保持這個物件的handle。

  Entity bean的客戶不需要考慮這個問題,因為實體bean只是在客戶的事務期間。由容器負責它的生命週期,包括啟用和鈍化。只是當客戶需要將實體物件從底層資料庫中刪除的時候,才呼叫remove方法。

使用bean的控制代碼:

  控制代碼是用來引用entERPrise bean的另一種方法。控制代碼相當於bean的一個較長的指標。你可以從remote介面獲得控制代碼。一旦你擁有了控制代碼,就可以將它寫入到檔案或其它的持久存貯器裡面。便於以後可以重新得到控制代碼,用它來重新建立enterprise bean的引用。

  但是,你只能用remote介面的控制代碼來重新建立bean的引用。你不能用控制代碼來建立bean本身。如果另一個程序刪除了bean,或者系統崩潰或關閉,刪除了bean的例項,則當應用程式試圖用控制代碼重新建立對bean的引用時會丟擲異常。

  當你不確定bean的例項是否依然存在的時候,你可以不用remote介面的控制代碼。而是儲存bean的home介面的控制代碼,在以後要用的時候再通過呼叫create方法或finder方法重新建立bean物件。

  在客戶建立了bean的例項以後,就能夠用getHandle()方法來獲得例項的控制代碼。一旦擁有的控制代碼,就能夠將它寫到檔案裡面去。在以後的時間,客戶可以讀這個檔案,將讀出來的物件轉化為控制代碼型別。然後,就可以在控制代碼上呼叫的getEJBObject方法來獲得bean的引用。最後再將getEJBObject方法返回的值轉化為合適的型別。

使用控制代碼來引用一個Bean

Import Java.io;

Import javax.ejb.Handle;

……

Cart cart;

……

//在cart物件上呼叫getHander方法來獲得它的控制代碼。

CartHander=cart.getHandler();

//將hander寫到檔案中去。

FileOutputStream f = new FileOutputStream(“carthandle.ser”);

ObjectOutputStream o = new ObjectOutputStream(f);

o.writeObject(myHandle);

o.flush();

o.close();

……

//在以後的某個時間,可以從檔案中讀出handle

FileInputStream fi = new FileInputStream(“carthandle.ser”);

ObjectInputSteam oi = new ObjectInputStream(fi);

//從檔案中讀出物件並將它轉化為Hander型別。

CartHanle = (Handle)oi.readObject();

Oi.close;

……

//使用handle來引用bean的例項

try{

Object ref = context.lookup(“cart”);

Cart cart1 = (Cart)javax.rmi.PortableRemoteObject.narrow(ref,Cart.class);

……

}catch(RemoteException e){

……

}

……

  當用完會話bean的控制代碼以後,客戶將使用javax.ejb.EJBHome.remove(Handle handle)方法來刪除控制代碼。

二、管理事務:

  客戶程式能夠管理它自己的事務,而不是讓enterprise bean或者是容器來管理。客戶管理自己事務的時,就好象會話bean管理它自己的事務一樣。

  當客戶管理自己的事務的時候,需要自己來描述事務的分界線。這也就是說,它必須明確的開始一個事務和終止(提交或回滾)一個事務。

  客戶使用javax.transaction.UserTransaction介面來管理它自己的事務。它必須首先用JNDI來獲得UserTransaction介面的引用。一旦有了UserTranscation的上下文,就可以用UserTransaction.begin()方法來開始一個事務(後面用UserTranscation.commit()方法提交或Usertransction.rollback()方法回滾這個事務)。這之間客戶做有關的查詢和更新操作。

  如下程式碼演示了客戶如何實現管理它自己的事務。客戶管理的屬於協議的那部分用粗體顯示:

客戶管理事務:

import javax.naming.initialContext;

import javax.transcation.UserTransaction;

……

public class clientTransaction{

public static void main(String[] argv){

UserTranscation ut=null;

InitialContext initContext = new InitialContext();

……

ut = (UserTransaction)initContext.lookup(“java:comp/UserTranscation”);

//開始一個事務

ut.begin();

//做事務工作。

……

ut.commit(); //or ut.rollback();

}

}

三、獲得enterprise bean的資訊:

  enterprise bean的資訊是一個指向metadata引用。客戶使用enterprise bean的home介面的getMetaData()方法能夠獲得bean的metadata資訊。

  GetMetaData()方法經常被開發環境和build工具所使用,因為它們需要知道enterprise bean的相關資訊,例如哪些連結到一起的bean已經被安裝。指令碼客戶也需要獲得bean的metadata資訊。

  一旦客戶重新得到home介面的引用。就能夠在home介面上呼叫getEJBMetaData()方法。然後,客戶就可以通過呼叫EJBMetaData介面上的方法來取得如下資訊。

  用EJBMetaData.getEJBHome()方法獲得bean的EJBHome介面。

  用EJBMetaData.getHomeInterfaecClass()方法來獲得home介面類的物件。包括其介面,類,域和方法。

  用EJBMetaData.getRemoteInterfaceClass()方法來獲得remote介面類的物件。包括所有的類資訊。

  用EJBMetaData.getPrimaryKeyClass()方法來獲得bean的主鍵類物件。

  不管是會話bean還是實體bean。可用EJBMetaData.isSession()方法來判斷。

  用EJBMetaData.isStatelessSession()來判斷會話bean是否有狀態還是無狀態的。