天天看點

RMI介紹

     其實我近日有想法要請總壇主開設Java分布式應用版塊,專門讨論Java分布式應用。如Jini,CORBA,RMI,RMI-IIOP等。更進一步,還包括各種技術之間的通訊等問題。比如RMI-IIOP和CORBA通訊,Jini通路CORBA對象,EJB到CORBA映射等進階問題。不過暫時還沒有實作。是以姑且在此先讨論一下這些東西好了。

RMI是Sun基于Java技術推出的一種分布式應用方案。用戶端和伺服器端均要求使用Java技術編寫代碼。其特點就是簡單性。但這種技術的确很重要,因為Jini技術就是建立在RMI的基礎上才實作的。

CORBA是OMG組織建立的另外一種基于對象的分布式應用規範。實作了系統無關性(這和RMI類似)和語言無關性(這是和RMI最大的差別)并建立了IDL規範實作到各種語言的映射(包括C/C++,Java,COBOL,Ada,SmallTalk,Lisp,Python等衆多語言)結合ORB來實作通訊、且由于規範的規定導緻不同的ORB産品還可以實作互操作(比如Sun的ORB,Visibroker,Orbacus等)。但不同語言寫的ORB理論上可以互操作,但具體怎樣連Sun的文檔中也說不清,至少連他們也沒有測試過。另外還少了一點最重要的,就是ORB的通訊使用的協定是IIOP。

補充一點:為了彌補RMI的缺點(無法和CORBA對象互操作)Sun推出了RMI-IIOP技術,可以完全使用Java語言來定義接口(不是IDL),并擴充了JNDI,加入了CosNaming Service Provider。結合二者就可以實作RMI(這裡的RMI使用的可不是JRMP協定了,而是IIOP)和CORBA對象的互操作了。這是一種很偉大的技術,使用Java來定義遠端接口不僅僅意味着RMI可以使用IIOP協定,其它諸如EJB,Jini等均可以使用IIOP協定。Sun站點上的那篇EJB to CORBA mapping文章就是講述的使用C/C++寫的用戶端可以通路EJB伺服器,是不是很另人感動呢?

就說到這裡好了,至于具體細節很麻煩,不是片刻就可以說的完的。

在文章的開始,我先說明一下這個文章的來曆和翻譯它的初衷。一開始學習程式設計我就喜歡從具體的程式學起,從最初的HelloWorld,然後根據程式分析具體的文法,學習具體的文法,學習程式設計就是一個不斷的循環,但是最根本的就是寫程式,而不是一個勁的啃書,從寫程式渎程式中去體驗文法,去體驗一次一次的成功,剛開始接觸RMI的時候,很多參考書都是講很多很多原理,基礎,但是就是沒有一個簡單的可實作的系統提供給我們,看了那麼多還不知道怎麼做,這也是現在很多程式設計書的通病。後來在sun公司網站上找到了一個RMI教程,上邊就提供了詳細的方法建構一個簡單的RMI系統,看到這個我感到非常高興,我希望能夠和大家一起分享這個,讓剛開始接觸RMI和想學RMI的人能夠從中得到一點收獲,于是就翻譯了這個,這裡沒有全部翻譯,隻是翻譯了實作這個系統的詳細步驟。

RMI,遠端方法調用(Remote Method Invocation)是Enterprise JavaBeans的支柱,是建立分布式Java應用程式的友善途徑。RMI是非常容易使用的,但是它非常的強大。

      RMI的基礎是接口,RMI構架基于一個重要的原理:定義接口和定義接口的具體實作是分開的。下面我們通過具體的例子,建立一個簡單的遠端計算服務和使用它的客戶程式

一個正常工作的RMI系統由下面幾個部分組成:

●     遠端服務的接口定義

●     遠端服務接口的具體實作

●     樁(Stub)和架構(Skeleton)檔案

●     一個運作遠端服務的伺服器

●     一個RMI命名服務,它允許用戶端去發現這個遠端服務

●     類檔案的提供者(一個HTTP或者FTP伺服器)

●     一個需要這個遠端服務的用戶端程式

