歡迎您光臨本站 註冊首頁

Java RMI 實現代碼動態下載

←手機掃碼閱讀     火星人 @ 2014-03-10 , reply:0

摘要:本譯文將向你介紹JavaTMRMI動態類文件下載的應用,學習完本文,你將會對JavaTMRMI有進一步的認識.希望你能參考我的上一篇譯文:開始學習Java RMI,遠程方法調用-基礎篇.

1.概要

Java平台一個重要的優點就是可以動態的從一個給定的URL下載Java軟體到一個正在運行JVM的獨立進程中,該進程通常位於一個不同物理系統中.這樣可以讓一個遠程系統運行一個程序,例如一個Applet,它從來沒有被安裝到本地的存儲介質上.在該文檔的前幾部分,我們先討論Applet的codebase,以幫助我們更好的介紹有關Java RMI的codebase.

舉例來說,一個運行在瀏覽器中的虛擬機,可以把java.applet.Applet的子類和其相關類的位元組碼下載下來(到本地).運行該瀏覽器的系統以前從沒有運行過該Applet,也沒有在本地安裝.一旦所有的類從服務端下載完成,瀏覽器藉助本地資源就開始運行這個Applet程序.

Java RMI正是採用了這個優點,下載、運行這些從來沒有在本地安裝過的類.調用Java RMI的API的虛擬機,不僅僅像那些瀏覽器中,能夠下載任意Java類文件,其中含有那些特定Java RMI存根類,它使藉助伺服器資源的遠程調用的執行成為可能.

Codebase觀點源於Java程序語言的ClassLoaders的應用.當一個Java程序使用一個ClassLoader時,那麼它需要知道它被允許到那裡調用類.通常,一個類調用者和HTTP Server一起使用,Server為Java平台應用提供編譯過的類.很可能,你提到第一對有關ClassLoader/codebase就是AppletClassLoader和作為HTML標籤<applet>的「codebase」屬性.本文檔假設你有些Java RMI編程經驗,同時寫過一些含有applet標籤的HTML文件.例如,在HTML源文件中(applet標籤)將含有一些類似下面的代碼:

<applet height=100 width=100 codebase="myclasses/" code="My.class"> <param name="ticker"> </applet>

2.什麼是codebase

類代碼址可以為一個源文件,或是一個目錄,虛擬機可以由此載入類.舉個例子來說,如果你邀請一個朋友到家吃晚飯,你需要告訴你朋友你的居住方向,以便你的朋友能夠確定你家的位置.同樣,你可以把代碼庫址(Codebase)看作一個你指給JVM的方向,讓JVM能夠找到[可能是遠程]它需要的類.

你可以把你的classpath看作為是「本地代碼庫址」,因為它是一系列調用本地代碼類目錄.當基於本地調用類時,你的classpath環境變數是(JVM的)參照.CLasspath變數可以設定為一個相對,絕對目錄或是類文件壓縮包.倘若CLASSPATH是一種「本地代碼庫址」,那麼Applets和遠程對象使用的codebase也可以認為是一種「遠程代碼庫址」.

3.工作原理

3.1 Applets如何使用代碼庫址(codebase)

為了能和Applet交互,這個applet和其運行中需要的任何類必須能夠被客戶端訪問.雖然applets也可以通過「ftp://」或是「file:///」地址訪問,但是它經常是通過Web服務訪問.

● 1.客戶端瀏覽器請求的一個applet的類在CLASSPATH中無法找到

● 2.通過HTTP,applet(和它需要其他類)從服務端下載到客戶端

● 3.在客戶端執行applet

圖一:下載Applets

<applet>標籤含有的代碼庫址(codebase)通常是HTML頁面URL的相對地址.

3.2 Java RMI如何使用代碼庫址(codebase)

使用Java RMI,應用程序能夠創建出遠程對象,該對象接受從客戶端中JVM方法調用.為了能讓客戶端調用遠程對象中的方法,客戶端必須採用一種機制來和遠程對象交流.Java RMI 使用了一個叫做存根的特殊類,它能夠被下載到客戶端和遠程對象的交流(進行方法的調用),而不是使用(專用)程序使客戶端同遠程對象的方法進行對話.java.rmi.server.codebase屬性代表了一個或是多個URL地址,從該地址那些存根類(和存根需要的其他類)能夠被下載.

像applet,那些要進行遠程方法調用的類也要從「file:///」地址下載,但是也像applet,一個「file:///」地址通常要求客戶端和服務端位於同樣的物理主機上,除非URL使用其他的文件系統,像NFS,這樣才能變得有效.

通常,那些被用來進行遠程方法調用的類,都是通過網路資源訪問的,像是HTTP或是FTP伺服器.

圖二:下載Java RMI 存根類

