當撰寫用戶端來存取 bean 的時候, 這個客戶可能是在本地(Local), 或是遠端(Remote). 一個本地的客戶表示它和這個 bean 執行在相同的 heap. 換句話說, 這個 bean 跟用戶端都存在相同的 heap 裡. 不過這是對entity bean 或是在某些非常特殊的情況下才會用 local 用戶端.
 
Java 參照(reference)所保有的位元值, 一但離開了正在運行的 JVM 後就不具有任何意義了. 換句話說, 如果你是一個物件並且手上握有另一個物件的 reference, 那麼這個物件必須跟你坐落在相同的 heap.
 
Java RMI (Remote Method Invocation) 藉由給予用戶端一個 proxy 物件: stub, 來扮演客戶與遠端物件之媒介代理, 來解決上述的問題. stub 處理了所有跟遠端物件間地低階網路通訊細節, 包括 sockets 和 streams.
 
藉由 RMI, 用戶端物件就好像是執行一個遠端的方法呼叫一樣, 但是實際上發生的事情是在呼叫跟用戶端運行在相同 heap 裡的 stub 的 method.
RMI 的目標是要促使網路的透通性(transparency), 基於這個目標, 在 server 端接收 socket 連線的腳色就是 skeleton, 它跟用戶端的 stub 是互相配對的(早期是, 但是現再不一定).
 

# 透過 remote object 的 method 傳遞一個 local 的 object:
 
所有的遠端方法都必須宣告 java.rmi.RemoteException, 所以除了 RemoteException 以外, remote method 的呼叫跟 local method 的呼叫其實沒什麼兩樣.
 
1. 用戶端只是呼叫 local 的stub(坐落在相同的heap), 然後 stub 必須打包所有的引數(透過一種 marshalling 的程序), 同時經由 socket 連線到 server, 再將它們送到 output stream.
 
2. 而 skeleton 得處理 stub 傳來的 streams, 卸包參數, 並決定該做的事情, 接著呼叫 remote object(現在變成 local object)的 method.
 
3. skeleton 打包回傳值並將之傳給stub, 並將回傳值以正常, 普通的格式交付給 client.
 
為了要在網路上可以傳遞參數與回傳值, 它們必須是 Shippable, 而 shippable 必須是:
* primitive type.
* Serializable object.
* primitive type 或 Serializable object 的 Array 或是 collection.
* remote object.
 
Q: Java 傳遞物件的方式是, 傳遞一份 object reference 的 copy, 而不是 object 的 copy. 所以在 remote object 的 method 的呼叫上, 這不是不具意義的嗎? 因為 reference 的是另一個 heap 上的東西.
 
A: 是沒錯, 正常的來說, Java 的 argument 是以 by value 的方式而非 by reference 的方式傳遞. 也就是傳遞 object reference 的 copy, 也就是傳遞 object reference 的 copy 的 value. 但是, 如果遠端方法必須有一個 object type 的 argument, 則該 argument 是以這個 object 本身的完整副本被傳遞出去. 所以對於遠端的呼叫, Java 是以 object 的 copy 傳遞物件, 而非 obejct reference 的 copy. 但是前提是這個 object 是一個可以續列化的物件. 所以它必須 implements java.io.Serializable 這個 interface.
 
 
/********************************************
  * Client 的 code
  *******************************************/
 try
 {
  Dog fido = new Dog();
  //這時傳遞的是真正 Dog object 的 copy
  remoteStub.trainPet(fido);
 }
 catch (RemoteException rex)
 {
  rex.printStackTrace();
 }

 /********************************************
  * Server 的 code
  *******************************************/
 void trainPet(Dog arg){}
 
 
1. Client 對  stub 呼叫 trainPet(fido), 傳遞 Dog object reference 的 copy. //實際上只有一個 Dog object.
 
2. stub 製造一個 Serializable object copy, 並透過網路將這個 copy 給 server 的 skeleton.
 
3. skeleton deserialize 傳來的 argument, 在 remote 的 heap 裡製造一個新的 Dog object. //這時的 Dog 跟在 client 的 Dog object 是一樣的.
 
4. skeleton 呼叫 remote object 的 method, 傳遞一個新的 Dog object reference 的 copy. 
 
 

# 透過 remote object method 傳遞一個 remote object:
 
Java 實際傳送的是 remote object 的 stub, 換句話說, 在 runtime 時, remote object 還是待在原地, 只是透過網路傳送它的 stub. 一個 remote object reference 指的是 remote reference 的 stub. 如果 client 擁有一個 remote object reference, 它便擁有該 stub 的 local reference, 同時, 該 stub 可以跟 remote  object 溝通.
 
/********************************************
  * Client 的 code
  *******************************************/
 try
 {
  //我不確定是不是用 remoteStub.getCustomer() 這樣寫, 只是將感覺寫出來, 以後要 check
  remoteStub.getCustomer();
 }
 catch (RemoteException rex)
 {
  rex.printStackTrace();
 }

 /********************************************
  * Server 的 code
  *******************************************/
 public class Customer
 {
 };
 public class A
 {
  Customer c = new Customer();
  public getCustomer()
  {
   return Customer();
  }
 };
 