下面我們一步一步建立一個簡單的RMI系統。首先在你的機器裡建立一個新的檔案夾,以便放置我們建立的檔案,為了簡單起見,我們隻使用一個檔案夾存放用戶端和服務端代碼,并且在同一個目錄下運作服務端和用戶端。

如果所有的RMI檔案都已經設計好了,那麼你需要下面的幾個步驟去生成你的系統:

1、  編寫并且編譯接口的Java代碼

2、  編寫并且編譯接口實作的Java代碼

3、  從接口實作類中生成樁(Stub)和架構(Skeleton)類檔案

4、  編寫遠端服務的主運作程式

5、  編寫RMI的用戶端程式

6、  安裝并且運作RMI系統

1、  接口

第一步就是建立和編譯服務接口的Java代碼。這個接口定義了所有的提供遠端服務的功能,下面是源程式:

//Calculator.java

//define the interface

import java.rmi.Remote;

public interface Calculator extends Remote

{

   public long add(long a, long b)

       throws java.rmi.RemoteException;

   public long sub(long a, long b)

       throws java.rmi.RemoteException;

   public long mul(long a, long b)

       throws java.rmi.RemoteException;

   public long div(long a, long b)

       throws java.rmi.RemoteException;

}

注意,這個接口繼承自Remote,每一個定義的方法都必須抛出一個RemoteException異常對象。

建立這個檔案,把它存放在剛才的目錄下,并且編譯。

>javac Calculator.java

2、  接口的具體實作

下一步,我們就要寫遠端服務的具體實作,這是一個CalculatorImpl類檔案:

//CalculatorImpl.java

//Implementation

import java.rmi.server.UnicastRemoteObject

public class CalculatorImpl extends UnicastRemoteObject implements Calculator

{

   // 這個實作必須有一個顯式的構造函數,并且要抛出一個RemoteException異常

   public CalculatorImpl()

       throws java.rmi.RemoteException {

       super();

   }

   public long add(long a, long b)

       throws java.rmi.RemoteException {

       return a + b;

   }

   public long sub(long a, long b)

       throws java.rmi.RemoteException {

       return a - b;

   }

   public long mul(long a, long b)

       throws java.rmi.RemoteException {

       return a * b;

   }

   public long div(long a, long b)

       throws java.rmi.RemoteException {

       return a / b;

   }

}

      同樣的,把這個檔案儲存在你的目錄裡然後編譯他。

      這個實作類使用了UnicastRemoteObject去聯接RMI系統。在我們的例子中,我們是直接的從UnicastRemoteObject這個類上繼承的,事實上并不一定要這樣做,如果一個類不是從UnicastRmeoteObject上繼承,那必須使用它的exportObject()方法去聯接到RMI。

      如果一個類繼承自UnicastRemoteObject,那麼它必須提供一個構造函數并且聲明抛出一個RemoteException對象。當這個構造函數調用了super(),它久激活UnicastRemoteObject中的代碼完成RMI的連接配接和遠端對象的初始化。

3、  樁(Stubs)和架構(Skeletons)

下一步就是要使用RMI編譯器rmic來生成樁和架構檔案,這個編譯運作在遠端服務實作類檔案上。

>rmic CalculatorImpl

在你的目錄下運作上面的指令,成功執行完上面的指令你可以發現一個Calculator_stub.class檔案,如果你是使用的Java2SDK,那麼你還可以發現Calculator_Skel.class檔案。

4、  主機伺服器

遠端RMI服務必須是在一個伺服器中運作的。CalculatorServer類是一個非常簡單的伺服器。

//CalculatorServer.java

import java.rmi.Naming;

public class CalculatorServer {

  public CalculatorServer() {

    try {

      Calculator c = new CalculatorImpl();

      Naming.rebind("rmi://localhost:1099/CalculatorService", c);

    } catch (Exception e) {

      System.out.println("Trouble: " + e);

    }

  }

  public static void main(String args[]) {

    new CalculatorServer();

  }

}

      建立這個伺服器程式,然後儲存到你的目錄下,并且編譯它。

5、  用戶端

用戶端源代碼如下:

