Java 遠(yuǎn)程方法調(diào)用(RMI)
1. RMI 簡介
2. RMI 體系結(jié)構(gòu)
3. 遠(yuǎn)程異常
4. 服務(wù)器開發(fā)步驟概述
5. 編寫遠(yuǎn)程接口
6. 實(shí)現(xiàn)遠(yuǎn)程接口
7. 編寫 RMI 服務(wù)器概述
8. 設(shè)置安全性管理器
9. 命名遠(yuǎn)程對(duì)象
10. 生成存根和框架
11. 客戶機(jī)開發(fā)概述
12. 應(yīng)用程序 vs. applet
13. 編寫 RMI 客戶機(jī)應(yīng)用程序
14. 設(shè)置安全性管理器
15. 查找對(duì)象
16. 調(diào)用遠(yuǎn)程方法
17. 編寫 RMI 客戶機(jī) applet
18. 編寫用于 applet 的 HTML 頁面
19. 部署概述
20. 啟動(dòng) RMI 注冊(cè)表
21. 啟動(dòng)對(duì)象服務(wù)器
22. 運(yùn)行客戶機(jī)應(yīng)用程序
23. 運(yùn)行客戶機(jī) applet
我們將從 Java 遠(yuǎn)程方法調(diào)用(RMI)開始討論,Java 1.1 中引入了這種技術(shù)。
RMI 的用途是使分布在不同虛擬機(jī)中的對(duì)象的外表和行為都象本地對(duì)象一樣。調(diào)用遠(yuǎn)程對(duì)象的虛擬機(jī)有時(shí)稱為客戶機(jī)。類似地,我們將包含遠(yuǎn)程對(duì)象的虛擬機(jī)稱為服務(wù)器。
獲取遠(yuǎn)程對(duì)象的引用和獲取本地對(duì)象的引用有點(diǎn)不同,但一旦獲得了引用,就可以象調(diào)用本地對(duì)象一樣調(diào)用遠(yuǎn)程對(duì)象,如下面的代碼片段所示。RMI 基礎(chǔ)結(jié)構(gòu)將自動(dòng)截取請(qǐng)求,找到遠(yuǎn)程對(duì)象,并遠(yuǎn)程地分派請(qǐng)求。
這種位置透明性甚至包括垃圾收集。也就是說,客戶機(jī)不必特地釋放遠(yuǎn)程對(duì)象,RMI 基礎(chǔ)結(jié)構(gòu)和遠(yuǎn)程虛擬機(jī)為您處理垃圾收集。
RMI 體系結(jié)構(gòu)
|
第 2 頁(共23 頁)
|
為了實(shí)現(xiàn)位置透明性,RMI 引入了兩種特殊類型的對(duì)象:
存根(stub)和
框架(skeleton)。
存根是代表遠(yuǎn)程對(duì)象的客戶機(jī)端對(duì)象。存根具有和遠(yuǎn)程對(duì)象相同的接口或方法列表,但當(dāng)客戶機(jī)調(diào)用存根方法時(shí),存根通過 RMI 基礎(chǔ)結(jié)構(gòu)將請(qǐng)求轉(zhuǎn)發(fā)到遠(yuǎn)程對(duì)象,實(shí)際上由遠(yuǎn)程對(duì)象執(zhí)行請(qǐng)求。
在服務(wù)器端,
框架對(duì)象處理"遠(yuǎn)方"的所有細(xì)節(jié),因此實(shí)際的遠(yuǎn)程對(duì)象不必?fù)?dān)心這些細(xì)節(jié)。也就是說,您完全可以象編碼本地對(duì)象一樣來編碼遠(yuǎn)程對(duì)象。框架將遠(yuǎn)程對(duì)象從 RMI 基礎(chǔ)結(jié)構(gòu)分離開來。在遠(yuǎn)程方法請(qǐng)求期間,RMI 基礎(chǔ)結(jié)構(gòu)自動(dòng)調(diào)用框架對(duì)象,因此它可以發(fā)揮自己的作用。
關(guān)于這種設(shè)置的最大的好處是,您不必親自為存根和框架編寫代碼。JDK 包含工具 rmic,它會(huì)為您創(chuàng)建存根和框架的類文件。
實(shí)際上,RMI 使用 TCP/IP 套接字來傳達(dá)遠(yuǎn)程方法請(qǐng)求。盡管套接字是相當(dāng)可靠的傳輸,但還是有許多事情可能出錯(cuò)。例如,假設(shè)服務(wù)器計(jì)算機(jī)在方法請(qǐng)求期間崩潰了?;蛘?,假設(shè)客戶機(jī)和服務(wù)器計(jì)算機(jī)是通過因特網(wǎng)連接的,而客戶機(jī)掉線了。
關(guān)鍵在于,使用遠(yuǎn)程對(duì)象時(shí)比使用本地對(duì)象時(shí)有更多可能出錯(cuò)的機(jī)會(huì)。因此客戶機(jī)程序能夠完美地從錯(cuò)誤中恢復(fù)就很重要了。
因此要讓客戶機(jī)知道此類錯(cuò)誤,每個(gè)將要被遠(yuǎn)程調(diào)用的方法都必須拋出 RemoteException,java.rmi 包中定義了該異常。
服務(wù)器開發(fā)步驟概述
|
第 4 頁(共23 頁)
|
讓我們研究一下編寫對(duì)象服務(wù)器所涉及的步驟。我們將花一些時(shí)間研究客戶機(jī)端。
您需要做的第一件事情是定義用于遠(yuǎn)程對(duì)象的接口。這個(gè)接口定義了客戶機(jī)能夠遠(yuǎn)程地調(diào)用的方法。遠(yuǎn)程接口和本地接口的主要差異在于,遠(yuǎn)程方法必須能拋出上一頁描述的 RemoteException。
接下來,編寫一個(gè)實(shí)現(xiàn)該接口的類。
然后,編寫在服務(wù)器上運(yùn)行的主程序。這個(gè)程序必須實(shí)例化一個(gè)或多個(gè)服務(wù)器對(duì)象,然后,通常將遠(yuǎn)程對(duì)象注冊(cè)到 RMI 名稱注冊(cè)表,這樣客戶機(jī)就能夠找到對(duì)象。
最后,生成存根和框架的代碼。JDK 提供了 rmic 工具,它讀取遠(yuǎn)程對(duì)象的類文件并為存根和框架創(chuàng)建類文件。
編寫遠(yuǎn)程接口
|
第 5 頁(共23 頁)
|
下面的代碼顯示了一個(gè)簡單遠(yuǎn)程接口的接口定義。實(shí)現(xiàn)這個(gè)接口的對(duì)象提供三個(gè)方法:一個(gè)方法返回字符串、一個(gè)方法接受字符串作為參數(shù)、而另一個(gè)方法不接受參數(shù)也不返回任何結(jié)果。正如先前提到的,這些方法必須能拋出 RemoteException,如果客戶機(jī)和服務(wù)器之間的通信出錯(cuò),則客戶機(jī)將捕獲此異常。
注:該接口本身繼承了 java.rmi 包中定義的 Remote 接口。Remote 接口本身沒有定義方法,但通過繼承它,我們說明該接口可以被遠(yuǎn)程地調(diào)用。
這里是編寫遠(yuǎn)程接口的代碼:
import java.rmi.*;
public interface Meeting extends Remote
{
public String getDate ()
throws RemoteException;
public void setDate ( String date )
throws RemoteException;
public void scheduleIt()
throws RemoteException;
}
實(shí)現(xiàn)遠(yuǎn)程接口
|
第 6 頁(共23 頁)
|
現(xiàn)在,讓我們研究一個(gè)實(shí)現(xiàn)遠(yuǎn)程 Meeting 接口的類。通常,MeetingServer 繼承 UnicastRemoteObject 類,UnicastRemoteObject 類提供遠(yuǎn)程對(duì)象所需的基本行為。術(shù)語"單播(unicast)"是指每個(gè)客戶機(jī)存根引用單個(gè)遠(yuǎn)程對(duì)象的現(xiàn)象。以后,RMI 可能會(huì)允許"多播(multicasting)",即一個(gè)存根可以引用幾個(gè)對(duì)等的遠(yuǎn)程對(duì)象。使用多播,RMI 基礎(chǔ)結(jié)構(gòu)可以均衡一組遠(yuǎn)程對(duì)象之間的負(fù)載。
下面的代碼樣本顯示了兩個(gè)方法的實(shí)現(xiàn):Meeting 接口中定義的 getDate 方法和一個(gè)無參數(shù)構(gòu)造器。請(qǐng)注意:這兩者都拋出 RemoteException;所有由客戶機(jī)遠(yuǎn)程調(diào)用的方法和構(gòu)造器都需要拋出這個(gè)異常。在本示例中,構(gòu)造器無實(shí)際工作可做,但我們還是需要定義它,這樣它能夠拋出遠(yuǎn)程異常。
但是 getDate 方法很有趣。它向調(diào)用者返回一個(gè)字符串的引用。雖然這看起來可能很簡單,但是,在這里 RMI 基礎(chǔ)結(jié)構(gòu)和框架以及存根實(shí)際上有很多工作要做。它們必須協(xié)同工作,以便將字符串的一個(gè)副本傳回客戶機(jī),然后,在客戶機(jī)的虛擬機(jī)中作為對(duì)象重新創(chuàng)建。
這里是實(shí)現(xiàn)遠(yuǎn)程接口的代碼:
import java.rmi.*;
import java.rmi.server.*;
public class MeetingServer
extends UnicastRemoteObject
implements Meeting
{
private String ivDate = new String ( "1/1/2000" );
public MeetingServer()
throws RemoteException
{
}
public String getDate()
throws RemoteException
{
return ivDate;
}
...
}
編寫 RMI 服務(wù)器概述
|
第 7 頁(共23 頁)
|
除了實(shí)現(xiàn)接口之外,我們還需要編寫服務(wù)器的主程序。目前 RMI 不支持作為 applet 的服務(wù)器程序,所以主程序必需是獨(dú)立的 Java 應(yīng)用程序。您要么為主程序編碼一個(gè)單獨(dú)的 Java 類,要么象我們?cè)谶@里所做的一樣,只在實(shí)現(xiàn)類中編碼一個(gè) main 方法。
還請(qǐng)注意,我們編碼的 main 函數(shù)可以向命令行拋出任何與 RMI 相關(guān)的異常。對(duì)于象本文中的小樣本程序,這樣做沒有問題,但在實(shí)際程序中,您可能要將執(zhí)行的步驟包括到獨(dú)立的 try-catch 塊中,從而能夠更好地執(zhí)行錯(cuò)誤處理。
這里是編寫 RMI 服務(wù)器的代碼結(jié)構(gòu):
import java.rmi.*;
import java.rmi.server.*;
public class MeetingServer
extends UnicastRemoteObject
implements Meeting
{
...
public static void main (String [] args ) throws
RemoteException, java.net.MalformedURLException,
RMISecurityException
{
// 1. Set Security manager
// 2. Create an object instance
// 3. Register object into the name space
}
}
*** 本圖將被刪除。留在這里僅供對(duì)照/檢查用 ***
服務(wù)器 main 通常要做的步驟是:
- 安裝安全性管理器類,它允許服務(wù)器程序從其它機(jī)器接收存根類
- 創(chuàng)建服務(wù)器對(duì)象的實(shí)例
- 將服務(wù)器對(duì)象注冊(cè)到 RMI 命名注冊(cè)表以便客戶機(jī)程序能找到該服務(wù)器對(duì)象
現(xiàn)在,讓我們進(jìn)一步研究這些步驟。
設(shè)置安全性管理器
|
第 8 頁(共23 頁)
|
第一步是安裝 RMI 安全性管理器。盡管這不是嚴(yán)格必須的,但它確實(shí)允許服務(wù)器虛擬機(jī)下載類文件。例如,假設(shè)客戶機(jī)調(diào)用服務(wù)器中的方法,該方法接受對(duì)應(yīng)用程序定義的對(duì)象類型(例如 BankAccount)的引用。通過設(shè)置安全性管理器,我們?cè)试S RMI 運(yùn)行時(shí)動(dòng)態(tài)地將 BankAccount 類文件復(fù)制到服務(wù)器,從而簡化了服務(wù)器上的配置。
讓 RMI 動(dòng)態(tài)地下載這些類的弊端是有安全性風(fēng)險(xiǎn)。也就是說,實(shí)質(zhì)上我們是在讓服務(wù)器執(zhí)行來自另一臺(tái)機(jī)器的代碼。雖然我們希望這些類文件不會(huì)危及服務(wù)器,但如果希望避免這樣的風(fēng)險(xiǎn),則您的 RMI 服務(wù)器不應(yīng)該安裝安全性管理器。然后,您必須確保將所有類文件安裝在本地服務(wù)器的類路徑中。
這里是用于設(shè)置安全性管理器的代碼:
import java.rmi.*;
import java.rmi.server.*;
public static void main (String [] args ) throws
RemoteException, java.net.MalformedURLException,
RMISecurityException
{
System.setSecurityManager (
new RMISecuritymanager() ); MeetingServer ms = new MeetingServer();
Naming.rebind (
"rmi://myhost.com/Meeting", ms );
}
*** 本圖將被刪除。留在這里僅供對(duì)照/檢查用 ***
注:傳遞對(duì)象參數(shù)類型是涉及許多方面的主題,因?yàn)橛袃煞N方法實(shí)現(xiàn)它。一種是在通信線路上僅傳遞引用;另一種是將對(duì)象序列化并在遠(yuǎn)程創(chuàng)建新對(duì)象。在本教程中,我們不會(huì)更深入地討論這些問題,但您應(yīng)該閱讀 JDK 中的 RMI 文檔以獲取更多詳細(xì)信息。
命名遠(yuǎn)程對(duì)象
|
第 9 頁(共23 頁)
|
服務(wù)器的下一步工作是創(chuàng)建服務(wù)器對(duì)象的初始實(shí)例,然后將對(duì)象的名稱寫到 RMI 命名注冊(cè)表。RMI 命名注冊(cè)表允許您將 URL 名稱分配給對(duì)象以便客戶機(jī)查找它們。要注冊(cè)名稱,需調(diào)用靜態(tài) rebind 方法,它是在 Naming 類上定義的。這個(gè)方法接受對(duì)象的 URL 名稱以及對(duì)象引用。
名稱字符串是很有趣的部分。它包含 rmi:// 前綴、運(yùn)行 RMI 對(duì)象的服務(wù)器的計(jì)算機(jī)主機(jī)名和對(duì)象本身的名稱,這個(gè)名稱正是您所想要的。注:您可以調(diào)用由 java.net.InetAddress 類定義的 getLocalHost 方法,而不必象我們?cè)谶@里所做的一樣硬編碼主機(jī)名。
這里是命名遠(yuǎn)程對(duì)象的代碼:
import java.rmi.*;
import java.rmi.server.*;
public static void main (String [] args ) throws
RemoteException, java.net.MalformedURLException,
RMISecurityException
{
System.setSecurityManager (
new RMISecuritymanager() );
MeetingServer ms = new MeetingServer();
Naming.rebind (
"rmi://myhost.com/Meeting", ms );
編寫并編譯了服務(wù)器實(shí)現(xiàn)之后,就準(zhǔn)備創(chuàng)建存根和框架類。那很容易:只要運(yùn)行 JDK 的 rmic 命令,指定實(shí)現(xiàn)類文件名(不帶擴(kuò)展名)。rmic 工具將為每個(gè)類文件創(chuàng)建一個(gè)存根和一個(gè)框架。然后,您需要正確地部署這些文件,在討論完編寫客戶機(jī)端代碼之后,我們將討論部署問題。
客戶機(jī)開發(fā)概述
|
第 11 頁(共23 頁)
|
現(xiàn)在,讓我們研究客戶機(jī)端。首先,您必須確定是想編寫客戶機(jī)獨(dú)立應(yīng)用程序還是客戶機(jī) applet。應(yīng)用程序的設(shè)置簡單些,但 applet 更容易部署,因?yàn)?Java RMI 基礎(chǔ)結(jié)構(gòu)能夠?qū)⑺鼈兿螺d到客戶機(jī)機(jī)器。我們將討論如何實(shí)現(xiàn)這兩者。
在客戶機(jī)中,代碼需要首先使用 RMI 注冊(cè)表來查找遠(yuǎn)程對(duì)象。一旦這樣做了之后,客戶機(jī)就可以調(diào)用由遠(yuǎn)程接口定義的方法。
應(yīng)用程序 vs. applet
|
第 12 頁(共23 頁)
|
讓我們來簡略看一下 Java 應(yīng)用程序和 applet 之間的差異。如果編寫應(yīng)用程序,必須定義一個(gè) main 入口點(diǎn),可以從中執(zhí)行 RMI 啟動(dòng)代碼。然后,您必須在客戶機(jī)機(jī)器上安裝應(yīng)用程序的類文件。如果有多臺(tái)客戶機(jī)計(jì)算機(jī),則您必須在每臺(tái)機(jī)器上手工安裝這個(gè)應(yīng)用程序的類文件。并且,正如我們過一會(huì)兒將要看到的,您還要在客戶機(jī)應(yīng)用程序計(jì)算機(jī)上安裝一些與 RMI 相關(guān)的服務(wù)器文件。
相反,如果您決定編寫一個(gè) applet,則不需要 main 入口點(diǎn)。applet 重寫了由瀏覽器調(diào)用的 init 方法??梢栽?init 中編寫與您在應(yīng)用程序的 main 中編寫的同類代碼。applet 的主要優(yōu)點(diǎn)是,除了瀏覽器,您不必在客戶機(jī)計(jì)算機(jī)上預(yù)安裝任何東西 ― Java RMI 基礎(chǔ)結(jié)構(gòu)將自動(dòng)下載所有必要的類文件。但是,請(qǐng)注意,您必須編寫一個(gè)瀏覽器能夠裝入的 HTML 文件。我們將簡略地討論這一切。
編寫 RMI 客戶機(jī)應(yīng)用程序
|
第 13 頁(共23 頁)
|
讓我們研究一下編寫 RMI 獨(dú)立應(yīng)用程序所包含的步驟。我們照常將 main 方法編碼為入口點(diǎn),并且在其中執(zhí)行 RMI 初始化步驟。首先設(shè)置安全性管理器,以便 RMI 運(yùn)行時(shí)能夠下載類文件,然后通過使用 RMI 命名注冊(cè)表獲取一個(gè)對(duì)遠(yuǎn)程對(duì)象的引用。最后我們就能夠調(diào)用遠(yuǎn)程方法了。
這里是編寫 RMI 客戶機(jī)應(yīng)用程序的代碼:
import java.rmi.*;
public class MeetingClient
{
public static void main ( String [] args )
throws RemoteException,
java.net.MalformedURLException,
java.rmi.NotBoundException
{
// 1. Set Security Manager
// 2. Look up remote object from name space
// 3. Call remote methods
}
}
*** 本圖將被刪除。留在這里僅供對(duì)照/檢查用 ***
雖然這個(gè)代碼顯示了僅在 main 方法中調(diào)用遠(yuǎn)程方法,但一旦應(yīng)用程序檢索到對(duì)象引用,則應(yīng)用程序也可以在其它方法中調(diào)用遠(yuǎn)程方法。此外,請(qǐng)注意,不需要進(jìn)行清理,即使對(duì)遠(yuǎn)程對(duì)象,Java RMI 基礎(chǔ)結(jié)構(gòu)也會(huì)保證進(jìn)行垃圾收集工作。
現(xiàn)在我們將更詳細(xì)地討論這些步驟。
設(shè)置安全性管理器
|
第 14 頁(共23 頁)
|
該代碼看起來和對(duì)象服務(wù)器 main 中的代碼類似。象對(duì)象服務(wù)器一樣,客戶機(jī)應(yīng)用程序也可以選擇是否設(shè)置安全性管理器。并且原因也相似:RMI 運(yùn)行時(shí)會(huì)自動(dòng)將遠(yuǎn)程對(duì)象的存根類文件下載到客戶機(jī),但僅當(dāng)應(yīng)用程序安裝了安全性管理器時(shí)才能這樣做。如果應(yīng)用程序使用缺省的安全性管理器,則需要在客戶機(jī)計(jì)算機(jī)的類路徑預(yù)安裝存根類文件,否則應(yīng)用程序?qū)⒉东@到一個(gè)安全性異常。
還應(yīng)該注意,在這個(gè)代碼中 main 方法只是將與 RMI 相關(guān)的異常拋回到命令行。更健壯的應(yīng)用程序應(yīng)該包含 try-catch 塊以便本地進(jìn)行錯(cuò)誤處理。
這里是設(shè)置安全性管理器的代碼:
import java.rmi.*;
public static void main ( String [] args )
throws RemoteException,
java.net.MalformedURLException,
java.rmi.NotBoundException
{
System.setSecurityManager(new RMISecurityManager()); ...
}
一旦應(yīng)用程序安裝了可使用的安全性管理器,它就可以從 RMI 命名注冊(cè)表檢索遠(yuǎn)程對(duì)象的引用。要這樣做,需調(diào)用靜態(tài) lookup 方法,傳遞對(duì)象服務(wù)器為遠(yuǎn)程對(duì)象所注冊(cè)的相同名稱。lookup 方法將檢索對(duì)遠(yuǎn)程對(duì)象的引用并創(chuàng)建存根對(duì)象,這里,存根對(duì)象存儲(chǔ)在變量 r 中。
如果客戶機(jī)提供的名稱與注冊(cè)表中的名稱不匹配,則 lookup 方法拋出 NotBoundException。如果所提供的 URL 無效,則 lookup 拋出 MalformedURLException。在下面的這個(gè)簡單的代碼片段中,沒有顯式地捕獲這些異常,但在實(shí)際程序中您需要這樣做。
請(qǐng)注意返回的引用類型是 Remote 類型,它是所有遠(yuǎn)程對(duì)象的超類。但是實(shí)際上,我們想要的是作為遠(yuǎn)程接口中定義的對(duì) Meeting 接口的引用。因此,我們需要如下一頁所述那樣對(duì)引用進(jìn)行向下強(qiáng)制類型轉(zhuǎn)換。
這里是查找對(duì)象的代碼:
import java.rmi.*;
public static void main ( String [] args )
throws RemoteException,
java.net.MalformedURLException,
java.rmi.NotBoundException
{
System.setSecurityManager(new RMISecurityManager());
Remote r = Naming.lookup {
"rmi://myhost.com/Meeting" ); ...
}
調(diào)用遠(yuǎn)程方法
|
第 16 頁(共23 頁)
|
在應(yīng)用程序能夠調(diào)用遠(yuǎn)程方法之前,它必須轉(zhuǎn)換遠(yuǎn)程引用的類型以匹配接口定義,在本示例中是 HelloInterface。雖然您可以使用簡單的強(qiáng)制類型轉(zhuǎn)換做到這一點(diǎn),但這里顯示的代碼首先通過調(diào)用 instanceof 運(yùn)算符來檢查強(qiáng)制類型轉(zhuǎn)換是否有效。當(dāng)遠(yuǎn)程對(duì)象不是預(yù)期的類型時(shí),這種技術(shù)可使您可以避免 InvalidCast 運(yùn)行時(shí)異常。
一旦我們成功地對(duì)引用進(jìn)行了強(qiáng)制類型轉(zhuǎn)換,就可以調(diào)用遠(yuǎn)程方法;這里是 getDate 方法,它返回一個(gè)字符串。
這里是調(diào)用遠(yuǎn)程方法的代碼:
import java.rmi.*;
public static void main ( String [] args )
throws RemoteException,
java.net.MalformedURLException,
java.rmi.NotBoundException
{
...
Remote r = Naming.lookup (
"rmi://myhost.com/Meeting");
String s = null;
if (r instanceof Meeting )
{
ms = (Meeting)r;
s = ms.getDate(); }
}
編寫 RMI 客戶機(jī) applet
|
第 17 頁(共23 頁)
|
現(xiàn)在,讓我們研究將 RMI 客戶機(jī)編寫為 applet 而不是獨(dú)立應(yīng)用程序時(shí)這兩者的差異。有三個(gè)基本差異:
- 通常的 applet 是在 init 方法而不是在 main 編碼 RMI 初始化。
- 因?yàn)槟荒芨?init 的方法說明(正在從 Applet 重寫它),所以不能將 init 編碼為可拋出的異常。因此必須用 try-catch 塊處理異常。
- 不必安裝 RMI 安全性管理器,因?yàn)?applet 自動(dòng)使用允許下載遠(yuǎn)程類的 AppletSecurityManager。
盡管有這些不同,applet 客戶機(jī)中與 RMI 相關(guān)的代碼和應(yīng)用程序中的基本相同。在這里仍然要使用 RMI 命名注冊(cè)表來查找遠(yuǎn)程引用,并將返回的引用強(qiáng)制轉(zhuǎn)換成正確類型等。
applet 和應(yīng)用程序之間的另一個(gè)差異是:要在瀏覽器內(nèi)使用 applet,必需編寫引用 applet 的 HTML 頁面。在下一頁我們研究這個(gè)問題。
這里是編寫 RMI 客戶機(jī) applet 的代碼:
import java.rmi.*;
public class MeetingClientApplet extends java.applet.Applet
{
public void init ()
{
try
(
//1. Look up remote object in the RMI registry
//2. Call remote methods (can also call from
// other methods if you save th reference)
}
catch ( Exception e )
{
}
}
}
編寫用于 applet 的 HTML 頁面
|
第 18 頁(共23 頁)
|
這是一個(gè)簡單的 HTML 文件,它裝入上一頁顯示的 applet。HTML 頁面中關(guān)鍵的一行是 applet 標(biāo)記,在這個(gè)標(biāo)記中指定 applet 的類文件(無文件擴(kuò)展名)。然后,需要在 Web 服務(wù)器上安裝該 HTML 文件和 applet 類文件。當(dāng)用戶在其支持 Java 平臺(tái)的瀏覽器中顯示這個(gè)頁面時(shí),瀏覽器會(huì)將 applet 類文件下載到客戶機(jī)計(jì)算機(jī),并調(diào)用 init 方法,在本示例中,它開始 RMI 通信。
<HML>
<title>Meeting Client Applet</title>
...
<applet code="MeetingClientApplet" width=500 height=120>
</appleet>
</HTML>
下一步任務(wù)是研究如何部署 RMI
代碼。我們將研究兩種情況:部署客戶機(jī)獨(dú)立應(yīng)用程序和部署客戶機(jī) applet
。
部署 RMI 程序所包括的步驟有:
- 啟動(dòng)運(yùn)行在 RMI 對(duì)象服務(wù)器上的 RMI 注冊(cè)表程序。
- 啟動(dòng)對(duì)象服務(wù)器主程序。
- 設(shè)置 Web 或 ftp 服務(wù)器以便 RMI 運(yùn)行時(shí)能夠如先前所述那樣下載類文件。
- 運(yùn)行客戶機(jī)應(yīng)用程序或在瀏覽器中顯示帶有 applet 的 HTML 頁面。
讓我們更詳細(xì)地研究每個(gè)步驟。
啟動(dòng) RMI 注冊(cè)表
|
第 20 頁(共23 頁)
|
部署的第一步是啟動(dòng)對(duì)象服務(wù)器計(jì)算機(jī)上的 RMI 命名注冊(cè)表。Java Development Kit 中包含了 rmiregistry 命令。
下圖顯示了在一個(gè)單獨(dú)窗口中啟動(dòng)注冊(cè)表的 Microsoft Windows 命令(start rmiregistry)。有關(guān)特定的指示,請(qǐng)參閱您的操作系統(tǒng)文檔。
注冊(cè)表程序管理從名稱到對(duì)象引用的映射,以便程序能夠調(diào)用 Naming.bind 來注冊(cè)名稱,以及調(diào)用 Naming.lookup 來檢索引用。
一個(gè)告誡:當(dāng)啟動(dòng)注冊(cè)表時(shí),請(qǐng)確保有效的類路徑?jīng)]有引用服務(wù)器上的存根類文件。如果引用的話,則 RMI 基礎(chǔ)結(jié)構(gòu)不會(huì)自動(dòng)將存根下載到客戶機(jī)計(jì)算機(jī)。
啟動(dòng)對(duì)象服務(wù)器
|
第 21 頁(共23 頁)
|
啟動(dòng)注冊(cè)表之后,接下來我們可以啟動(dòng)服務(wù)器計(jì)算機(jī)上的 RMI 對(duì)象服務(wù)器主程序。和啟動(dòng)注冊(cè)表類似,這一步與客戶機(jī)是 applet 還是應(yīng)用程序無關(guān)。
請(qǐng)記住我們編碼 main 方法,以創(chuàng)建對(duì)象并將對(duì)象的名稱注冊(cè)到 RMI 注冊(cè)表中。下圖顯示了包含 MeetingServer 遠(yuǎn)程對(duì)象及其框架的 MeetingServer 虛擬機(jī)。(java -Djava.rmi.server.codebase=http://mywebserver.com/ -Djava.security.policy=mypolicy MeetingServer)
對(duì)象服務(wù)器虛擬機(jī)需要訪問圖中顯示的所有類文件:MeetingServer 主程序、框架類文件和接口類文件。因此,在啟動(dòng)服務(wù)器程序之前,您必須確保所有這些文件都在類路徑中。存根類文件必須安裝在 Web 服務(wù)器上;注:我們?cè)诿钪幸?Web 服務(wù)器以啟動(dòng)對(duì)象服務(wù)器主程序。在本示例中,代碼庫特性僅指定主機(jī)名,而沒有指定目錄,所以 Web 服務(wù)器將從其缺省目錄提供服務(wù)。有關(guān)如何設(shè)置目錄的更多詳細(xì)信息,請(qǐng)參閱 Web 服務(wù)器文檔。
從 JDK 1.2 開始,RMI 服務(wù)器還需要引用一個(gè)策略文件,該文件授予或拒絕遠(yuǎn)程對(duì)象的權(quán)限。在本示例中,我們創(chuàng)建了一個(gè)授予所有權(quán)限的策略文件;對(duì)象這樣的樣本程序而言,這沒有問題,但對(duì)于生產(chǎn)環(huán)境這樣做可能太危險(xiǎn)了。注:當(dāng)啟動(dòng)對(duì)象服務(wù)器時(shí),必須指定策略文件。
和以前一樣,本圖顯示了用來運(yùn)行對(duì)象服務(wù)器 main 程序的 Microsoft Windows 命令。有關(guān)具體細(xì)節(jié),請(qǐng)參閱操作系統(tǒng)文檔。
運(yùn)行客戶機(jī)應(yīng)用程序
|
第 22 頁(共23 頁)
|
現(xiàn)在,讓我們研究運(yùn)行客戶機(jī)程序。我們將從研究作為獨(dú)立應(yīng)用程序的客戶機(jī)開始,然后再討論客戶機(jī) applet。
客戶機(jī)計(jì)算機(jī)上的虛擬機(jī)需要訪問客戶機(jī)程序本身的類文件和遠(yuǎn)程接口的類文件;必須在客戶機(jī)類路徑中安裝這些文件??蛻魴C(jī)還需要訪問存根類,但不必在客戶機(jī)上安裝這個(gè)文件。RMI 運(yùn)行時(shí)會(huì)自動(dòng)按需下載存根類。請(qǐng)記住,為了利用這個(gè)特性,我們需要在客戶機(jī)應(yīng)用程序中安裝安全性管理器。因此我們可以將存根類文件放置在運(yùn)行 Web 服務(wù)器的計(jì)算機(jī)上,而不是安裝在客戶機(jī)上。
當(dāng)我們運(yùn)行客戶機(jī)應(yīng)用程序時(shí)(請(qǐng)參閱命令提示 java -Djava.rmi.server.codebase=http://mywebserver.com/ -Djava.security.policy=mypolicy MeetingClient),指定代碼庫特性來指明去哪里可以找到存根類文件。如果您不希望動(dòng)態(tài)下載存根或沒有 Web 服務(wù)器,那么可以將存根類文件手工復(fù)制到客戶機(jī)計(jì)算機(jī)上的類路徑。
注:一旦下載了存根,與 RMI 服務(wù)器的所有進(jìn)一步的交互就都使用 RMI 協(xié)議。這包含命名注冊(cè)表中的查找和遠(yuǎn)程方法調(diào)用和返回。
正如我們?cè)诜?wù)器中看到的那樣,在 JDK 1.2 和更高版本中,客戶機(jī)還需要使用策略文件。這里,我們將服務(wù)器上所使用的策略文件復(fù)制到了客戶機(jī)上,所以可以方便地在命令行上引用它。
注:使用 ftp 協(xié)議而不是 http 來將存根下載到客戶機(jī),這也是有可能的。有關(guān)詳細(xì)信息,請(qǐng)參閱 JDK 文檔。
運(yùn)行客戶機(jī) applet
|
第 23 頁(共23 頁)
|
運(yùn)行客戶機(jī) applet 和運(yùn)行獨(dú)立應(yīng)用程序頗有些不同。一個(gè)差異是您通常從運(yùn)行在客戶機(jī)計(jì)算機(jī)上的瀏覽器內(nèi)運(yùn)行 applet。當(dāng)然,瀏覽器需要支持 Java 平臺(tái)。
另一個(gè)差異是因?yàn)?applet 已經(jīng)支持下載類文件,所以除了要重裝瀏覽器之外,不必在客戶機(jī)計(jì)算機(jī)上預(yù)安裝任何東西。與應(yīng)用程序不同,您可以將所有必需的 HTML 文件和類文件都放置在 Web 服務(wù)器上,applet 和 RMI 基礎(chǔ)結(jié)構(gòu)會(huì)按需下載這些文件。我們也不必關(guān)心客戶機(jī)上的策略文件。
還有另一個(gè)關(guān)于 applet 的復(fù)雜問題:如果希望對(duì)象服務(wù)器和 Web 服務(wù)器駐留在不同的主機(jī)上,則需要了解對(duì) applet 進(jìn)行數(shù)字簽名。那是因?yàn)?,缺省情況下,瀏覽器安全性管理器僅允許網(wǎng)絡(luò)訪問(包括 RMI 方法)返回到 Web 服務(wù)器。也就是說,除非對(duì) applet 進(jìn)行了簽名,否則,如果將對(duì)象分發(fā)到不是 Web 服務(wù)器的服務(wù)器上,您將會(huì)看到安全性違例。數(shù)字簽名這個(gè)主題已經(jīng)超出了這個(gè)簡短課程的范圍之外。
最后,應(yīng)該注意,實(shí)際上,有可能編寫?yīng)毩?yīng)用程序,以便可以按需下載幾乎所有東西,類似于這里所討論的 applet。要獲取更多詳細(xì)信息,請(qǐng)參考關(guān)于
自舉應(yīng)用程序的 JDK 文檔。
關(guān)于 RMI,我們已經(jīng)討論得夠多了,足夠讓您開始編寫和部署自己的分布式程序?,F(xiàn)在,讓我們研究用于分布式對(duì)象的另一種基礎(chǔ)結(jié)構(gòu):CORBA 方法。