<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    程序人生

    在Java中摸爬滾打的日子

      BlogJava :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
      6 Posts :: 1 Stories :: 19 Comments :: 0 Trackbacks
        開通博客也有好些天了,一直沒有時(shí)間靜下心來寫博文,今天我就把兩年前整理的一篇關(guān)于JAVA RMI入門級(jí)文章貼出來,供有這方面需要的同學(xué)們參考學(xué)習(xí)。

    RMI 相關(guān)知識(shí)

    RMI全稱是Remote Method Invocation-遠(yuǎn)程方法調(diào)用,Java RMIJDK1.1中實(shí)現(xiàn)的,其威力就體現(xiàn)在它強(qiáng)大的開發(fā)分布式網(wǎng)絡(luò)應(yīng)用的能力上,是純Java的網(wǎng)絡(luò)分布式應(yīng)用系統(tǒng)的核心解決方案之一。其實(shí)它可以被看作是RPCJava版本。但是傳統(tǒng)RPC并不能很好地應(yīng)用于分布式對(duì)象系統(tǒng)。而Java RMI 則支持存儲(chǔ)于不同地址空間的程序級(jí)對(duì)象之間彼此進(jìn)行通信,實(shí)現(xiàn)遠(yuǎn)程對(duì)象之間的無縫遠(yuǎn)程調(diào)用。

    RMI目前使用Java遠(yuǎn)程消息交換協(xié)議JRMPJava Remote Messaging Protocol)進(jìn)行通信由于JRMP是專為Java對(duì)象制定的Java RMI具有Java"Write Once,Run Anywhere"的優(yōu)點(diǎn),是分布式應(yīng)用系統(tǒng)的百分之百純Java解決方案。用Java RMI開發(fā)的應(yīng)用系統(tǒng)可以部署在任何支持JREJava Run Environment Java,運(yùn)行環(huán)境)的平臺(tái)上。但由于JRMP是專為Java對(duì)象制定的,因此,RMI對(duì)于用非Java語言開發(fā)的應(yīng)用系統(tǒng)的支持不足。不能與用非Java語言書寫的對(duì)象進(jìn)行通信。

    RMI可利用標(biāo)準(zhǔn)Java本機(jī)方法接口JNI與現(xiàn)有的和原有的系統(tǒng)相連接。RMI還可利用標(biāo)準(zhǔn)JDBC包與現(xiàn)有的關(guān)系數(shù)據(jù)庫連接。RMI/JNIRMI/JDBC相結(jié)合,可幫助您利用RMI與目前使用非Java語言的現(xiàn)有服務(wù)器進(jìn)行通信,而且在您需要時(shí)可擴(kuò)展Java在這些服務(wù)器上的使用。RMI可幫助您在擴(kuò)展使用時(shí)充分利用Java的強(qiáng)大功能。


    一、RMI(遠(yuǎn)程方法調(diào)用)的組成

    一個(gè)正常工作的RMI系統(tǒng)由下面幾個(gè)部分組成: 

    ·遠(yuǎn)程服務(wù)的接口定義 
    ·遠(yuǎn)程服務(wù)接口的具體實(shí)現(xiàn) 
    ·樁(Stub)和框架(Skeleton)文件 
    ·一個(gè)運(yùn)行遠(yuǎn)程服務(wù)的服務(wù)器 
    ·一個(gè)RMI命名服務(wù),它允許客戶端去發(fā)現(xiàn)這個(gè)遠(yuǎn)程服務(wù) 
    ·類文件的提供者(一個(gè)HTTP或者FTP服務(wù)器) 
    ·一個(gè)需要這個(gè)遠(yuǎn)程服務(wù)的客戶端程序 


    二、RMI(遠(yuǎn)程方法調(diào)用)原理示意圖



    方法調(diào)用從客戶對(duì)象經(jīng)占位程序(Stub)、遠(yuǎn)程引用層(Remote Reference Layer)和傳輸層(Transport Layer)向下,傳遞給主機(jī),然后再次經(jīng)傳 輸層,向上穿過遠(yuǎn)程調(diào)用層和骨干網(wǎng)(Skeleton),到達(dá)服務(wù)器對(duì)象。 占位程序扮演著遠(yuǎn)程服務(wù)器對(duì)象的代理的角色,使該對(duì)象可被客戶激活。 遠(yuǎn)程引用層處理語義、管理單一或多重對(duì)象的通信,決定調(diào)用是應(yīng)發(fā)往一個(gè)服務(wù)器還是多個(gè)。傳輸層管理實(shí)際的連接,并且追蹤可以接受方法調(diào)用的遠(yuǎn)程對(duì)象。服務(wù)器端的骨干網(wǎng)完成對(duì)服務(wù)器對(duì)象實(shí)際的方法調(diào)用,并獲取返回值。返回值向下經(jīng)遠(yuǎn)程引用層、服務(wù)器端的傳輸層傳遞回客戶端,再向上經(jīng)傳輸層和遠(yuǎn)程調(diào)用層返回。最后,占位程序獲得返回值。 

    要完成以上步驟需要有以下幾個(gè)步驟: 

    1、 生成一個(gè)遠(yuǎn)程接口 

    2、 實(shí)現(xiàn)遠(yuǎn)程對(duì)象(服務(wù)器端程序)

    3、 生成占位程序和骨干網(wǎng)(服務(wù)器端程序)

    4、 編寫服務(wù)器程序 

    5、 編寫客戶程序 

    6、 注冊(cè)遠(yuǎn)程對(duì)象 

    7、 啟動(dòng)遠(yuǎn)程對(duì)象 


    三、RMI(遠(yuǎn)程方法調(diào)用)的優(yōu)點(diǎn) 

    從最基本的角度看,RMIJava遠(yuǎn)程過程調(diào)用(RPC)機(jī)制。與傳統(tǒng)的RPC系統(tǒng)相比,RMI具有若干優(yōu)點(diǎn),因?yàn)樗?/font>Java面向?qū)ο蠓椒?/span>的一部分。傳統(tǒng)的RPC系統(tǒng)采用中性語言,所以是最普通的系統(tǒng)--它們不能提供所有可能的目標(biāo)平臺(tái)所具有的功能。 

    RMIJava為核心,可與采用本機(jī)方法與現(xiàn)有系統(tǒng)相連接。這就是說,RMI可采用自然、直接和功能全面的方式為您提供分布式計(jì)算技術(shù),而這種技術(shù)可幫助您以不斷遞增和無縫的方式為整個(gè)系統(tǒng)添加Java功能。

    RMI的主要優(yōu)點(diǎn)如下: 

    面向?qū)ο?/span>RMI可將完整的對(duì)象作為參數(shù)和返回值進(jìn)行傳遞,而不僅僅是預(yù)定義的數(shù)據(jù)類型。也就是說,您可以將類似Java哈希表這樣的復(fù)雜類型作為一個(gè)參數(shù)進(jìn)行傳遞。而在目前的RPC系統(tǒng)中,您只能依靠客戶機(jī)將此類對(duì)象分解成基本數(shù)據(jù)類型,然后傳遞這些數(shù)據(jù)類型,最后在服務(wù)器端重新創(chuàng)建哈希表。RMI則不需額外的客戶程序代碼(將對(duì)象分解成基本數(shù)據(jù)類型),直接跨網(wǎng)傳遞對(duì)象。 

    可移動(dòng)屬性:RMI可將屬性(類實(shí)現(xiàn)程序)從客戶機(jī)移動(dòng)到服務(wù)器,或者從服務(wù)器移到客戶機(jī)。這樣就能具備最大的靈活性,因?yàn)檎吒淖儠r(shí)只需要您編寫一個(gè)新的Java類,并將其在服務(wù)器主機(jī)上安裝一次即可。 

    設(shè)計(jì)方式:對(duì)象傳遞功能使您可以在分布式計(jì)算中充分利用面向?qū)ο蠹夹g(shù)的強(qiáng)大功能,如二層和三層結(jié)構(gòu)系統(tǒng)。如果您能夠傳遞屬性,那么您就可以在您的解決方案中使用面向?qū)ο蟮脑O(shè)計(jì)方式。所有面向?qū)ο蟮脑O(shè)計(jì)方式無不依靠不同的屬性來發(fā)揮功能,如果不能傳遞完整的對(duì)象--包括實(shí)現(xiàn)和類型--就會(huì)失去設(shè)計(jì)方式上所提供的優(yōu)點(diǎn)。 

    安  全:RMI使用Java內(nèi)置的安全機(jī)制保證下載執(zhí)行程序時(shí)用戶系統(tǒng)的安全。RMI使用專門為保護(hù)系統(tǒng)免遭惡意小應(yīng)用程序侵害而設(shè)計(jì)的安全管理程序,可保護(hù)您的系統(tǒng)和網(wǎng)絡(luò)免遭潛在的惡意下載程序的破壞。在情況嚴(yán)重時(shí),服務(wù)器可拒絕下載任何執(zhí)行程序。 

    便于編寫和使用:RMI使得Java遠(yuǎn)程服務(wù)程序和訪問這些服務(wù)程序的Java客戶程序的編寫工作變得輕松、簡單。遠(yuǎn)程接口實(shí)際上就是Java接口。服務(wù)程序大約用三行指令宣布本身是服務(wù)程序,其它方面則與任何其它Java對(duì)象類似。這種簡單方法便于快速編寫完整的分布式對(duì)象系統(tǒng)的服務(wù)程序,并快速地制做軟件的原型和早期版本,以便于進(jìn)行測試和評(píng)估。因?yàn)?font face="Courier New">RMI程序編寫簡單,所以維護(hù)也簡單。 

    可連接現(xiàn)有/原有的系統(tǒng):RMI可通過Java的本機(jī)方法接口JNI與現(xiàn)有系統(tǒng)進(jìn)行進(jìn)行交互。利用RMIJNI,您就能用Java語言編寫客戶端程序,還能使用現(xiàn)有的服務(wù)器端程序。在使用RMI/JNI與現(xiàn)有服務(wù)器連接時(shí),您可以有選擇地用Java重新編寫服務(wù)程序的任何部分,并使新的程序充分發(fā)揮Java的功能。類似地,RMI可利用JDBC、在不修改使用數(shù)據(jù)庫的現(xiàn)有非Java源代碼的前提下與現(xiàn)有關(guān)系數(shù)據(jù)庫進(jìn)行交互。 

    編寫一次,到處運(yùn)行:RMIJava“編寫一次,到處運(yùn)行 方法的一部分。任何基于RMI的系統(tǒng)均可100%地移植到任何Java虛擬機(jī)上,RMI/JDBC系統(tǒng)也不例外。如果使用RMI/JNI與現(xiàn)有系統(tǒng)進(jìn)行交互工作,則采用JNI編寫的代碼可與任何Java虛擬機(jī)進(jìn)行編譯、運(yùn)行。 

    分布式垃圾收集:RMI采用其分布式垃圾收集功能收集不再被網(wǎng)絡(luò)中任何客戶程序所引用的遠(yuǎn)程服務(wù)對(duì)象。與Java 虛擬機(jī)內(nèi)部的垃圾收集類似,分布式垃圾收集功能允許用戶根據(jù)自己的需要定義服務(wù)器對(duì)象,并且明確這些對(duì)象在不再被客戶機(jī)引用時(shí)會(huì)被刪除。 

    并行計(jì)算:RMI采用多線程處理方法,可使您的服務(wù)器利用這些Java線程更好地并行處理客戶端的請(qǐng)求。Java分布式計(jì)算解決方案:RMIJDK 1.1開始就是Java平臺(tái)的核心部分,因此,它存在于任何一臺(tái)1.1 Java虛擬機(jī)中。所有RMI系統(tǒng)均采用相同的公開協(xié)議,所以,所有Java 系統(tǒng)均可直接相互對(duì)話,而不必事先對(duì)協(xié)議進(jìn)行轉(zhuǎn)換。


    四、RMICORBA的關(guān)系

    RMI 和 CORBA 常被視為相互競爭的技術(shù),因?yàn)閮烧叨继峁?duì)遠(yuǎn)程分布式對(duì)象的透明訪問。但這兩種技術(shù)實(shí)際上是相互補(bǔ)充的,一者的長處正好可以彌補(bǔ)另一者的短處。RMI 和 CORBA 的結(jié)合產(chǎn)生了 RMI-IIOPRMI-IIOP 是企業(yè)服務(wù)器端 Java 開發(fā)的基礎(chǔ)。1997 年,IBM 和 Sun Microsystems啟動(dòng)了一項(xiàng)旨在促進(jìn) Java 作為企業(yè)開發(fā)技術(shù)的發(fā)展的合作計(jì)劃。兩家公司特別著力于如何將 Java 用作服務(wù)器端語言,生成可以結(jié)合進(jìn)現(xiàn)有體系結(jié)構(gòu)的企業(yè)級(jí)代碼。所需要的就是一種遠(yuǎn)程傳輸技術(shù),它兼有 Java 的 RMIRemote Method Invocation,遠(yuǎn)程方法調(diào)用)較少的資源占用量和更成熟的 CORBACommon Object Request Broker Architecture公共對(duì)象請(qǐng)求代理體系結(jié)構(gòu))技術(shù)的健壯性。出于這一需要,RMI-IIOP問世了,它幫助將 Java 語言推向了目前服務(wù)器端企業(yè)開發(fā)的主流語言的領(lǐng)先地位。 
    (來源:sunmatrix.org.cn


    Java RMI 簡單示例一

    以下用一個(gè)最簡單的Hello的示例來介紹RMI的應(yīng)用。

    1定義一個(gè)遠(yuǎn)程接口

    IHello.java代碼如下

    import java.rmi.Remote;

    public interface IHello extends Remote {

    public String sayHello(String name) throws java.rmi.RemoteException;

    }


    2實(shí)現(xiàn)遠(yuǎn)程的接口(服務(wù)端就在此遠(yuǎn)程接口的實(shí)現(xiàn)類中)

    HelloImpl.java代碼如下

    import java.rmi.RemoteException;
    import java.rmi.server.UnicastRemoteObject;
    public class HelloImpl extends UnicastRemoteObject implements IHello {
        
    // 這個(gè)實(shí)現(xiàn)必須有一個(gè)顯式的構(gòu)造函數(shù),并且要拋出一個(gè)RemoteException異常  
        protected HelloImpl() throws RemoteException {
            
    super();
        }
        
    /**
         * 說明清楚此屬性的業(yè)務(wù)含義
         
    */
        
    private static final long serialVersionUID = 4077329331699640331L;
        
    public String sayHello(String name) throws RemoteException {
            
    return "Hello " + name + " ^_^ ";
        }
        
    public static void main(String[] args) {
            
    try {
                IHello hello 
    = new HelloImpl();
                java.rmi.Naming.rebind(
    "rmi://localhost:1099/hello", hello);
                System.out.print(
    "Ready");
            } 
    catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    3:新建RMI客戶端調(diào)用程序

    Hello_RMI_Client.java代碼如下

    import java.rmi.Naming;
    public class Hello_RMI_Client {
        
    public static void main(String[] args) {
            
    try {
                IHello hello 
    = (IHello) Naming.lookup("rmi://localhost:1099/hello");
                    System.out.println(hello.sayHello(
    "zhangxianxin"));
            } 
    catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    4:編譯并運(yùn)行

    4.1 javac命令編譯IHello.javaHelloImpl.javaHello_RMI_Client.java

    >javac *.java

    4.2 rmic命令生成樁和框架文件

     >rmic HelloImpl

    成功執(zhí)行完上面的命令可以發(fā)現(xiàn)生成一個(gè)HelloImpl_stub.class文件,如果JDK是使用Java2SDK,那么還可以發(fā)現(xiàn)多出一個(gè)HelloImpl_Skel.class文件。如果服務(wù)端程序與客戶端程序在同一臺(tái)機(jī)器上并在同一目錄中,則可以省略掉接口實(shí)現(xiàn)類生成的樁和框架文件,但這就失去了使用RMI的意義,而如果要在不同的JVM上運(yùn)行時(shí),客戶端程序就必須得依靠服務(wù)端運(yùn)程方法實(shí)現(xiàn)的樁和框架文件以及接口類。

    4.3 運(yùn)行注冊(cè)程序RMIRegistry,必須在包含剛寫的類的目錄下運(yùn)行這個(gè)注冊(cè)程序。

    >rmiregistry

    注冊(cè)程序開始運(yùn)行了,不要管他,現(xiàn)在切換到另外一個(gè)控制臺(tái)運(yùn)行服務(wù)器 

    4.4 運(yùn)行服務(wù)器HelloImpl

    >java HelloImpl

    當(dāng)啟動(dòng)成功出現(xiàn)Ready...... 這個(gè)服務(wù)器就開始工作了,把接口的實(shí)現(xiàn)加載到內(nèi)存等待客戶端的聯(lián)接。現(xiàn)在切換到第三個(gè)控制臺(tái),啟動(dòng)我們的客戶端。

    4.5 啟動(dòng)客戶端:為了在其他的機(jī)器運(yùn)行客戶端程序你需要一個(gè)遠(yuǎn)程接口(IHello.class) 和一個(gè)stub(HelloImpl_Stub.class)。 使用如下命令運(yùn)行客戶端

    >java Hello_RMI_Client

    當(dāng)運(yùn)行成功會(huì)在控制臺(tái)打印:Hello zhangxianxin ^_^

    備注:如果不想在控制臺(tái)上開啟RMI注冊(cè)程序RMIRegistry的話,可在RMI服務(wù)類程序中添加LocateRegistry.createRegistry(1099); 如下所示:

    修改后的HelloImpl.java代碼如下

    import java.rmi.RemoteException;
    import java.rmi.registry.LocateRegistry;
    import java.rmi.server.UnicastRemoteObject;
    public class HelloImpl extends UnicastRemoteObject implements IHello {
        
    // 這個(gè)實(shí)現(xiàn)必須有一個(gè)顯式的構(gòu)造函數(shù),并且要拋出一個(gè)RemoteException異常  
        protected HelloImpl() throws RemoteException {
            
    super();
        }
        
        
    private static final long serialVersionUID = 4077329331699640331L;
        
    public String sayHello(String name) throws RemoteException {
            
    return "Hello " + name + " ^_^ ";
        }
        
    public static void main(String[] args) {
            
    try {
                IHello hello 
    = new HelloImpl();
                LocateRegistry.createRegistry(
    1099); //加上此程序,就可以不要在控制臺(tái)上開啟RMI的注冊(cè)程序,1099是RMI服務(wù)監(jiān)視的默認(rèn)端口
                java.rmi.Naming.rebind("rmi://localhost:1099/hello", hello);
                System.out.print(
    "Ready");
            } 
    catch (Exception e) {
                e.printStackTrace();
            }
        }
        }


    Java RMI 簡單示例二

    以下用一個(gè)文件交換程序來介紹RMI的應(yīng)用。這個(gè)應(yīng)用允許客戶端從服務(wù)端交換(或下載)所有類型的文件。第一步是定義一個(gè)遠(yuǎn)程的接口,這個(gè)接口指定的簽名方法將被服務(wù)端提供和被客戶端調(diào)用。

    1.定義一個(gè)遠(yuǎn)程接口 

    IFileUtil.java代碼如下:

    import java.rmi.Remote;

    import java.rmi.RemoteException;

    public interface IFileUtil extends Remote {

    public byte[] downloadFile(String fileName) throws RemoteException;

    }

    接口IFileDownload提供了一個(gè)downloadFile方法,然后返回一個(gè)相應(yīng)的文件數(shù)據(jù)。

    2.實(shí)現(xiàn)遠(yuǎn)程的接口 
      FileImpl繼承于UnicastRemoteObject類。這顯示出FileImpl類是用來創(chuàng)建一個(gè)單獨(dú)的、不能復(fù)制的、遠(yuǎn)程的對(duì)象,這個(gè)對(duì)象使用RMI默認(rèn)的基于TCP的通信方式。

    FileUtilImpl.java代碼如下:


    import java.io.BufferedInputStream;

    import java.io.File;

    import java.io.FileInputStream;

    import java.rmi.RemoteException;

    import java.rmi.server.UnicastRemoteObject;

    public class FileUtilImpl extends UnicastRemoteObject implements IFileUtil {

    protected FileUtilImpl() throws RemoteException {

    super();

    }

    private static final long serialVersionUID = 7594622080290821912L;

    public byte[] downloadFile(String fileName) throws RemoteException{

    File file 
    = new File(fileName);

    byte buffer[] = new byte[(int) file.length()];

    int size = buffer.length;

    System.out.println(
    "download file size = "+size +"b");

    if(size>1024*1024*10){//限制文件大小不能超過10M,文件太大可能導(dǎo)制內(nèi)存溢出!

    throw new RemoteException("Error:<The File is too big!>");

    }

    try {

    BufferedInputStream input 
    = new BufferedInputStream(

    new FileInputStream(fileName));

    input.read(buffer, 
    0, buffer.length);

    input.close();

    System.out.println(
    "Info:<downloadFile() hed execute successful!>");

    return buffer;

    catch (Exception e) {

    System.out.println(
    "FileUtilImpl: " + e.getMessage());

    e.printStackTrace();

    return null;

    }

    }

    }

    3.編寫服務(wù)端 

    1)創(chuàng)建并安裝一個(gè)RMISecurityManager實(shí)例。

    2)創(chuàng)建一個(gè)遠(yuǎn)程對(duì)象的實(shí)例。

    3)使用RMI注冊(cè)工具來注冊(cè)這個(gè)對(duì)象。

    FileUtilServer.java 代碼如下:

    import java.rmi.Naming;

    import java.rmi.RMISecurityManager;

    public class FileUtilServer {

    public static void main(String argv[]) {

    try {

    IFileUtil file 
    = new FileUtilImpl();

    //LocateRegistry.createRegistry(1099); //加上此程序,就可以不要在控制臺(tái)上開啟RMI的注冊(cè)程序,1099是RMI服務(wù)監(jiān)視的默認(rèn)端口

    Naming.rebind(
    "rmi://127.0.0.1/FileUtilServer", file);

    System.out.print(
    "Ready");

    catch (Exception e) {

    System.out.println(
    "FileUtilServer: " + e.getMessage());

    e.printStackTrace();

    }
    }
    }

    聲明Naming.rebind("rmi://127.0.0.1/FileUtilServer", file中假定了RMI注冊(cè)工具(RMI registry )使用并啟動(dòng)了1099端口。如果在其他端口運(yùn)行了RMI注冊(cè)工具,則必須在這個(gè)聲明中定義。例如,如果RMI注冊(cè)工具在4500端口運(yùn)行,則聲明應(yīng)為:  Naming.rebind("rmi://127.0.0.1:4500/FileUtilServer", file

    另外我們已經(jīng)同時(shí)假定了我們的服務(wù)端和RMI注冊(cè)工具是運(yùn)行在同一臺(tái)機(jī)器上的。否則需要修改rebind方法中的地址。


    4.編寫客戶端 

    客戶端可以遠(yuǎn)程調(diào)用遠(yuǎn)程接口(FileInterface)中的任何一個(gè)方法。無論如何實(shí)現(xiàn),客戶端必須先從RMI注冊(cè)工具中獲取一個(gè)遠(yuǎn)程對(duì)象的引用。當(dāng)引用獲得后,方法downloadFile被調(diào)用。在執(zhí)行過程中,客戶端從命令行中獲得兩個(gè)參數(shù),第一個(gè)是要下載的文件名,第二個(gè)是要下載的機(jī)器的地址,在對(duì)應(yīng)地址的機(jī)器上運(yùn)行服務(wù)端。

    FileUtilClient.java 代碼如下:

    import java.io.BufferedOutputStream;

    import java.io.File;

    import java.io.FileOutputStream;

    import java.rmi.Naming;

    public class FileUtilClient {

    public static void main(String args[]) {

    if (args.length != 3) {

    System.out.println(
    "第一個(gè)參數(shù):RMI服務(wù)的IP地址");

    System.out.println(
    "第二個(gè)參數(shù):要下載的文件名");

    System.out.println(
    "第三個(gè)參數(shù):要文件保存位置");

    System.exit(
    0);

    }

    try {

    String name 
    = "rmi://" + args[0+ "/FileUtilServer";

    IFileUtil fileUtil 
    = (IFileUtil) Naming.lookup(name);

    byte[] filedata = fileUtil.downloadFile(args[1]);

    if(filedata==null){

    System.out.println(
    "Error:<filedata is null!>");

    System.exit(
    0);

    }

    File file 
    = new File(args[2]);

    System.out.println(
    "file.getAbsolutePath() = "+file.getAbsolutePath());

    BufferedOutputStream output 
    = new BufferedOutputStream(

    new FileOutputStream(file.getAbsolutePath()));

    output.write(filedata, 
    0, filedata.length);

    output.flush();

    output.close();

    System.out.println(
    "~~~~~End~~~~~");

    catch (Exception e) {

    System.err.println(
    "FileUtilServer exception: " + e.getMessage());

    e.printStackTrace();

    }

    }

    }


    5.運(yùn)行程序
    為了運(yùn)行程序,我們必須使用rmic來編譯生成stubsskeletons:

    >rmic FileUtilImpl

    這將會(huì)生成FileUtilImpl_Stub.classFileUtilImpl_Skel.class兩個(gè)文件。stub是客戶端的代理,而skeleton是服務(wù)端的框架。服務(wù)端和客戶端采用javac來編譯(如果服務(wù)端和客戶端在兩個(gè)不同的機(jī)器,則必須復(fù)制一個(gè)IFileUtil接口)。

    使用rmiregistry或者start rmiregistry 命令來運(yùn)行RMI注冊(cè)工具到window系統(tǒng)默認(rèn)的端口上:
    > rmiregistry portNumber
    此處的portNumber為端口,RMI注冊(cè)工具運(yùn)行之后,需要運(yùn)行服務(wù)FileUtilServer,因?yàn)?/font>RMI的安全機(jī)制將在服務(wù)端發(fā)生作用,所以必須增加一條安全策略: grant{permission java.security.AllPermission "", "";};

    為了運(yùn)行服務(wù)端,需要有除客戶類(FileUtilClient.class)之外所有的類文件。確認(rèn)安全策略在policy.txt文件之后,使用如下命令來運(yùn)行服務(wù)器。
    > java -Djava.security.policy=policy.txt FileUtilServer

    為了在其他的機(jī)器運(yùn)行客戶端程序,需要一個(gè)遠(yuǎn)程接口(IFileUtil.class)和一個(gè)stub(FileUtilImpl_Stub.class)。 使用如下命令運(yùn)行客戶端:
    > java FileUtilClient fileName machineName savePath
      這里fileName是要下載的文件名,machineName 是要下載的文件所在的機(jī)器(也是服務(wù)端所在的機(jī)器),savePath 是要將下載過來的文件保存的路徑(帶文件名)。如果全部通過的話,當(dāng)客戶端運(yùn)行后,則這個(gè)文件將被下載到本地。



    Spring對(duì)RMI的支持

    1.使用RMI暴露服務(wù)

    使用SpringRMI支持,你可以通過RMI基礎(chǔ)設(shè)施透明的暴露你的服務(wù)。設(shè)置好SpringRMI支持后,你會(huì)看到一個(gè)和遠(yuǎn)程EJB接口類似的配置,只是沒有對(duì)安全上下文傳遞和遠(yuǎn)程事務(wù)傳遞的標(biāo)準(zhǔn)支持。當(dāng)使用RMI調(diào)用器時(shí),Spring對(duì)這些額外的調(diào)用上下文提供了鉤子,你可以在此插入安全框架或者定制的安全證書。


    2. 使用 RmiServiceExporter 暴露服務(wù)

    使用 RmiServiceExporter,我們可以把AccountService對(duì)象的接口暴露成RMI對(duì)象。可以使用 RmiProxyFactoryBean 或者在傳統(tǒng)RMI服務(wù)中使用普通RMI來訪問該接口。RmiServiceExporter 顯式地支持使用RMI調(diào)用器暴露任何非RMI的服務(wù)。 

    當(dāng)然,我們首先需要在Spring BeanFactory中設(shè)置我們的服務(wù): 

    <bean id="accountService" class="example.AccountServiceImpl">

        
    <!-- any additional properties, maybe a DAO? -->

    </bean>

    然后,我們將使用 RmiServiceExporter 來暴露我們的服務(wù):

    <bean class="org.springframework.remoting.rmi.RmiServiceExporter">

    <!-- does not necessarily have to be the same name as the bean to be exported -->

    <property name="serviceName" value="AccountService"/>

    <property name="service" ref="accountService"/>

    <property name="serviceInterface" value="example.AccountService"/>

    <!-- defaults to 1099 -->

    <property name="registryPort" value="1199"/>

    </bean>

    正如你所見,我們覆蓋了RMI注冊(cè)的端口號(hào)。通常,你的應(yīng)用服務(wù)也會(huì)維護(hù)RMI注冊(cè),最好不要和它沖突。更進(jìn)一步來說,服務(wù)名是用來綁定下面的服務(wù)的。所以本例中,服務(wù)綁定在 rmi://HOST:1199/AccountService。在客戶端我們將使用這個(gè)URL來鏈接到服務(wù)。 

    注意:我們省略了一個(gè)屬性,就是 servicePort 屬性,它的默認(rèn)值為0。 這表示在服務(wù)通信時(shí)使用匿名端口。當(dāng)然如果你愿意的話,也可以指定一個(gè)不同的端口。 


    3. 在客戶端鏈接服務(wù)

    我們的客戶端是一個(gè)使用AccountService來管理account的簡單對(duì)象: 

    public class SimpleObject {

      
    private AccountService accountService;

      
    public void setAccountService(AccountService accountService) {

        
    this.accountService = accountService;

      }

    }

    為了把服務(wù)連接到客戶端上,我們將創(chuàng)建另一個(gè)單獨(dú)的bean工廠,它包含這個(gè)簡單對(duì)象和服務(wù)鏈接配置位: 

    <bean class="example.SimpleObject">

    <property name="accountService" ref="accountService"/>

    </bean>

    <bean id="accountService" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">

    <property name="serviceUrl" value="rmi://HOST:1199/AccountService"/>

    <property name="serviceInterface" value="example.AccountService"/>

    </bean>

    這就是我們?cè)诳蛻舳藶橹С诌h(yuǎn)程account服務(wù)所需要做的。Spring將透明的創(chuàng)建一個(gè)調(diào)用器并且通過RmiServiceExporter使得account服務(wù)支持遠(yuǎn)程服務(wù)。在客戶端,我們用RmiProxyFactoryBean連接它。


    Spring對(duì)RMI支持的實(shí)際應(yīng)用實(shí)例

    OMAS系統(tǒng)中提供給業(yè)務(wù)系統(tǒng)的RMI客戶反饋服務(wù)的實(shí)現(xiàn)服務(wù)暴露是通過Resource/modules/interfaces/spring-conf/serviceContext.xml配置文件實(shí)現(xiàn)的,而遠(yuǎn)程接口的實(shí)現(xiàn)類必須序列化(即實(shí)現(xiàn)Serializable接口)。

    Resource/modules/interfaces/spring-conf/serviceContext.xml的內(nèi)容如下:

    <?xml version="1.0" encoding="UTF-8"?>

    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">

    <beans default-autowire="byName" default-lazy-init="false">

    <!-- service實(shí)現(xiàn)類的配置 -->

    <bean id="fbWebService" class="com.ce.omas.interfaces.service.impl.FeedbackWebServiceImpl" />

    <bean class="org.springframework.remoting.rmi.RmiServiceExporter">

    <!-- does not necessarily have to be the same name as the bean to be exported -->

    <property name="serviceName" value="FeedbackRMIService" />

    <property name="service" ref="fbWebService" />

    <property name="serviceInterface" value="com.ce.omas.interfaces.service.IFeedbackWebService" />

    <!-- <property name="registryHost" value="rmi://192.168.100.7"/> -->

    <!-- defaults to 1099 -->

    <property name="registryPort" value="1199" />

    </bean>

    </beans>


    對(duì)應(yīng)的暴露的服務(wù)接口如下:

    public interface IFeedbackWebService {

    /**

     * <b>方法用途和描述:</b> 客戶反饋RMI服務(wù)端接口方法<br>

     * <b>方法的實(shí)現(xiàn)邏輯描述:</b> 通過RMI提供服務(wù),Spring支持的RMI遠(yuǎn)程調(diào)用

     * 
    @param systemID : 業(yè)務(wù)系統(tǒng)的唯一標(biāo)識(shí)

     * 
    @param feedbackType :用戶反饋的類型(1-系統(tǒng)BUG、2-系統(tǒng)易用性、3-客服人員態(tài)度、4-運(yùn)維人員態(tài)度、5-其他)

     * 
    @param feedbackContent :用戶反饋的正文內(nèi)容

     * 
    @return 反饋是否成功 true | false

     
    */

    public boolean setFeedback(String systemID, FeedbackType feedbackType,

    String feedbackContent) 
    throws OMASServiceException ;

    }


    暴露的服務(wù)接口實(shí)現(xiàn)如下:

    public class FeedbackWebServiceImpl implements IFeedbackWebService,  Serializable {

    private static Log _log = LogFactory.getLog(FeedbackWebServiceImpl.class);

    private static final long serialVersionUID = -5532505108644974594L;

    /**

     * 客戶反饋服務(wù)接口

     
    */

    private IFeedbackOperationService feedbackService;

    /**

    * 方法用途和描述: 注入運(yùn)營模塊的添加客戶反饋的服務(wù)

    @param feedbackWebService 運(yùn)營模塊服務(wù)

     
    */

    public void setFeedbackService(IFeedbackOperationService feedbackWebService) {

    _log.info(
    "注入運(yùn)營模塊的添加客戶反饋的服務(wù)");

    this.feedbackService = feedbackWebService;

    }

    /**

    * 方法用途和描述: 外部接口子系統(tǒng)中添加客戶反饋的方法

    @param systemID :業(yè)務(wù)系統(tǒng)ID

    @param feedbackType :反饋類型

    @param feedbackContent :反饋內(nèi)容

    @return 操作是否成功 ture or false

     * 
    @throws ServiceException 

     
    */

    public boolean setFeedback(String systemID, FeedbackType feedbackType, String feedbackContent) throws OMASServiceException {

    _log.info(
    "進(jìn)入到外部接口的添加客戶反饋的方法");

    if (BlankUtil.isBlank(systemID) || BlankUtil.isBlank(feedbackType)

    || BlankUtil.isBlank(feedbackContent)) {

    _log.error(
    "添加客戶反饋的接口參數(shù)為空!");

    throw new OMASServiceException("omas.interfaces.001");//添加客戶反饋的接口參數(shù)為空

    }

    WebServiceFeedbackVO vo 
    = new WebServiceFeedbackVO();

    vo.setFeedbackType(String.valueOf(feedbackType.getValue()));

    vo.setFeedbackContent(feedbackContent);

    vo.setSystemID(systemID);

    _log.info(
    "調(diào)用運(yùn)營子系統(tǒng)的添加客戶反饋的方法開始!");

    try {

    if (feedbackService == null) {

    _log.error(
    "運(yùn)營模塊服務(wù)為空");

    throw new OMASServiceException("omas.interfaces.002");//運(yùn)營模塊服務(wù)為空

    }

    feedbackService.addFeedbackOperation(vo);

    catch (ServiceException e) {

    _log.error(
    "調(diào)用運(yùn)營子系統(tǒng)的添加客戶反饋出現(xiàn)異常:"+e.getMessage());

    if(ExceptionConstants.EXCEPTION_OMAS_FEEDBACK_VO.equals(e.getMsgKey())){//客戶調(diào)用接口的對(duì)像為空

    throw new OMASServiceException("omas.interfaces.003");

    if(ExceptionConstants.EXCEPTION_OMAS_FEEDBACK_SYSTEMID.equals(e.getMsgKey())){//業(yè)務(wù)系統(tǒng)標(biāo)識(shí)ID為空

    throw new OMASServiceException("omas.omasservice.010");

    if(ExceptionConstants.EXCEPTION_OMAS_FEEDBACK_SIZE.equals(e.getMsgKey())){//非法的業(yè)務(wù)系統(tǒng)唯一標(biāo)識(shí)

    throw new OMASServiceException("omas.interfaces.004");

    if(ExceptionConstants.EXCEPTION_OMAS_FEEDBACK_BASE.equals(e.getMsgKey())){//數(shù)據(jù)庫訪問出了一點(diǎn)小問題!

    throw new OMASServiceException("omas.interfaces.005");

    }

    throw new OMASServiceException("omas.omasservice.000");//未捕獲到的異常信息!

    }

    return true;

    }

    }

    接口方法setFeedbackString, FeedbackType, String)的實(shí)現(xiàn)大家不用關(guān)心,其與RMI并無關(guān)系,只是一些純業(yè)務(wù)處理邏輯而已,要注意的是接口實(shí)現(xiàn)類必須實(shí)現(xiàn) IfeedbackWebServiceSerializable接口。

    在客戶本地的omasservice.jar包中客戶反饋的RMI客戶端的配置如下:

    Resource/config/omasrmi-client.xml

    <?xml version="1.0" encoding="UTF-8"?>

    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">

    <beans default-autowire="byName" default-lazy-init="true">

    <bean id="fbWebServiceProxy"

    class
    ="org.springframework.remoting.rmi.RmiProxyFactoryBean">

    <property name="serviceUrl">

    <value>rmi://127.0.0.1:1199/FeedbackRMIService</value>

    </property>

    <property name="serviceInterface">

    <value>com.ce.omas.interfaces.service.IFeedbackWebService</value>

    </property>

    </bean>

    <bean class="com.ce.omas.omasservice.service.impl.FeedbackRMIClientImpl">

    <property name="feedbackWebService" ref="fbWebServiceProxy" />

    </bean>

    </beans>


    客戶端調(diào)用RMI服務(wù)的方法如下所示:

    /**

    * 方法用途和描述: 客戶反饋:通過RMI方法與OMAS通訊

    * 方法的實(shí)現(xiàn)邏輯描述:

    @param feedbackType

    @param feedbackContent

    @return

    @throws OMASServiceException

    */

    public static boolean setFeedback_RMIClient(String systemID, FeedbackType feedbackType, String feedbackContent) throws OMASServiceException {

    if (systemID == null || "".equals(systemID)) {

    _log.error(
    "業(yè)務(wù)系統(tǒng)標(biāo)識(shí)<SystemID>為空或不是對(duì)象");

    throw new OMASServiceException("omas.omasservice.010");

    }

    String rmiClientConfigFilePath 
    = PropertyReader .getValue(ConfigConstants.OMASSERVICE_CONFIG_PATH, ConfigConstants.RMI_CLIENT_CONFIG_FILEPATH);

    if (rmiClientConfigFilePath == null || "".equals(rmiClientConfigFilePath)) {

    _log.error(
    "配置文件錯(cuò)誤:Key<rmiClientConfigFile>為空或不存在");

    throw new OMASServiceException("omas.omasservice.006");

    }

    _log.info(
    "rmiClientConfigPath = " + rmiClientConfigFilePath);

    ApplicationContext context 
    = null;

    try {

    context = new ClassPathXmlApplicationContext(rmiClientConfigFilePath);


    catch (Exception e) {

    _log.error(
    "客戶反饋:解析rmi-config.xml文件時(shí)出現(xiàn)異常:" + e);

    _log.info(
    "rmi-config.xml文件路徑:"+rmiClientConfigFilePath);

    throw new OMASServiceException("omas.omasservice.007");

    }

    IFeedbackWebService service 
    = null;

    try {

    service = (IFeedbackWebService) context .getBean("fbWebServiceProxy");


    catch (Exception e) {

    _log.error(
    "從Spring的RMI客戶端Bean配置文件獲取服務(wù)對(duì)象時(shí)出現(xiàn)異常:" + e);

    throw new OMASServiceException("omas.omasservice.009");

    }

    boolean bln = service.setFeedback(systemID, feedbackType, feedbackContent);

    _log.info(
    "反饋操作是否成功[true|false]:" + bln);

    return bln;

    }

    在此客戶端調(diào)用的程序中,你要關(guān)注的主要是以上背景色標(biāo)志為黃色的相關(guān)代碼。



    posted on 2010-05-07 00:32 瀟湘振宇 閱讀(42485) 評(píng)論(2)  編輯  收藏 所屬分類: 技術(shù)文摘

    Feedback

    # re: Java RMI 入門指南 2010-05-15 00:14 Johnny.Liang
    期待你的下一篇博文,特別是關(guān)于OSGi,緩存技術(shù)的博文  回復(fù)  更多評(píng)論
      

    # re: Java RMI 入門指南[未登錄] 2015-05-29 11:55 abc
    不錯(cuò)  回復(fù)  更多評(píng)論
      


    只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 在线免费观看h片| 全免费a级毛片免费看不卡| 亚洲AV无码乱码在线观看牲色| 亚洲成a人片77777老司机| 噜噜噜亚洲色成人网站| 2021久久精品免费观看| 日韩一卡2卡3卡4卡新区亚洲| 亚洲熟妇av午夜无码不卡| 久久久久国产精品免费看| 亚洲无码高清在线观看| 亚洲不卡影院午夜在线观看| 亚欧日韩毛片在线看免费网站| 亚洲国产精品视频| 亚洲午夜无码久久久久小说| 1000部禁片黄的免费看| 国产亚洲福利精品一区| 国产99久久久久久免费看| 日韩免费福利视频| 最新国产成人亚洲精品影院| 蜜桃成人无码区免费视频网站| 在线亚洲人成电影网站色www| 激情婷婷成人亚洲综合| 成人午夜性A级毛片免费| 亚洲视频免费在线播放| 日韩免费观看一区| 在线播放亚洲第一字幕| 色婷婷综合缴情综免费观看 | 亚洲毛片在线观看| 男女一边桶一边摸一边脱视频免费 | 日本二区免费一片黄2019| 亚洲ts人妖网站| 1000部禁片黄的免费看| 久久精品国产亚洲AV高清热| a在线观看免费网址大全| 亚洲中文字幕无码爆乳av中文 | 国产偷v国产偷v亚洲高清| 久久免费国产精品| 久久久青草青青国产亚洲免观 | 久久性生大片免费观看性| 亚洲综合色区在线观看| 全黄A免费一级毛片|