//CalculatorClient.java

import java.rmi.Naming;

import java.rmi.RemoteException;

import java.net.MalformedURLException;

import java.rmi.NotBoundException;

public class CalculatorClient {

   public static void main(String[] args) {

       try {

           Calculator c = (Calculator)

                          Naming.lookup(

                "rmi://localhost

                       /CalculatorService");

           System.out.println( c.sub(4, 3) );

           System.out.println( c.add(4, 5) );

           System.out.println( c.mul(3, 6) );

           System.out.println( c.div(9, 3) );

       }

       catch (MalformedURLException murle) {

           System.out.println();

           System.out.println(

             "MalformedURLException");

           System.out.println(murle);

       }

       catch (RemoteException re) {

           System.out.println();

           System.out.println(

                       "RemoteException");

           System.out.println(re);

       }

       catch (NotBoundException nbe) {

           System.out.println();

           System.out.println(

                      "NotBoundException");

           System.out.println(nbe);

       }

       catch (

           java.lang.ArithmeticException

                                     ae) {

           System.out.println();

           System.out.println(

            "java.lang.ArithmeticException");

           System.out.println(ae);

       }

   }

}

      儲存這個用戶端程式到你的目錄下(注意這個目錄是一開始建立那個,所有的我們的檔案都在那個目錄下),并且編譯他。

6、  運作RMI系統

現在我們建立了所有運作這個簡單RMI系統所需的檔案,現在我們終于可以運作這個RMI系統啦!來享受吧。

我們是在指令控制台下運作這個系統的,你必須開啟三個控制台視窗,一個運作伺服器,一個運作用戶端,還有一個運作RMIRegistry。

首先運作注冊程式RMIRegistry,你必須在包含你剛寫的類的那麼目錄下運作這個注冊程式。

>rmiregistry

好,這個指令成功的話,注冊程式已經開始運作了,不要管他,現在切換到另外一個控制台,在第二個控制台裡,我們運作伺服器CalculatorService,輸入如下指令:

>java CalculatorServer

這個伺服器就開始工作了,把接口的實作加載到記憶體等待用戶端的聯接。好現在切換到第三個控制台,啟動我們的用戶端。

>java CalculatorClient

如果所有的這些都成功運作,你應該看到下面的輸出:

1

9

18

3

如果你看到了上面的輸出,恭喜你,你成功了,你已經成功的建立了一個RMI系統,并且使他正确工作了。即使你運作在同一個計算機上,RMI還是使用了你的網絡堆棧和TCP/IP去進行通訊,并且是運作在三個不同的Java虛拟機上。這已經是一個完整的RMI系統。

投票評分

特别好

挺不錯

一般性

有點差

太差了

表單的底部

投票評分

特别好

挺不錯

一般性

有點差

太差了

表單的底部

     JavaRMI入門實戰

關鍵字:Java RMI

作者:renrzg

為通過網絡執行其他機器上的代碼,傳統的方法不僅難以學習,而且易出錯。解決這個問題的最佳方法是:某些對象正好位于另一台機器,我們可以發送一條消息,并獲得傳回結果,就像位于自己的本機器一樣。Java遠端方法調用(RMI)特性使客戶機上運作的程式可以調用遠端伺服器上的對象。遠端方法調用特性使Java程式設計人員能夠在網絡環境中分布操作。

下面介紹一下必要的步驟,建立自己的RMI對象。

一、遠端接口概念:

    RMI對接口有着強烈的依賴。在需要建立一個遠端對象的時候,我們通過傳遞一個接口來隐藏基層的實施細節。是以客戶得到遠端對象的一個句柄正好同一些本地的根代碼連接配接,有後者負責通過網絡通信。但我們并不關心這些事情,通過自己的接口句柄發送消息即可。

  建立一個遠端接口時,必須遵守下列規則:

1)  遠端接口必須為public屬性(不能有“包通路”;也就是說,他不能是“友好的”)。否則,一旦客戶試圖裝載一個實作了遠端接口的遠端對象,就會得到一個錯誤。

2)  遠端接口必須擴充接口java.rmi.Remote。

