程序人生

          在Java中摸爬滾打的日子

            BlogJava :: 首頁(yè) :: 聯(lián)系 :: 聚合  :: 管理
            6 Posts :: 1 Stories :: 19 Comments :: 0 Trackbacks
              開(kāi)通博客也有好些天了,一直沒(méi)有時(shí)間靜下心來(lái)寫博文,今天我就把兩年前整理的一篇關(guān)于JAVA RMI入門級(jí)文章貼出來(lái),供有這方面需要的同學(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)大的開(kāi)發(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ì)象之間的無(wú)縫遠(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開(kāi)發(fā)的應(yīng)用系統(tǒng)可以部署在任何支持JREJava Run Environment Java,運(yùn)行環(huán)境)的平臺(tái)上。但由于JRMP是專為Java對(duì)象制定的,因此,RMI對(duì)于用非Java語(yǔ)言開(kāi)發(fā)的應(yīng)用系統(tǒng)的支持不足。不能與用非Java語(yǔ)言書寫的對(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ù)庫(kù)連接。RMI/JNIRMI/JDBC相結(jié)合,可幫助您利用RMI與目前使用非Java語(yǔ)言的現(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)傳 輸層,向上穿過(guò)遠(yuǎn)程調(diào)用層和骨干網(wǎng)(Skeleton),到達(dá)服務(wù)器對(duì)象。 占位程序扮演著遠(yuǎn)程服務(wù)器對(duì)象的代理的角色,使該對(duì)象可被客戶激活。 遠(yuǎn)程引用層處理語(yǔ)義、管理單一或多重對(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)程過(guò)程調(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)采用中性語(yǔ)言,所以是最普通的系統(tǒng)--它們不能提供所有可能的目標(biāo)平臺(tái)所具有的功能。 

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

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

          面向?qū)ο?/span>RMI可將完整的對(duì)象作為參數(shù)和返回值進(jìn)行傳遞,而不僅僅是預(yù)定義的數(shù)據(jù)類型。也就是說(shuō),您可以將類似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ì)方式無(wú)不依靠不同的屬性來(lái)發(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èn)這些服務(wù)程序的Java客戶程序的編寫工作變得輕松、簡(jiǎn)單。遠(yuǎn)程接口實(shí)際上就是Java接口。服務(wù)程序大約用三行指令宣布本身是服務(wù)程序,其它方面則與任何其它Java對(duì)象類似。這種簡(jiǎn)單方法便于快速編寫完整的分布式對(duì)象系統(tǒng)的服務(wù)程序,并快速地制做軟件的原型和早期版本,以便于進(jìn)行測(cè)試和評(píng)估。因?yàn)?font face="Courier New">RMI程序編寫簡(jiǎn)單,所以維護(hù)也簡(jiǎn)單。 

          可連接現(xiàn)有/原有的系統(tǒng):RMI可通過(guò)Java的本機(jī)方法接口JNI與現(xiàn)有系統(tǒng)進(jìn)行進(jìn)行交互。利用RMIJNI,您就能用Java語(yǔ)言編寫客戶端程序,還能使用現(xiàn)有的服務(wù)器端程序。在使用RMI/JNI與現(xiàn)有服務(wù)器連接時(shí),您可以有選擇地用Java重新編寫服務(wù)程序的任何部分,并使新的程序充分發(fā)揮Java的功能。類似地,RMI可利用JDBC、在不修改使用數(shù)據(jù)庫(kù)的現(xiàn)有非Java源代碼的前提下與現(xiàn)有關(guān)系數(shù)據(jù)庫(kù)進(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開(kāi)始就是Java平臺(tái)的核心部分,因此,它存在于任何一臺(tái)1.1 Java虛擬機(jī)中。所有RMI系統(tǒng)均采用相同的公開(kāi)協(xié)議,所以,所有Java 系統(tǒng)均可直接相互對(duì)話,而不必事先對(duì)協(xié)議進(jìn)行轉(zhuǎn)換。


          四、RMICORBA的關(guān)系

          RMI 和 CORBA 常被視為相互競(jìng)爭(zhēng)的技術(shù),因?yàn)閮烧叨继峁?duì)遠(yuǎn)程分布式對(duì)象的透明訪問(wèn)。但這兩種技術(shù)實(shí)際上是相互補(bǔ)充的,一者的長(zhǎng)處正好可以彌補(bǔ)另一者的短處。RMI 和 CORBA 的結(jié)合產(chǎn)生了 RMI-IIOPRMI-IIOP 是企業(yè)服務(wù)器端 Java 開(kāi)發(fā)的基礎(chǔ)。1997 年,IBM 和 Sun Microsystems啟動(dòng)了一項(xiàng)旨在促進(jìn) Java 作為企業(yè)開(kāi)發(fā)技術(shù)的發(fā)展的合作計(jì)劃。兩家公司特別著力于如何將 Java 用作服務(wù)器端語(yǔ)言,生成可以結(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問(wèn)世了,它幫助將 Java 語(yǔ)言推向了目前服務(wù)器端企業(yè)開(kāi)發(fā)的主流語(yǔ)言的領(lǐng)先地位。 
          (來(lái)源:sunmatrix.org.cn


          Java RMI 簡(jiǎn)單示例一

          以下用一個(gè)最簡(jiǎn)單的Hello的示例來(lái)介紹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();
              }
              
          /**
               * 說(shuō)明清楚此屬性的業(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è)程序開(kāi)始運(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ù)器就開(kāi)始工作了,把接口的實(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)上開(kā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)上開(kā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 簡(jiǎn)單示例二

          以下用一個(gè)文件交換程序來(lái)介紹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類是用來(lái)創(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){//限制文件大小不能超過(guò)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è)工具來(lái)注冊(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)上開(kā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è)方法。無(wú)論如何實(shí)現(xiàn),客戶端必須先從RMI注冊(cè)工具中獲取一個(gè)遠(yuǎn)程對(duì)象的引用。當(dāng)引用獲得后,方法downloadFile被調(diào)用。在執(zhí)行過(guò)程中,客戶端從命令行中獲得兩個(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來(lái)編譯生成stubsskeletons:

          >rmic FileUtilImpl

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

          使用rmiregistry或者start rmiregistry 命令來(lái)運(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文件之后,使用如下命令來(lái)運(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 是要將下載過(guò)來(lái)的文件保存的路徑(帶文件名)。如果全部通過(guò)的話,當(dāng)客戶端運(yùn)行后,則這個(gè)文件將被下載到本地。



          Spring對(duì)RMI的支持

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

          使用SpringRMI支持,你可以通過(guò)RMI基礎(chǔ)設(shè)施透明的暴露你的服務(wù)。設(shè)置好SpringRMI支持后,你會(huì)看到一個(gè)和遠(yuǎn)程EJB接口類似的配置,只是沒(méi)有對(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來(lái)訪問(wèn)該接口。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 來(lái)暴露我們的服務(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>

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

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


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

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

          public class SimpleObject {

            
          private AccountService accountService;

            
          public void setAccountService(AccountService accountService) {

              
          this.accountService = accountService;

            }

          }

          為了把服務(wù)連接到客戶端上,我們將創(chuàng)建另一個(gè)單獨(dú)的bean工廠,它包含這個(gè)簡(jiǎn)單對(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)用器并且通過(guò)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ù)暴露是通過(guò)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> 通過(guò)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)營(yíng)模塊的添加客戶反饋的服務(wù)

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

           
          */

          public void setFeedbackService(IFeedbackOperationService feedbackWebService) {

          _log.info(
          "注入運(yùn)營(yíng)模塊的添加客戶反饋的服務(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)營(yíng)子系統(tǒng)的添加客戶反饋的方法開(kāi)始!");

          try {

          if (feedbackService == null) {

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

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

          }

          feedbackService.addFeedbackOperation(vo);

          catch (ServiceException e) {

          _log.error(
          "調(diào)用運(yùn)營(yíng)子系統(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ù)庫(kù)訪問(wèn)出了一點(diǎn)小問(wèn)題!

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

          }

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

          }

          return true;

          }

          }

          接口方法setFeedbackString, FeedbackType, String)的實(shí)現(xiàn)大家不用關(guān)心,其與RMI并無(wú)關(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ù)的方法如下所示:

          /**

          * 方法用途和描述: 客戶反饋:通過(guò)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 瀟湘振宇 閱讀(42500) 評(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 入門指南[未登錄](méi) 2015-05-29 11:55 abc
          不錯(cuò)  回復(fù)  更多評(píng)論
            


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


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 隆昌县| 商河县| 临城县| 榆社县| 库尔勒市| 渭源县| 呈贡县| 昌图县| 县级市| 双鸭山市| 繁峙县| 丰原市| 星子县| 渑池县| 托里县| 保定市| 丹凤县| 堆龙德庆县| 虎林市| 高青县| 淮南市| 馆陶县| SHOW| 遵化市| 临漳县| 长寿区| 奉贤区| 洛浦县| 西平县| 苏尼特右旗| 望奎县| 布拖县| 四子王旗| 乐昌市| 翼城县| 汝州市| 会同县| 比如县| 崇礼县| 铁力市| 鲁山县|