1. client 透過 stub A invoke remote object A 的 getCustomer().
 
2. remote object A 回傳一個 Customer object reference 的 copy, 接著 skeleton 將它置換成(同時 serialize) remote object Customer 的 stub, 然後將 Customer  stub 回傳給 client.
 
3. Customer stub 在 client 被 deserialize, 同時 client 得到指向這個新的 Customer stub 的 local reference copy.
 
 
 
Q: client 怎麼知道有哪些方法可以呼叫?? stub 怎麼知道 remote object 提供哪些方法?
 
Before Answer: 如果 stub 要假裝是 remote object, 它就必須提供跟 remote object 相同的方法.
 
A: 一個 interface. 這是所有分散環境下, 所有方法必須讓 client 看的見的管道. 這個 interface 稱為商業邏輯介面, 因為它有 client 想要呼叫的商業邏輯方法. 在技術上一個 remote object 的商業邏輯界面必須是一個遠端介面. so.. 它必須遵循:
1. 繼承java.rmi.Remote
2. 每個 method 必須宣告 java.rmi.RemoteException
3. 引數和回傳值必須是可以傳遞的.
 
import java.rmi.*;
public interface DiceRoller extends Remote
{
 public int rollDice() throws RemoteException;
}
 

 
# 在 EJB, remote object 是 EJBObject 而非 bean!
EJBObject 永遠是 bean 的 guard. 所以 EJBObject 實作 remote business logic interface, 並接收 remote invocation. 一旦呼叫達到 EJBObject, server 便介入其中提供所有的服務, such as 安全控管(client 有被授權可以呼叫這個方法嗎?), 交易(這個呼叫是現在交易的一部份, 還是要啟動一個另一個交易?), 存續(在執行這個方法前, 這個 bean 還需要從 db 載入任何資訊嗎?)
 
所以說, stub 跟 EJBObject 皆 implements remote business logic object, 但是裡面卻沒內容. 而 bean 雖然沒有去 implements remote business logic object, 但是它卻有真正 business logic 的功能.
 

 
# 在 EJB, 這個 remote business logic interface 稱為  component interface. 是用來公開商業邏輯方法給用戶端的地方.
 
跟一般 remote interface 不同之處, 就是 component interface extends EJBObject, 不是直接 extends Remote.
 
1. interface java.rmi.Remote 是一個沒有定義方法的 interface.
2. interface EJBObject extends Remote. 所以 EJBObject 是一個 remote interface, 並存有一些 method.
3. component interface extends EJBObject, 所以它除了也是一個 remote interface 以外, 也有了 EJBObject 的 methods, 更重要的是! 它本身定義了 business logic methods 可供 client 使用.
 
1. javax.ejb.EnterpriseBean 是一個沒有定義方法的 interface
2. interface SessionBean extends EnterpriseBean, 併存在若干方法.
3. bean extends SessionBean, 除了接收了 SessionBean 的方法外, 並定義了與實作了 business logic methods.
 
component interface 與 bean 有相同的 method, 但 bean 並沒有去 implements component interface. 因為 bean 從不會被期望它是一個 remote object, 也不該是.
 
server 會幫我們知道 bean, component interface 跟 EJBObject 的關連, 並確保 component interface 與 bean class 有相吻合的 methods.
 
Q: 那到底是誰去 implements component interface 呢?
A: 是 container! container 除了會 implements component interface 外, 還會 implements 產生一個 EJBObject class, 並提供一個與 EJBObject 配合的 stub class. 但是真正的 business logic methods 是落在 bean 裡面的, 而 EJBObject class 只是一個 remote object, 一個 guard. 它唯一的工作回應 client 的 invocation(透過 stub), 攔截 client 對 bean 的呼叫, 然後交由 container 或 server 來處理.
 

 
# Home: 每一個 session bean 跟 entity bean 都有搭配一個 Home.
 
Message-driven bean 不需要 Home, 因為 message-driven bean 不提供用戶端視野, 也就是說, client 無法得到 Message-driven bean 的 reference.
 
Home 的主要任務: 交出一個 component interface 的 reference. 每個 bean 都有自己的 Home, 同時 Home 負責所有該 bean 的 instance. 例如: 如果 deploy 一個 ShoppingCart 的 Session bean, container 將會產生一個 ShoppingCart bean 的 Home class. 這個 Home class 將會處理所有 ShoppingCart bean 的 instance, 換言之, 如果有兩千個 client 都要有自己的 ShoppingCart bean 的 reference(這代表一個指向 ShoppingCart bean 的 component interface), 而唯一一個 ShoppingCart 的 Home 將會提供所有的 reference. 所以不管 Home class 交出了多少的 EJBObject 和 stub, Home 都只有一個(一個 bean, 一個 Home).
 