3)  除與應用程式本身有關的違例,遠端接口中的每個方法都必須在自己的throws從句中聲明java.rmi.RemoteException.

4)  作為參數或傳回值傳遞的一個遠端對象(不管是直接,還是本地對象中嵌入)必須聲明為遠端接口,不可聲明為實施類。

下面是一個遠端接口示例,

//PerfectTimeI.java

//The PerfectTime remote interface

package test;

import java.rmi.*;

public interface PerfectTimeI extends Remote {

long getPerfectTime() throws RemoteException;

}

它表面上與其他的接口類似,隻是對Remote進行了擴充,而且所有的方法都會“擲”出RemoteException.接口和方法都是Public的。

編譯PerfectTimeI.java,生成PerfectTimeI.class(test是包,編譯時注意路徑)

G:/RMI>javac test/PerfectTimeI.java

二、遠端接口的實施:

   伺服器必須包含一個擴充了UnicastRemoteObject類,并實作遠端接口。這個類也可以含有附加的方法,但客戶隻能使用遠端接口中的方法。因為客戶是指向接口的一個句柄,而不是它的哪個類。

  必須為遠端對象定義構件器,即使隻準備定義一個預設構件器,用它調用基礎類構件器。必須把它明确地編寫出來,因為它必須“擲”出RemoteException違例。

 下面列出遠端接口PerfectTime的事實過程:他代表精确計時服務

//PerfectTime.java

//The implementation of the PerfectTime remote object

package test;

import java.net.*;

import java.rmi.*;

import java.rmi.registry.*;

import java.rmi.server.*;

public class PerfectTime extends UnicastRemoteObject implements PerfectTimeI

{

//預設構件器,也要“擲”出RemoteException違例。

public PerfectTime() throws RemoteException {

super();

}

public long getPerfectTime() throws RemoteException {

return System.currentTimeMillis();

}

public static void main(String[] args) {

//System.setSecurityManager(new RMISecurityManager());

try {

PerfectTime pt = new PerfectTime();

Naming.rebind("PerfectTime", pt);

System.out.println("Ready to do Time");

} catch (Exception e) {

e.printStackTrace();

}

}

}

編譯PerfectTime.java,生成PerfectTime.class(test是包,編譯時注意路徑)

G:/RMI>javac test/PerfectTime.java

三、建立根和幹:

 建立RemoteObject的主幹和架構。要完成這個工作可使用rmic編譯器,rmic編譯器生成遠端對象的存根和骨架。存根(Stub)是遠端對象在用戶端的代理,它将RMI調用傳遞給伺服器端的骨架(Skeleton),後者負責将該調用傳遞給實際的遠端方法輸入如下:

G:/RMI>rmic -d G:/RMI test.PerfectTime

執行這個指令,

若rmic成功運作,test目錄裡就會多出兩個新類:

PerfectTime_Stub.class

PerfectTime_Skel.class

它們分别對應的是根(stub)和幹(skeleton).

四、使用遠端對象:

RMI全部的宗旨就是可能簡化遠端接口對象的使用。我們客戶程式中要做的唯一一件額外事情是查找從伺服器取回遠端接口。下面就是編寫的Java程式:将消息發給對象:

//DisplayPerfectTime.java

//Users remote object PerfectTime

package test;

import java.rmi.*;

import java.rmi.registry.*;

public class DisplayPerfectTime {

public DisplayPerfectTime() {

super();

}

public static void main(String[] args) {

//System.setSecurityManager(new RMISecurityManager());

try {

PerfectTimeI t = (PerfectTimeI) Naming.lookup("PerfectTime");

for (int i = 0; i < 10; i++) {

System.out.println("PerfectTime:" + t.getPerfectTime());

}

} catch (Exception e) {

e.printStackTrace();

}

}

}

編譯DisplayPerfectTime.java.

G:/RMI>javac test/DisplayPerfectTime.java

五、啟動注冊并運作代碼:

