摘要:本譯文將向你介紹JavaTMRMI動(dòng)態(tài)類(lèi)文件下載的應(yīng)用,學(xué)習(xí)完本文,你將會(huì)對(duì)JavaTMRMI有進(jìn)一步的認(rèn)識(shí)。希望你能參考我的上一篇譯文:開(kāi)始學(xué)習(xí)Java RMI,遠(yuǎn)程方法調(diào)用-基礎(chǔ)篇。
1.概要 Java平臺(tái)一個(gè)重要的優(yōu)點(diǎn)就是可以動(dòng)態(tài)的從一個(gè)給定的URL下載Java軟件到一個(gè)正在運(yùn)行JVM的獨(dú)立進(jìn)程中,該進(jìn)程通常位于一個(gè)不同物理系統(tǒng)中。這樣可以讓一個(gè)遠(yuǎn)程系統(tǒng)運(yùn)行一個(gè)程序,例如一個(gè)Applet,它從來(lái)沒(méi)有被安裝到本地的存儲(chǔ)介質(zhì)上。在該文檔的前幾部分,我們先討論Applet的codebase,以幫助我們更好的介紹有關(guān)Java RMI的codebase. 舉例來(lái)說(shuō),一個(gè)運(yùn)行在瀏覽器中的虛擬機(jī),可以把java.applet.Applet的子類(lèi)和其相關(guān)類(lèi)的字節(jié)碼下載下來(lái)(到本地)。運(yùn)行該瀏覽器的系統(tǒng)以前從沒(méi)有運(yùn)行過(guò)該Applet,也沒(méi)有在本地安裝。一旦所有的類(lèi)從服務(wù)端下載完成,瀏覽器借助本地資源就開(kāi)始運(yùn)行這個(gè)Applet程序。 Java RMI正是采用了這個(gè)優(yōu)點(diǎn),下載、運(yùn)行這些從來(lái)沒(méi)有在本地安裝過(guò)的類(lèi)。調(diào)用Java RMI的API的虛擬機(jī),不僅僅像那些瀏覽器中,能夠下載任意Java類(lèi)文件,其中含有那些特定Java RMI存根類(lèi),它使借助服務(wù)器資源的遠(yuǎn)程調(diào)用的執(zhí)行成為可能。 Codebase觀點(diǎn)源于Java程序語(yǔ)言的ClassLoaders的應(yīng)用。當(dāng)一個(gè)Java程序使用一個(gè)ClassLoader時(shí),那么它需要知道它被允許到那里調(diào)用類(lèi)。通常,一個(gè)類(lèi)調(diào)用者和HTTP Server一起使用,Server為Java平臺(tái)應(yīng)用提供編譯過(guò)的類(lèi)。很可能,你提到第一對(duì)有關(guān)ClassLoader/codebase就是AppletClassLoader和作為HTML標(biāo)簽<applet>的“codebase”屬性。本文檔假設(shè)你有些Java RMI編程經(jīng)驗(yàn),同時(shí)寫(xiě)過(guò)一些含有applet標(biāo)簽的HTML文件。例如,在HTML源文件中(applet標(biāo)簽)將含有一些類(lèi)似下面的代碼:
2.什么是codebase 類(lèi)代碼址可以為一個(gè)源文件,或是一個(gè)目錄,虛擬機(jī)可以由此加載類(lèi)。舉個(gè)例子來(lái)說(shuō),如果你邀請(qǐng)一個(gè)朋友到家吃晚飯,你需要告訴你朋友你的居住方向,以便你的朋友能夠確定你家的位置。同樣,你可以把代碼庫(kù)址(Codebase)看作一個(gè)你指給JVM的方向,讓JVM能夠找到[可能是遠(yuǎn)程]它需要的類(lèi)。 你可以把你的classpath看作為是“本地代碼庫(kù)址”,因?yàn)樗且幌盗姓{(diào)用本地代碼類(lèi)目錄。當(dāng)基于本地調(diào)用類(lèi)時(shí),你的classpath環(huán)境變量是(JVM的)參照。CLasspath變量可以設(shè)定為一個(gè)相對(duì),絕對(duì)目錄或是類(lèi)文件壓縮包。倘若CLASSPATH是一種“本地代碼庫(kù)址”,那么Applets和遠(yuǎn)程對(duì)象使用的codebase也可以認(rèn)為是一種“遠(yuǎn)程代碼庫(kù)址”。 3.工作原理 3.1 Applets如何使用代碼庫(kù)址(codebase) 為了能和Applet交互,這個(gè)applet和其運(yùn)行中需要的任何類(lèi)必須能夠被客戶(hù)端訪問(wèn)。雖然applets也可以通過(guò)“ftp://”或是“file:///”地址訪問(wèn),但是它經(jīng)常是通過(guò)Web服務(wù)訪問(wèn)。 ● 1.客戶(hù)端瀏覽器請(qǐng)求的一個(gè)applet的類(lèi)在CLASSPATH中無(wú)法找到 ● 2.通過(guò)HTTP,applet(和它需要其他類(lèi))從服務(wù)端下載到客戶(hù)端 ● 3.在客戶(hù)端執(zhí)行applet 圖一:下載Applets <applet>標(biāo)簽含有的代碼庫(kù)址(codebase)通常是HTML頁(yè)面URL的相對(duì)地址。 3.2 Java RMI如何使用代碼庫(kù)址(codebase) 使用Java RMI,應(yīng)用程序能夠創(chuàng)建出遠(yuǎn)程對(duì)象,該對(duì)象接受從客戶(hù)端中JVM方法調(diào)用。為了能讓客戶(hù)端調(diào)用遠(yuǎn)程對(duì)象中的方法,客戶(hù)端必須采用一種機(jī)制來(lái)和遠(yuǎn)程對(duì)象交流。Java RMI 使用了一個(gè)叫做存根的特殊類(lèi),它能夠被下載到客戶(hù)端和遠(yuǎn)程對(duì)象的交流(進(jìn)行方法的調(diào)用),而不是使用(專(zhuān)用)程序使客戶(hù)端同遠(yuǎn)程對(duì)象的方法進(jìn)行對(duì)話。java.rmi.server.codebase屬性代表了一個(gè)或是多個(gè)URL地址,從該地址那些存根類(lèi)(和存根需要的其他類(lèi))能夠被下載。 像applet,那些要進(jìn)行遠(yuǎn)程方法調(diào)用的類(lèi)也要從“file:///”地址下載,但是也像applet,一個(gè)“file:///”地址通常要求客戶(hù)端和服務(wù)端位于同樣的物理主機(jī)上,除非URL使用其他的文件系統(tǒng),像NFS,這樣才能變得有效。 通常,那些被用來(lái)進(jìn)行遠(yuǎn)程方法調(diào)用的類(lèi),都是通過(guò)網(wǎng)絡(luò)資源訪問(wèn)的,像是HTTP或是FTP服務(wù)器。 圖二:下載Java RMI 存根類(lèi) ● 1. 遠(yuǎn)程對(duì)象代碼庫(kù)址(codebase)是通過(guò)在遠(yuǎn)程對(duì)象服務(wù)端設(shè)定java.rmi.server.codebase屬性指定的。在Java RMI 注冊(cè)表的幫助下,Java RMI 服務(wù)端注冊(cè)了一個(gè)遠(yuǎn)程對(duì)象,并綁定了一個(gè)名字。服務(wù)端JVM中代碼庫(kù)址(codebase)設(shè)定給在Java RMI注冊(cè)表中的遠(yuǎn)程對(duì)象引用提供了注解。
● 2. Java RMI客戶(hù)端請(qǐng)求一個(gè)(已知)命名的遠(yuǎn)程對(duì)象的引用??蛻?hù)端使用該引用(遠(yuǎn)程對(duì)象的存根實(shí)例)進(jìn)行對(duì)遠(yuǎn)程對(duì)象的方法調(diào)用。 ● 3. Java RMI注冊(cè)表向請(qǐng)求的類(lèi)返回一個(gè)引用(存根實(shí)例)??蛻?hù)端會(huì)優(yōu)先于codebase在本地的classpath中尋找存根類(lèi),如果發(fā)現(xiàn)了,那么它就會(huì)在本地調(diào)用該類(lèi)。然而,如果在本地的classpath找不到該stub的存根類(lèi),客戶(hù)端就會(huì)試著從遠(yuǎn)程對(duì)象代碼庫(kù)址(codebase)中檢索該類(lèi)。 ● 4. 客戶(hù)端從代碼庫(kù)址(codebase)請(qǐng)求類(lèi)??蛻?hù)端使用的代碼庫(kù)址(codebase)就是存根實(shí)例注解的URL,它是在存根類(lèi)被注冊(cè)表加載時(shí)注解的。回到第一步1,為導(dǎo)出對(duì)象的存根做注解,然后隨著綁定的名字被注冊(cè)到注冊(cè)表中。 ● 5. 定義的存根類(lèi)(和其需要的其他類(lèi))被下載到客戶(hù)端。 注意:第4和5是相同的步驟,當(dāng)遠(yuǎn)程對(duì)象被一個(gè)名字綁定到注冊(cè)表中時(shí),注冊(cè)表就開(kāi)始調(diào)用該遠(yuǎn)程對(duì)象類(lèi)。當(dāng)注冊(cè)表試著調(diào)用遠(yuǎn)程對(duì)象的存根類(lèi)時(shí),從代碼庫(kù)址(codebase)中,它和遠(yuǎn)程對(duì)象一起請(qǐng)求該類(lèi)的定義。 ● 6. 現(xiàn)在客戶(hù)端已經(jīng)具備了調(diào)用遠(yuǎn)程對(duì)象方法的所有條件。存根(在這過(guò)程中)像一個(gè)服務(wù)端的遠(yuǎn)程對(duì)象的代理一樣;不像applet使用代碼庫(kù)址(codebase)運(yùn)行代碼在本地的虛擬機(jī)中,Java RMI 客戶(hù)端使用遠(yuǎn)程的代碼庫(kù)址(codebase)運(yùn)行代碼在另一個(gè),可能是遠(yuǎn)程JVM中。如圖三所示: 圖三:Java RMI 客戶(hù)端遠(yuǎn)程方法調(diào)用 4.在Java RMI中利用codebase屬性,實(shí)現(xiàn)非存根(sub)類(lèi)的下載 除了下載存根類(lèi)(stub)和其輔助類(lèi)到客戶(hù)端,java.rmi.server.codebase屬性還被用來(lái)指定其他的,不僅僅是stub類(lèi)的下載地址。 當(dāng)客戶(hù)端調(diào)用遠(yuǎn)程對(duì)象的方法時(shí),該方法可能無(wú)參或是有許多的參數(shù),根據(jù)方法參數(shù)類(lèi)型,這樣就可能有三種不同情況發(fā)生。 第一種情況,所有的(遠(yuǎn)程)方法參數(shù)(或是返回值)都是原始的數(shù)據(jù)類(lèi)型,這樣遠(yuǎn)程對(duì)象知道如何的解釋他們作為方法的參數(shù),同時(shí)也無(wú)需檢查classpath和codebase屬性。 第二種情況,至少有一個(gè)參數(shù)或是返回值是一個(gè)對(duì)象,然而遠(yuǎn)程對(duì)象可以在本地的classpath中可以找到該對(duì)象類(lèi)的定義。 第三種情況(如圖四,第六步所示),遠(yuǎn)程方法收到一個(gè)對(duì)象參數(shù),然而遠(yuǎn)程對(duì)象在本地的classpath中沒(méi)有找到對(duì)象的定義。這種遠(yuǎn)程方法的調(diào)用情況如圖四所示??蛻?hù)端發(fā)送的對(duì)象類(lèi)可能是(遠(yuǎn)程方法)參數(shù)類(lèi)的子類(lèi)型,它可能是兩者其中之一: ● 一個(gè)接口的實(shí)現(xiàn),該接口為方法的參數(shù)(或是返回值)類(lèi)型 ● 一個(gè)類(lèi)的子類(lèi),該類(lèi)為方法的參數(shù)(或是返回值)類(lèi)型 圖四:Java RMI客戶(hù)端遠(yuǎn)程方法調(diào)用,傳遞一個(gè)未知的參數(shù)類(lèi)型的子類(lèi)型 類(lèi)似applet的代碼庫(kù)址(codebase),客戶(hù)端設(shè)定的代碼庫(kù)址(codebase),用于其他JVM下載遠(yuǎn)程類(lèi),非遠(yuǎn)程類(lèi)和接口地址。如果在客戶(hù)端的應(yīng)用中設(shè)定了代碼庫(kù)址(codebase)屬性,那么客戶(hù)端在調(diào)用子類(lèi)型時(shí),代碼庫(kù)址(codebase)就被作為參數(shù)加到子類(lèi)型的實(shí)例上。如果在客戶(hù)端沒(méi)有設(shè)定代碼庫(kù)址(codebase),那么遠(yuǎn)程對(duì)象就會(huì)錯(cuò)誤的使用自己的代碼庫(kù)址(codebase)。
5.命令行例子 在applet情況下,代碼庫(kù)址(codebase)是嵌在網(wǎng)頁(yè)中的,就如我們?cè)诒疚牡牡谝徊糠挚吹降腍TML例子。 在Java RMI應(yīng)用時(shí),codebase不是依靠一個(gè)鑲嵌在網(wǎng)頁(yè)中類(lèi)的引用實(shí)現(xiàn)的,客戶(hù)端會(huì)和Java RMI的注冊(cè)表溝通獲得遠(yuǎn)程對(duì)象的應(yīng)用。由于遠(yuǎn)程對(duì)象的代碼庫(kù)址(codebase)可以指向任意URL,不能僅是一個(gè)相對(duì)于已知的URL地址,必須是存根類(lèi)(stub)和其相關(guān)類(lèi)目錄的絕對(duì)地址。代碼庫(kù)址(codebase)可以指向: ● 一個(gè)目錄地址,該目錄中含有類(lèi)包子目錄 ● 一個(gè)Jar文件路徑,含有類(lèi)包的目錄壓縮文件 ● 滿(mǎn)足以上條件的多個(gè)目錄或是多個(gè)Jar文件,中間用空格間隔 注意:如果代碼庫(kù)址(codebase)設(shè)定為一目錄地址,那么結(jié)尾一定要是“/”。 例子: 如果你把要下載類(lèi)在“webvector”HTTP服務(wù)器的export目錄下(在Web根目錄下),那么的你的代碼庫(kù)址(codebase)就該這樣設(shè)置: -Djava.rmi.server.codebase=http://webvector/export/ 如果你把要下載類(lèi)放在“webline”HTTP服務(wù)器的public目錄下(在Web根目錄下),一個(gè)名字為“mystuff.jar”的Jar文件,你的代碼庫(kù)址(codebase)就該如此設(shè)置: -Djava.rmi.server.codebase=http://webline/public/mystuff.jar 現(xiàn)在我們假設(shè)你把要下載的類(lèi)分為兩個(gè)文件“myStuff.jar”和“myOtherStuff.jar”,而且這兩個(gè)文件放在不同的服務(wù)器上(名字是:“webfront”和“webwave”),你的代碼庫(kù)址(codebase)屬性就該這樣設(shè)定: -Djava.rmi.server.codebase="http://webfront/myStuff.jar http://webwave/myOtherStuff.jar" 6.疑難問(wèn)題解答 如果你的Java RMI 程序配置正確,任何一個(gè)可以序列化的類(lèi),包含Java RMI 存根類(lèi),都是可以被下載下來(lái)的。動(dòng)態(tài)的存根(stub)能夠正常的下載,需要滿(mǎn)足幾種狀況: ● A. 通過(guò)URL提供的存根類(lèi)和存根類(lèi)依賴(lài)的任何類(lèi)能夠被客戶(hù)端可達(dá)。 ● B. 在服務(wù)程序中通過(guò)調(diào)用bind或是rebind設(shè)定(或是在程序安裝的過(guò)程中激活)(譯注:rebind(String url, Remote obj) 或是bind(String url, Remote obj)),如下情況:如步驟A中設(shè)定的URL同時(shí)如果設(shè)定的為一個(gè)目錄,那么必須是“/”結(jié)尾。 ● C. rmiregistry在它相關(guān)的classpath中找不到存根類(lèi)或是其依賴(lài)的其他類(lèi)。這也是為什么我們?cè)谧?cè)表調(diào)用存根時(shí),給它加上代碼庫(kù)址(codebase)的參數(shù)的原因了,在服務(wù)端或是安裝代碼中,作為一個(gè)調(diào)用的結(jié)果。 ● D. 客戶(hù)端安裝的SecurityManager允許存根(stub)下載。在Java 2 SE或是高版本中,這將意味著客戶(hù)端必須在策略文件中進(jìn)行合適的配置。 在使用Java RMI的java.rmi.server.codebase系統(tǒng)變量時(shí),有兩種經(jīng)常性的問(wèn)題,我們將在下邊討論。 6.1 運(yùn)行Java RMI服務(wù)端可能碰到的問(wèn)題 你碰到的第一個(gè)問(wèn)題可能是收到ClassNotFoundException的異常,當(dāng)你向注冊(cè)表綁定(bind或是rebind)一個(gè)遠(yuǎn)程對(duì)象和名字時(shí)。這種異常通常是由不合法的codebase屬性引起的,導(dǎo)致了在注冊(cè)表中不能定位遠(yuǎn)程對(duì)象的存根(stub)或是存根需要的其他類(lèi)。 同遠(yuǎn)程對(duì)象本身相比,遠(yuǎn)程對(duì)象的存根(stub)實(shí)現(xiàn)了所有同樣的接口,這需要特別的注意。因此這些接口,同其他定制的類(lèi)作為方法參數(shù)或是返回值,也必須能夠通過(guò)指定的代碼庫(kù)址(codebase)下載。 通常,由于忽略了屬性設(shè)定時(shí)URL中末尾“/”,導(dǎo)致這個(gè)異常的拋出。其他的一些原因可能是:屬性值不是一個(gè)URL;URL路徑拼寫(xiě)錯(cuò)誤或是不正確;設(shè)定的URL中存根類(lèi)(stub)和其相關(guān)的類(lèi)不存在。 這種情況下,你遇到的異??赡苁沁@樣:
6.2 運(yùn)行Java RMI客戶(hù)端可能碰到的問(wèn)題 你可能遇到第二個(gè)問(wèn)題,就是在注冊(cè)表中查找遠(yuǎn)程對(duì)象時(shí),輸出ClassNotFoundException的異常。如果你在運(yùn)行客戶(hù)端代碼時(shí)收到這個(gè)堆棧異常信息,那么問(wèn)題可能是你的Java RMI注冊(cè)表啟動(dòng)時(shí)classpath設(shè)定的問(wèn)題。參考 requirement C in section 6.0.這里有一個(gè)這樣異常例子:
其他資源 如果你對(duì)代碼庫(kù)址(codebase)還有其他的沒(méi)有解答的問(wèn)題,請(qǐng)首先瀏覽RMI-USER組。 你可能也想加入RMI-USER郵件列表中。 |
|
來(lái)自: 螞蟻搬家 > 《我的圖書(shū)館》