● 1. 遠程對象代碼庫址(codebase)是通過在遠程對象服務端設定java.rmi.server.codebase屬性指定的.在Java RMI 註冊表的幫助下,Java RMI 服務端註冊了一個遠程對象,並綁定了一個名字.服務端JVM中代碼庫址(codebase)設定給在Java RMI註冊表中的遠程對象引用提供了註解.

● 2. Java RMI客戶端請求一個(已知)命名的遠程對象的引用.客戶端使用該引用(遠程對象的存根實例)進行對遠程對象的方法調用.

● 3. Java RMI註冊表向請求的類返回一個引用(存根實例).客戶端會優先於codebase在本地的classpath中尋找存根類,如果發現了,那麼它就會在本地調用該類.然而,如果在本地的classpath找不到該stub的存根類,客戶端就會試著從遠程對象代碼庫址(codebase)中檢索該類.

● 4. 客戶端從代碼庫址(codebase)請求類.客戶端使用的代碼庫址(codebase)就是存根實例註解的URL,它是在存根類被註冊表載入時註解的.回到第一步1,為導出對象的存根做註解,然後隨著綁定的名字被註冊到註冊表中.

● 5. 定義的存根類(和其需要的其他類)被下載到客戶端.

注意:第4和5是相同的步驟,當遠程對象被一個名字綁定到註冊表中時,註冊表就開始調用該遠程對象類.當註冊表試著調用遠程對象的存根類時,從代碼庫址(codebase)中,它和遠程對象一起請求該類的定義.

● 6. 現在客戶端已經具備了調用遠程對象方法的所有條件.存根(在這過程中)像一個服務端的遠程對象的代理一樣;不像applet使用代碼庫址(codebase)運行代碼在本地的虛擬機中,Java RMI 客戶端使用遠程的代碼庫址(codebase)運行代碼在另一個,可能是遠程JVM中.如圖三所示:

圖三:Java RMI 客戶端遠程方法調用

4.在Java RMI中利用codebase屬性,實現非存根(sub)類的下載

除了下載存根類(stub)和其輔助類到客戶端,java.rmi.server.codebase屬性還被用來指定其他的,不僅僅是stub類的下載地址.

當客戶端調用遠程對象的方法時,該方法可能無參或是有許多的參數,根據方法參數類型,這樣就可能有三種不同情況發生.

第一種情況,所有的(遠程)方法參數(或是返回值)都是原始的數據類型,這樣遠程對象知道如何的解釋他們作為方法的參數,同時也無需檢查classpath和codebase屬性.

第二種情況,至少有一個參數或是返回值是一個對象,然而遠程對象可以在本地的classpath中可以找到該對象類的定義.

第三種情況(如圖四,第六步所示),遠程方法收到一個對象參數,然而遠程對象在本地的classpath中沒有找到對象的定義.這種遠程方法的調用情況如圖四所示.客戶端發送的對象類可能是(遠程方法)參數類的子類型,它可能是兩者其中之一:

● 一個介面的實現,該介面為方法的參數(或是返回值)類型

● 一個類的子類,該類為方法的參數(或是返回值)類型

圖四:Java RMI客戶端遠程方法調用,傳遞一個未知的參數類型的子類型

類似applet的代碼庫址(codebase),客戶端設定的代碼庫址(codebase),用於其他JVM下載遠程類,非遠程類和介面地址.如果在客戶端的應用中設定了代碼庫址(codebase)屬性,那麼客戶端在調用子類型時,代碼庫址(codebase)就被作為參數加到子類型的實例上.如果在客戶端沒有設定代碼庫址(codebase),那麼遠程對象就會錯誤的使用自己的代碼庫址(codebase).

5.命令行例子

在applet情況下,代碼庫址(codebase)是嵌在網頁中的,就如我們在本文的第一部分看到的HTML例子.

在Java RMI應用時,codebase不是依靠一個鑲嵌在網頁中類的引用實現的,客戶端會和Java RMI的註冊表溝通獲得遠程對象的應用.由於遠程對象的代碼庫址(codebase)可以指向任意URL,不能僅是一個相對於已知的URL地址,必須是存根類(stub)和其相關類目錄的絕對地址.代碼庫址(codebase)可以指向:

● 一個目錄地址,該目錄中含有類包子目錄

● 一個Jar文件路徑,含有類包的目錄壓縮文件

● 滿足以上條件的多個目錄或是多個Jar文件,中間用空格間隔

注意:如果代碼庫址(codebase)設定為一目錄地址,那麼結尾一定要是「/」.

例子:

如果你把要下載類在「webvector」HTTP伺服器的export目錄下(在Web根目錄下),那麼的你的代碼庫址(codebase)就該這樣設置:

-Djava.rmi.server.codebase=http://webvector/export/