在運作PerfectTime類和DisplayPectTime類之前,使用者必須首先在将要宿主PerfectTime的計算機上啟動RMI注冊(Registry)程式,即使将要運作PerfectTime的計算機與運作DisplayPerfectTime的是同一台機器,這一步也是必須的。系統資料庫伺服器的名字是rmiregistry.在32位Windows環境中,可使用: start rmiregistry 令其在背景運作。然後分别開兩個不同的程序運作Server端和Client端:啟動系統資料庫伺服器:

G:/RMI>start rmiregistry

綁定PerfectTime到注冊,運作服務端程式:在Windows下,輸入下列指令,在背景啟動PerfectTime程式:

G:/RMI>java test.PerfectTime

Ready to do Time

運作用戶端程式:如下

G:/RMI>java test.DisplayPerfectTime

PerfectTime:961722589649

PerfectTime:961722589669

PerfectTime:961722589679

PerfectTime:961722589679

PerfectTime:961722589689

PerfectTime:961722589689

PerfectTime:961722589689

PerfectTime:961722589699

PerfectTime:961722589699

PerfectTime:961722589699

Java CORBA入門!

作者:jdeveloper

Below is a simple example of a CORBA program      
download the source file       
1. produce a idl file like this      
   hello.idl      
   module HelloApp {      
     interface Hello    {               
         string sayHello();      
    };      
  };      
2. produce stub and skeleton files through idltojava.exe      
   idltojava hello.idl      
   idltojava is now named as idlj.exe and is included in the JDK.       
3. write a server program like this       
// HelloServer.java       
import HelloApp.*;      
import org.omg.CosNaming.*;      
import org.omg.CosNaming.NamingContextPackage.*;      
import org.omg.CORBA.*;      
import java.io.*;      
class HelloServant extends _HelloImplBase       
{      
    public String sayHello()      
    {      
       return "/nHello world !!/n";       
    }         
}      
public class HelloServer {      
    public static void main(String args[])      
    {      
         try{      
             // create and initialize the ORB      
             ORB orb = ORB.init(args, null);      
             // create servant and register it with the ORB      
             HelloServant helloRef = new HelloServant();      
             orb.connect(helloRef);      
             // get the root naming context      
             org.omg.CORBA.Object objRef =       
                 orb.resolve_initial_references("NameService");      
             NamingContext ncRef = NamingContextHelper.narrow(objRef);      
             // bind the Object Reference in Naming      
             NameComponent nc = new NameComponent("Hello", "");      
             NameComponent path[] = {nc};      
             ncRef.rebind(path, helloRef);      
             // wait for invocations from clients      
            java.lang.Object sync = new java.lang.Object();      
            synchronized (sync) {      
                sync.wait();      
            }      
         } catch (Exception e) {      
             System.err.println("ERROR: " + e);      
             e.printStackTrace(System.out);      
         }      
    }      
}          
4. write a client program like this      
// HelloClient.java      
import HelloApp.*;      
import org.omg.CosNaming.*;      
import org.omg.CORBA.*;      
public class HelloClient       
{      
    public static void main(String args[])      
    {      
         try{      
             // create and initialize the ORB      
             ORB orb = ORB.init(args, null);      
            // get the root naming context      
            org.omg.CORBA.Object objRef =       
                 orb.resolve_initial_references("NameService");      
            NamingContext ncRef = NamingContextHelper.narrow(objRef);      
            // test      
            System.out.println("OK..");                      
            // resolve the Object Reference in Naming      
            NameComponent nc = new NameComponent("Hello", "");      
            NameComponent path[] = {nc};      
            Hello helloRef = HelloHelper.narrow(ncRef.resolve(path));      
             // call the Hello server object and print results      
           //String oldhello = helloRef.lastMessage();      
           //System.out.println(oldhello);      
             String Hello = helloRef.sayHello();      
             System.out.println(Hello);      
         } catch (Exception e) {      
             System.out.println("ERROR : " + e) ;      
             e.printStackTrace(System.out);      
         }      
    }      
}      
5. complie these files      
   javac *.java HelloApp/*.java      
6. run the application      
  a. first you've to run the Name Service prior to the others likethis      
     c:/>tnameserv      
  b. run server      
     c:/>java HelloServer      
  c. run client       
     c:/>java HelloClient