RMI 相關知識
RMI全稱是Remote Method Invocation-遠程方法調用,Java RMI在JDK1.1中實現的,其威力就體現在它強大的開發分布式網絡應用的能力上,是純Java的網絡分布式應用系統的核心解決方案之一。其實它可以被看作是RPC的Java版本。但是傳統RPC并不能很好地應用于分布式對象系統。而Java RMI 則支持存儲于不同地址空間的程序級對象之間彼此進行通信,實現遠程對象之間的無縫遠程調用。
RMI目前使用Java遠程消息交換協議JRMP(Java Remote Messaging Protocol)進行通信。由于JRMP是專為Java對象制定的,Java RMI具有Java的"Write Once,Run Anywhere"的優點,是分布式應用系統的百分之百純Java解決方案。用Java RMI開發的應用系統可以部署在任何支持JRE(Java Run Environment Java,運行環境)的平臺上。但由于JRMP是專為Java對象制定的,因此,RMI對于用非Java語言開發的應用系統的支持不足。不能與用非Java語言書寫的對象進行通信。
RMI可利用標準Java本機方法接口JNI與現有的和原有的系統相連接。RMI還可利用標準JDBC包與現有的關系數據庫連接。RMI/JNI和RMI/JDBC相結合,可幫助您利用RMI與目前使用非Java語言的現有服務器進行通信,而且在您需要時可擴展Java在這些服務器上的使用。RMI可幫助您在擴展使用時充分利用Java的強大功能。
一、RMI(遠程方法調用)的組成
一個正常工作的RMI系統由下面幾個部分組成:
·遠程服務的接口定義
·遠程服務接口的具體實現
·樁(Stub)和框架(Skeleton)文件
·一個運行遠程服務的服務器
·一個RMI命名服務,它允許客戶端去發現這個遠程服務
·類文件的提供者(一個HTTP或者FTP服務器)
·一個需要這個遠程服務的客戶端程序
二、RMI(遠程方法調用)原理示意圖
方法調用從客戶對象經占位程序(Stub)、遠程引用層(Remote Reference Layer)和傳輸層(Transport Layer)向下,傳遞給主機,然后再次經傳 輸層,向上穿過遠程調用層和骨干網(Skeleton), 到達服務器對象。 占位程序扮演著遠程服務器對象的代理的角色,使該對象可被客戶激活。 遠程引用層處理語義、管理單一或多重對象的通信,決定調用是應發 往一個服務器還是多個。傳輸層管理實際的連接,并且追蹤可以接受方法調用的遠程對象。服務器端的骨干網完成對服務器對象實際的方法調用,并獲取返回值。返 回值向下經遠程引用層、服務器端的傳輸層傳遞回客戶端,再向上經傳輸層和遠程調用層返回。最后,占位程序獲得返回值。
要完成以上步驟需要有以下幾個步驟:
1、 生成一個遠程接口
2、 實現遠程對象(服務器端程序)
3、 生成占位程序和骨干網(服務器端程序)
4、 編寫服務器程序
5、 編寫客戶程序
6、 注冊遠程對象
7、 啟動遠程對象
三、RMI(遠程方法調用)的優點
從最基本的角度看,RMI是Java的遠程過程調用(RPC)機制。與傳統的RPC系統相比,RMI具有若干優點,因為它是Java面向對象方法的一部分。傳統的RPC系統采用中性語言,所以是最普通的系統--它們不能提供所有可能的目標平臺所具有的功能。
RMI以Java為核心,可與采用本機方法與現有系統相連接。這就是說,RMI可采用自然、直接和功能全面的方式為您提供分布式計算技術,而這種技術可幫助您以不斷遞增和無縫的方式為整個系統添加Java功能。
RMI的主要優點如下:
面向對象:RMI可將完整的對象作為參數和返回值進行傳遞,而不僅僅是預定義的數據類型。也就是說,您可以將類似Java哈希表這樣的復雜類型作為一個參數進行傳遞。而在目前的RPC系統中,您只能依靠客戶機將此類對象分解成基本數據類型,然后傳遞這些數據類型,最后在服務器端重新創建哈希表。RMI則不需額外的客戶程序代碼(將對象分解成基本數據類型),直接跨網傳遞對象。
可移動屬性:RMI可將屬性(類實現程序)從客戶機移動到服務器,或者從服務器移到客戶機。這樣就能具備最大的靈活性,因為政策改變時只需要您編寫一個新的Java類,并將其在服務器主機上安裝一次即可。
設計方式:對象傳遞功能使您可以在分布式計算中充分利用面向對象技術的強大功能,如二層和三層結構系統。如果您能夠傳遞屬性,那么您就可以在您的解決方案中使用面向對象的設計方式。所有面向對象的設計方式無不依靠不同的屬性來發揮功能,如果不能傳遞完整的對象--包括實現和類型--就會失去設計方式上所提供的優點。
安 全:RMI使用Java內置的安全機制保證下載執行程序時用戶系統的安全。RMI使用專門為保護系統免遭惡意小應用程序侵害而設計的安全管理程序,可保護您的系統和網絡免遭潛在的惡意下載程序的破壞。在情況嚴重時,服務器可拒絕下載任何執行程序。
便于編寫和使用:RMI使得Java遠程服務程序和訪問這些服務程序的Java客戶程序的編寫工作變得輕松、簡單。遠程接口實際上就是Java接口。服務程序大約用三行指令宣布本身是服務程序,其它方面則與任何其它Java對象類似。這種簡單方法便于快速編寫完整的分布式對象系統的服務程序,并快速地制做軟件的原型和早期版本,以便于進行測試和評估。因為RMI程序編寫簡單,所以維護也簡單。
可連接現有/原有的系統:RMI可通過Java的本機方法接口JNI與現有系統進行進行交互。利用RMI和JNI,您就能用Java語言編寫客戶端程序,還能使用現有的服務器端程序。在使用RMI/JNI與現有服務器連接時,您可以有選擇地用Java重新編寫服務程序的任何部分,并使新的程序充分發揮Java的功能。類似地,RMI可利用JDBC、在不修改使用數據庫的現有非Java源代碼的前提下與現有關系數據庫進行交互。
編寫一次,到處運行:RMI是Java“編寫一次,到處運行 ”方法的一部分。任何基于RMI的系統均可100%地移植到任何Java虛擬機上,RMI/JDBC系統也不例外。如果使用RMI/JNI與現有系統進行交互工作,則采用JNI編寫的代碼可與任何Java虛擬機進行編譯、運行。
分布式垃圾收集:RMI采用其分布式垃圾收集功能收集不再被網絡中任何客戶程序所引用的遠程服務對象。與Java 虛擬機內部的垃圾收集類似,分布式垃圾收集功能允許用戶根據自己的需要定義服務器對象,并且明確這些對象在不再被客戶機引用時會被刪除。
并行計算:RMI采用多線程處理方法,可使您的服務器利用這些Java線程更好地并行處理客戶端的請求。Java分布式計算解決方案:RMI從JDK 1.1開始就是Java平臺的核心部分,因此,它存在于任何一臺1.1 Java虛擬機中。所有RMI系統均采用相同的公開協議,所以,所有Java 系統均可直接相互對話,而不必事先對協議進行轉換。
四、RMI與CORBA的關系
RMI 和 CORBA 常被視為相互競爭的技術,因為兩者都提供對遠程分布式對象的透明訪問。但這兩種技術實際上是相互補充的,一者的長處正好可以彌補另一者的短處。RMI 和 CORBA 的結合產生了 RMI-IIOP,RMI-IIOP 是企業服務器端 Java 開發的基礎。1997 年,IBM 和 Sun Microsystems啟動了一項旨在促進 Java 作為企業開發技術的發展的合作計劃。兩家公司特別著力于如何將 Java 用作服務器端語言,生成可以結合進現有體系結構的企業級代碼。所需要的就是一種遠程傳輸技術,它兼有 Java 的 RMI(Remote Method Invocation,遠程方法調用)較少的資源占用量和更成熟的 CORBA(Common Object Request Broker Architecture,公共對象請求代理體系結構)技術的健壯性。出于這一需要,RMI-IIOP問世了,它幫助將 Java 語言推向了目前服務器端企業開發的主流語言的領先地位。
(來源:sun;matrix.org.cn)
Java RMI 簡單示例一
以下用一個最簡單的Hello的示例來介紹RMI的應用。
1:定義一個遠程接口
IHello.java代碼如下:
public interface IHello extends Remote {
public String sayHello(String name) throws java.rmi.RemoteException;
}
2:實現遠程的接口(服務端就在此遠程接口的實現類中)
HelloImpl.java代碼如下:
import java.rmi.server.UnicastRemoteObject;
public class HelloImpl extends UnicastRemoteObject implements IHello {
// 這個實現必須有一個顯式的構造函數,并且要拋出一個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();
java.rmi.Naming.rebind("rmi://localhost:1099/hello", hello);
System.out.print("Ready


} catch (Exception e) {
e.printStackTrace();
}
}
}
3:新建RMI客戶端調用程序
Hello_RMI_Client.java代碼如下:
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:編譯并運行
4.1 用javac命令編譯IHello.java、HelloImpl.java、Hello_RMI_Client.java
>javac *.java
4.2 用rmic命令生成樁和框架文件
>rmic HelloImpl
成功執行完上面的命令可以發現生成一個HelloImpl_stub.class文件,如果JDK是使用Java2SDK,那么還可以發現多出一個HelloImpl_Skel.class文件。如果服務端程序與客戶端程序在同一臺機器上并在同一目錄中,則可以省略掉接口實現類生成的樁和框架文件,但這就失去了使用RMI的意義,而如果要在不同的JVM上運行時,客戶端程序就必須得依靠服務端運程方法實現的樁和框架文件以及接口類。
4.3 運行注冊程序RMIRegistry,必須在包含剛寫的類的目錄下運行這個注冊程序。
>rmiregistry
注冊程序開始運行了,不要管他,現在切換到另外一個控制臺運行服務器
4.4 運行服務器HelloImpl
>java HelloImpl
當啟動成功出現Ready...... 這個服務器就開始工作了,把接口的實現加載到內存等待客戶端的聯接。現在切換到第三個控制臺,啟動我們的客戶端。
4.5 啟動客戶端:為了在其他的機器運行客戶端程序你需要一個遠程接口(IHello.class) 和一個stub(HelloImpl_Stub.class)。 使用如下命令運行客戶端
>java Hello_RMI_Client
當運行成功會在控制臺打印:Hello zhangxianxin ^_^
備注:如果不想在控制臺上開啟RMI注冊程序RMIRegistry的話,可在RMI服務類程序中添加LocateRegistry.createRegistry(1099); 如下所示:
修改后的HelloImpl.java代碼如下:
import java.rmi.registry.LocateRegistry;
import java.rmi.server.UnicastRemoteObject;
public class HelloImpl extends UnicastRemoteObject implements IHello {
// 這個實現必須有一個顯式的構造函數,并且要拋出一個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); //加上此程序,就可以不要在控制臺上開啟RMI的注冊程序,1099是RMI服務監視的默認端口
java.rmi.Naming.rebind("rmi://localhost:1099/hello", hello);
System.out.print("Ready


} catch (Exception e) {
e.printStackTrace();
}
}
}
Java RMI 簡單示例二
以下用一個文件交換程序來介紹RMI的應用。這個應用允許客戶端從服務端交換(或下載)所有類型的文件。第一步是定義一個遠程的接口,這個接口指定的簽名方法將被服務端提供和被客戶端調用。
1.定義一個遠程接口
IFileUtil.java代碼如下:
import java.rmi.RemoteException;
public interface IFileUtil extends Remote {
public byte[] downloadFile(String fileName) throws RemoteException;
}
接口IFileDownload提供了一個downloadFile方法,然后返回一個相應的文件數據。
2.實現遠程的接口
類FileImpl繼承于UnicastRemoteObject類。這顯示出FileImpl類是用來創建一個單獨的、不能復制的、遠程的對象,這個對象使用RMI默認的基于TCP的通信方式。
FileUtilImpl.java代碼如下:
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,文件太大可能導制內存溢出!
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.編寫服務端
(1)創建并安裝一個RMISecurityManager實例。
(2)創建一個遠程對象的實例。
(3)使用RMI注冊工具來注冊這個對象。
FileUtilServer.java 代碼如下:
import java.rmi.RMISecurityManager;
public class FileUtilServer {
public static void main(String argv[]) {
try {
IFileUtil file = new FileUtilImpl();
//LocateRegistry.createRegistry(1099); //加上此程序,就可以不要在控制臺上開啟RMI的注冊程序,1099是RMI服務監視的默認端口
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注冊工具(RMI registry )使用并啟動了1099端口。如果在其他端口運行了RMI注冊工具,則必須在這個聲明中定義。例如,如果RMI注冊工具在4500端口運行,則聲明應為: Naming.rebind("rmi://127.0.0.1:4500/FileUtilServer", file)
另外我們已經同時假定了我們的服務端和RMI注冊工具是運行在同一臺機器上的。否則需要修改rebind方法中的地址。
4.編寫客戶端
客戶端可以遠程調用遠程接口(FileInterface)中的任何一個方法。無論如何實現,客戶端必須先從RMI注冊工具中獲取一個遠程對象的引用。當引用獲得后,方法downloadFile被調用。在執行過程中,客戶端從命令行中獲得兩個參數,第一個是要下載的文件名,第二個是要下載的機器的地址,在對應地址的機器上運行服務端。
FileUtilClient.java 代碼如下:
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("第一個參數:RMI服務的IP地址");
System.out.println("第二個參數:要下載的文件名");
System.out.println("第三個參數:要文件保存位置");
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.運行程序
為了運行程序,我們必須使用rmic來編譯生成stubs和skeletons:
>rmic FileUtilImpl
這將會生成FileUtilImpl_Stub.class和FileUtilImpl_Skel.class兩個文件。stub是客戶端的代理,而skeleton是服務端的框架。服務端和客戶端采用javac來編譯(如果服務端和客戶端在兩個不同的機器,則必須復制一個IFileUtil接口)。
使用rmiregistry或者start rmiregistry 命令來運行RMI注冊工具到window系統默認的端口上:
> rmiregistry portNumber
此處的portNumber為端口,RMI注冊工具運行之后,需要運行服務FileUtilServer,因為RMI的安全機制將在服務端發生作用,所以必須增加一條安全策略: grant{permission java.security.AllPermission "", "";};
為了運行服務端,需要有除客戶類(FileUtilClient.class)之外所有的類文件。確認安全策略在policy.txt文件之后,使用如下命令來運行服務器。
> java -Djava.security.policy=policy.txt FileUtilServer
為了在其他的機器運行客戶端程序,需要一個遠程接口(IFileUtil.class)和一個stub(FileUtilImpl_Stub.class)。 使用如下命令運行客戶端:
> java FileUtilClient fileName machineName savePath
這里fileName是要下載的文件名,machineName 是要下載的文件所在的機器(也是服務端所在的機器),savePath 是要將下載過來的文件保存的路徑(帶文件名)。如果全部通過的話,當客戶端運行后,則這個文件將被下載到本地。
Spring對RMI的支持
1.使用RMI暴露服務
使用Spring的RMI支持,你可以通過RMI基礎設施透明的暴露你的服務。設置好Spring的RMI支持后,你會看到一個和遠程EJB接口類似的配置,只是沒有對安全上下文傳遞和遠程事務傳遞的標準支持。當使用RMI調用器時,Spring對這些額外的調用上下文提供了鉤子,你可以在此插入安全框架或者定制的安全證書。
2. 使用 RmiServiceExporter 暴露服務
使用 RmiServiceExporter,我們可以把AccountService對象的接口暴露成RMI對象。可以使用 RmiProxyFactoryBean 或者在傳統RMI服務中使用普通RMI來訪問該接口。RmiServiceExporter 顯式地支持使用RMI調用器暴露任何非RMI的服務。
當然,我們首先需要在Spring BeanFactory中設置我們的服務:
<!-- any additional properties, maybe a DAO? -->
</bean>
然后,我們將使用 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注冊的端口號。通常,你的應用服務也會維護RMI注冊,最好不要和它沖突。更進一步來說,服務名是用來綁定下面的服務的。所以本例中,服務綁定在 rmi://HOST:1199/AccountService。在客戶端我們將使用這個URL來鏈接到服務。
注意:我們省略了一個屬性,就是 servicePort 屬性,它的默認值為0。 這表示在服務通信時使用匿名端口。當然如果你愿意的話,也可以指定一個不同的端口。
3. 在客戶端鏈接服務
我們的客戶端是一個使用AccountService來管理account的簡單對象:
private AccountService accountService;
public void setAccountService(AccountService accountService) {
this.accountService = accountService;
}
}
為了把服務連接到客戶端上,我們將創建另一個單獨的bean工廠,它包含這個簡單對象和服務鏈接配置位:
<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>
這就是我們在客戶端為支持遠程account服務所需要做的。Spring將透明的創建一個調用器并且通過RmiServiceExporter使得account服務支持遠程服務。在客戶端,我們用RmiProxyFactoryBean連接它。
Spring對RMI支持的實際應用實例
在OMAS系統中提供給業務系統的RMI客戶反饋服務的實現服務暴露是通過Resource/modules/interfaces/spring-conf/serviceContext.xml配置文件實現的,而遠程接口的實現類必須序列化(即實現Serializable接口)。
Resource/modules/interfaces/spring-conf/serviceContext.xml的內容如下:
<!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實現類的配置 -->
<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>
對應的暴露的服務接口如下:
/**
* <b>方法用途和描述:</b> 客戶反饋RMI服務端接口方法<br>
* <b>方法的實現邏輯描述:</b> 通過RMI提供服務,Spring支持的RMI遠程調用
* @param systemID : 業務系統的唯一標識
* @param feedbackType :用戶反饋的類型(1-系統BUG、2-系統易用性、3-客服人員態度、4-運維人員態度、5-其他)
* @param feedbackContent :用戶反饋的正文內容
* @return 反饋是否成功 true | false
*/
public boolean setFeedback(String systemID, FeedbackType feedbackType,
String feedbackContent) throws OMASServiceException ;
}
暴露的服務接口實現如下:
private static Log _log = LogFactory.getLog(FeedbackWebServiceImpl.class);
private static final long serialVersionUID = -5532505108644974594L;
/**
* 客戶反饋服務接口
*/
private IFeedbackOperationService feedbackService;
/**
* 方法用途和描述: 注入運營模塊的添加客戶反饋的服務
* @param feedbackWebService 運營模塊服務
*/
public void setFeedbackService(IFeedbackOperationService feedbackWebService) {
_log.info("注入運營模塊的添加客戶反饋的服務");
this.feedbackService = feedbackWebService;
}
/**
* 方法用途和描述: 外部接口子系統中添加客戶反饋的方法
* @param systemID :業務系統ID
* @param feedbackType :反饋類型
* @param feedbackContent :反饋內容
* @return 操作是否成功 ture or false
* @throws ServiceException
*/
public boolean setFeedback(String systemID, FeedbackType feedbackType, String feedbackContent) throws OMASServiceException {
_log.info("進入到外部接口的添加客戶反饋的方法");
if (BlankUtil.isBlank(systemID) || BlankUtil.isBlank(feedbackType)
|| BlankUtil.isBlank(feedbackContent)) {
_log.error("添加客戶反饋的接口參數為空!");
throw new OMASServiceException("omas.interfaces.001");//添加客戶反饋的接口參數為空
}
WebServiceFeedbackVO vo = new WebServiceFeedbackVO();
vo.setFeedbackType(String.valueOf(feedbackType.getValue()));
vo.setFeedbackContent(feedbackContent);
vo.setSystemID(systemID);
_log.info("調用運營子系統的添加客戶反饋的方法開始!");
try {
if (feedbackService == null) {
_log.error("運營模塊服務為空");
throw new OMASServiceException("omas.interfaces.002");//運營模塊服務為空
}
feedbackService.addFeedbackOperation(vo);
} catch (ServiceException e) {
_log.error("調用運營子系統的添加客戶反饋出現異常:"+e.getMessage());
if(ExceptionConstants.EXCEPTION_OMAS_FEEDBACK_VO.equals(e.getMsgKey())){//客戶調用接口的對像為空
throw new OMASServiceException("omas.interfaces.003");
} if(ExceptionConstants.EXCEPTION_OMAS_FEEDBACK_SYSTEMID.equals(e.getMsgKey())){//業務系統標識ID為空
throw new OMASServiceException("omas.omasservice.010");
} if(ExceptionConstants.EXCEPTION_OMAS_FEEDBACK_SIZE.equals(e.getMsgKey())){//非法的業務系統唯一標識
throw new OMASServiceException("omas.interfaces.004");
} if(ExceptionConstants.EXCEPTION_OMAS_FEEDBACK_BASE.equals(e.getMsgKey())){//數據庫訪問出了一點小問題!
throw new OMASServiceException("omas.interfaces.005"); } throw new OMASServiceException("omas.omasservice.000");//未捕獲到的異常信息! } return true; } }
接口方法setFeedback(String, FeedbackType, String)的實現大家不用關心,其與RMI并無關系,只是一些純業務處理邏輯而已,要注意的是接口實現類必須實現 IfeedbackWebService和Serializable接口。
在客戶本地的omasservice.jar包中客戶反饋的RMI客戶端的配置如下:
Resource/config/omasrmi-client.xml
<!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>
客戶端調用RMI服務的方法如下所示:
* 方法用途和描述: 客戶反饋:通過RMI方法與OMAS通訊
* 方法的實現邏輯描述:
* @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("業務系統標識<SystemID>為空或不是對象");
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("配置文件錯誤: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文件時出現異常:" + 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配置文件獲取服務對象時出現異常:" + e);
throw new OMASServiceException("omas.omasservice.009");
}
boolean bln = service.setFeedback(systemID, feedbackType, feedbackContent);
_log.info("反饋操作是否成功[true|false]:" + bln);
return bln;
}
在此客戶端調用的程序中,你要關注的主要是以上背景色標志為黃色的相關代碼。
原文:http://www.blogjava.net/zhenyu33154/articles/320245.html
文章列表