如果你把要下載類放在「webline」HTTP伺服器的public目錄下(在Web根目錄下),一個名字為「mystuff.jar」的Jar文件,你的代碼庫址(codebase)就該如此設置:

-Djava.rmi.server.codebase=http://webline/public/mystuff.jar

現在我們假設你把要下載的類分為兩個文件「myStuff.jar」和「myOtherStuff.jar」,這兩個文件放在不同的伺服器上(名字是:「webfront」和「webwave」),你的代碼庫址(codebase)屬性就該這樣設定:

-Djava.rmi.server.codebase="http://webfront/myStuff.jar http://webwave/myOtherStuff.jar"

6.疑難問題解答

如果你的Java RMI 程序配置正確,任何一個可以序列化的類,包含Java RMI 存根類,都是可以被下載下來的.動態的存根(stub)能夠正常的下載,需要滿足幾種狀況:

● A. 通過URL提供的存根類和存根類依賴的任何類能夠被客戶端可達.

● B. 在服務程序中通過調用bind或是rebind設定(或是在程序安裝的過程中激活)(譯註:rebind(String url, Remote obj) 或是bind(String url, Remote obj)),如下情況:如步驟A中設定的URL同時如果設定的為一個目錄,那麼必須是「/」結尾.

● C. rmiregistry在它相關的classpath中找不到存根類或是其依賴的其他類.這也是為什麼我們在註冊表調用存根時,給它加上代碼庫址(codebase)的參數的原因了,在服務端或是安裝代碼中,作為一個調用的結果.

● D. 客戶端安裝的SecurityManager允許存根(stub)下載.在Java 2 SE或是高版本中,這將意味著客戶端必須在策略文件中進行合適的配置.

在使用Java RMI的java.rmi.server.codebase系統變數時,有兩種經常性的問題,我們將在下邊討論.

6.1 運行Java RMI服務端可能碰到的問題

你碰到的第一個問題可能是收到ClassNotFoundException的異常,當你向註冊表綁定(bind或是rebind)一個遠程對象和名字時.這種異常通常是由不合法的codebase屬性引起的,導致了在註冊表中不能定位遠程對象的存根(stub)或是存根需要的其他類.

同遠程對象本身相比,遠程對象的存根(stub)實現了所有同樣的介面,這需要特別的注意.因此這些介面,同其他定製的類作為方法參數或是返回值,也必須能夠通過指定的代碼庫址(codebase)下載.

通常,由於忽略了屬性設定時URL中末尾「/」,導致這個異常的拋出.其他的一些原因可能是:屬性值不是一個URL;URL路徑拼寫錯誤或是不正確;設定的URL中存根類(stub)和其相關的類不存在.

這種情況下,你遇到的異常可能是這樣:

java.rmi.ServerException: RemoteException occurred in server thread; nested exception is:    	java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is:   	java.lang.ClassNotFoundException: examples.callback.MessageReceiverImpl_Stub   java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is:   	java.lang.ClassNotFoundException: examples.callback.MessageReceiverImpl_Stub   java.lang.ClassNotFoundException: examples.callback.MessageReceiverImpl_Stub   	at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(Compiled Code)   	at sun.rmi.transport.StreamRemoteCall.executeCall(Compiled Code)   	at sun.rmi.server.UnicastRef.invoke(Compiled Code)   	at sun.rmi.registry.RegistryImpl_Stub.rebind(Compiled Code)   	at java.rmi.Naming.rebind(Compiled Code)   	at examples.callback.MessageReceiverImpl.main(Compiled Code)   RemoteException occurred in server thread; nested exception is:   	java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is:    	java.lang.ClassNotFoundException: examples.callback.MessageReceiverImpl_Stub

6.2 運行Java RMI客戶端可能碰到的問題

你可能遇到第二個問題,就是在註冊表中查找遠程對象時,輸出ClassNotFoundException的異常.如果你在運行客戶端代碼時收到這個堆棧異常信息,那麼問題可能是你的Java RMI註冊表啟動時classpath設定的問題.參考 requirement C in section 6.0.這裡有一個這樣異常例子:

java.rmi.UnmarshalException: Return value class not found; nested exception is:
java.lang.ClassNotFoundException: MyImpl_Stub
at sun.rmi.registry.RegistryImpl_Stub.lookup(RegistryImpl_Stub.java:109
at java.rmi.Naming.lookup(Naming.java:60)
at RmiClient.main(MyClient.java:28)

其他資源

如果你對代碼庫址(codebase)還有其他的沒有解答的問題,請瀏覽RMI-USER組.

你可能也想加入RMI-USER郵件列表中.


[火星人 ] Java RMI 實現代碼動態下載已經有639次圍觀

http://coctec.com/docs/java/show-post-61937.html