ps: 但 client 從未得到 bean 的 reference, client 得到的是 EJBObject 的 reference.
 
# 取得和使用 AdviceBean 的 Home
1. AdviceBean 已經 deploy, 同時 server 產生了 AdviceBean 的 Home class, 並將它註冊到 JNDI.
2. 用戶端使用註冊的名稱"Advisor"到 JNDI 查詢 Home. =>lookup("Advisor");
3. JNDI 回傳 remote Home stub.
4. client 藉由 create() 來跟 Home 要求一個 component interface 的 reference. 也就是 client 想要建立一個 bean, 同時取回 bean 的 EJBObject 的 stub.
5. 這時透過了server 的 service, bean 被建立了.
6. EJBObject 也被建立出來, 同時它的 stub 被送回 client.
7. client 透過 EJBObject 的 stub(一個元件介面)呼叫 bean 的商業邏輯方法.
8. client 可以丟掉它的 Home stub, 如果它不想再存取更多的 AdviceBean, 但它仍可以繼續呼叫 component interface 的 method.
 
 
# 誰負責做什麼?
 
Bean Provider:
1. component interface (extends javax.ejb.EJBObject)
2. bean class (implement javax.ejb.SessionBean 或 javax.ejb.EntityBean)
3. Home interface (extends javax.ejb.EJBHome)
 
Container:
1. EJBObject class (implements component interface)
2. EJBObject stub class (implements component interface, 並知道如何跟 EJBObject class 對話)
3. Home class (implements Home interface)
4. Home stub class (implements Home interface, 並知道如何跟 Home class 對話)
 

# client 分享 Home, 也分享 Entity Bean.
 
對於這個 bean(例如 CustomerBean), 每一個 client 都它自己的且唯一的 Home reference.
 
但是如果兩個 client "同時"都試圖存取相同的 Customer(F. Smith #420), 這麼一來:
1. 除了兩個 client 都會有相同的 Home reference, 分享了 Home, 在 client 端也有自己的 Home stub(目前有兩個 Home stub).
2. 相同的, 兩個 client 也都會有相同的 EJBObject 的參照, 也就是 F. Smith #420 的 EJBObject, 因此分享了 EJBObject, 而一個 EJBObject 對應了 F. Smith #420 這個 bean, 所以也分享了 bean. 但是跟 Home stub 一樣, 在兩個 client 裡, 都分別有自己的 EJBObject stub.
 
 
# client 分享 Home, 但不分享 Session Bean
 
當 client 在執行 business logic method 時, 一個 Session Bean 使屬於該 client 的, 一個 Session Bean 的 client 能夠被確保市執行這個 bean method 時的唯一 client.
 
# 建立一個 Stateful Session Bean
1. client 取得一個 Home class stub 後, 呼叫 Home stub 的 create().
2. Home stub 將 create() 的呼叫送給 remote Home class.
3. Container 介入並提供服務.
4. bean 被實體化.
5. EJBObject class 跟 EJBObject class stub 也被實體化.
6. Home stub 回傳給 client EJBObject class stub.
 
# 建立一個 Stateless Session Bean
1. client 取得一個 Home class stub 後, 呼叫 Home stub 的 create().
2. Home stub 將 create() 的呼叫傳給 remote Home class.
3. Container 介入並提供服務.
4. bean 被實體化, 但是只待在 bean pool 裡! (等如果 client 對 EJBObject stub 呼叫 method, 它只會出來服務)
5. EJBObject class 跟 EJBObject class stub 也被實體化, 但是 EJBObject 並沒有跟 bean 有任何關聯! 所以說, 真正被呼叫method 前, Stateless Session Bean 不需要這個保鏢.
6. Home stub 回傳給 client EJBObject class stub.
 
ps: client 呼叫 Home stub 的 create(), 並不表示 Stateless Session bean 會跟 EJBObject 同時被建立出來. 這完全是取決於 container.
 
* Stateless Session bean 較具有延展性 (scalable)
client 不會分享 EJBObject, 但是相同的 bean 卻可以服務多個 EJBObject, 只不過"不是"同時發生, 也就是說, 一個 business logic method 的呼叫中, 一次只能處理一個客戶. bean 會從 pool 出來處理一個 client 的呼叫, 然後又跳回去 pool, 一直重複此動作.
 

 
# Message-driven bean 不需要有用戶端視野, 也就是它們沒有 interface(沒有 Home interface, 也沒有 compoent interface).
 
1. client 傳送一個訊息到一個JMS (Java Message Service).
2. JMS 將訊息傳給 container.
3. container 從 pool 裡抓出一個 Message-driven bean.
4. Container 藉由呼叫這個 bean 的 MessageListener interface 的 onMessage(), 將訊息傳給這個 Message-driven bean.
 
 
 
 
 
 
 
 
 
 
 
 
 
arrow
arrow
    全站熱搜

    Kalin 發表在 痞客邦 留言(0) 人氣()