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 定义一个远程接口

I H ello .java 代码如下

import  java.rmi.Remote;

public   interface  IHello  extends  Remote {

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

}

 

 

2 实现远程的接口 (服务端就在此远程接口的实现类中)

HelloImpl.java 代码如下

import  java.rmi.RemoteException;
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 代码如下

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:编译并运行

4.1  javac 命令编译 I H ello .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  启动客户端 : 为了在其他的机器运行客户端程序你需要一个远程接口( I H ello .class) 和一个 stub( HelloImpl _Stub.class)。 使用如下命令运行客户端

>java  Hello_RMI_Client

当运行成功会在控制台打印: Hello zhangxianxin ^_^

 

备注: 如果不想在控制台上开启RMI 注册程序 RMIRegistry 的话,可在 RMI 服务类程序中添加 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 {
     //  这个实现必须有一个显式的构造函数,并且要抛出一个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.定义一个远程接口 

IFile Util .java代码如下 : 

import  java.rmi.Remote;

import  java.rmi.RemoteException;

public   interface  IFileUtil  extends  Remote {

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

}

接口IFileDownload 提供了一个 downloadFile 方法,然后返回一个相应的文件数据。

 

2.实现远程的接口 
   FileImpl 继承于 UnicastRemoteObject 类。这显示出 FileImpl 类是用来创建一个单独的、不能复制的、远程的对象,这个对象使用 RMI 默认的基于 TCP 的通信方式。

File Util Impl.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,文件太大可能导制内存溢出!

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 注册工具来注册这个对象。

File Util Server.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);  // 加上此程序,就可以不要在控制台上开启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", fi le 中假定了 RMI 注册工具 (RMI registry ) 使用并启动了 1099 端口。如果在其他端口运行了 RMI 注册工具,则必须在这个声明中定义。例如,如果 RMI 注册工具在 4500 端口运行,则声明应为:   Naming.rebind("rmi://127.0.0.1 : 4500/FileUtilServer", fi le

另外我们已经同时假定了我们的服务端和RMI 注册工具是运行在同一台机器上的。否则需要修改 rebind 方法中的地址。

 

4.编写客户端 

客户端可以远程调用远程接口(FileInterface) 中的任何一个方法。无论如何实现,客户端必须先从 RMI 注册工具中获取一个远程对象的引用。当引用获得后,方法 downloadFile 被调用。在执行过程中,客户端从命令行中获得两个参数,第一个是要下载的文件名 , 第二个是要下载的机器的地址,在对应地址的机器上运行服务端。

File Util Client.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("第一个参数: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 File Util Impl

这将会生成File Util Impl_Stub.class File Util Impl_Skel.class两个文件。 stub 是客户端的代理,而 skeleton 是服务端的框架。服务端和客户端采用 javac 来编译(如果服务端和客户端在两个不同的机器,则必须复制一个 I File Util 接口)。

使用rmiregistry 或者 start rmiregistry  命令来运行 RMI 注册工具到 window 系统默认的端口上:
> rmiregistry portNumber
此处的portNumber 为端口 , RMI注册工具运行之后,需要运行服务 File Util Server,因为 RMI 的安全机制将在服务端发生作用 , 所以必须增加一条安全策略:  grant{permission java.security.AllPermission "", "";};

为了运行服务端,需要有除客户类(File Util Client . class)之外所有的类文件。确认安全策略在 policy.txt 文件之后 , 使用如下命令来运行服务器。
> java -Djava.security.policy=policy.txt File Util Server

为了在其他的机器运行客户端程序, 需要一个远程接口 ( I File Util .class)和一个 stub(File Util Impl_Stub.class)。 使用如下命令运行客户端 :
> java File Util Client 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 中设置我们的服务: 

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

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

</ bean >

然后,我们将使用 RmiServiceExporter  来暴露我们的服务: 

< 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 注册的端口号。通常,你的应用服务也会维护 RMI 注册,最好不要和它冲突。更进一步来说,服务名是用来绑定下面的服务的。所以本例中,服务绑定在  rmi://HOST:1199/AccountService 。在客户端我们将使用这个 URL 来链接到服务。 

注意:我们省略了一个属性,就是  servicePort  属性,它的默认值为0 。 这表示在服务通信时使用匿名端口。当然如果你愿意的话,也可以指定一个不同的端口。  


3. 在客户端链接服务

我们的客户端是一个使用AccountService 来管理 account 的简单对象: 

public   class  SimpleObject {

   private  AccountService accountService;

   public   void  setAccountService(AccountService accountService) {

     this .accountService = accountService;

  }

}

为了把服务连接到客户端上,我们将创建另一个单独的bean 工厂,它包含这个简单对象和服务链接配置位: 

< 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 >

这就是我们在客户端为支持远程account 服务所需要做的。 Spring 将透明的创建一个调用器并且通过 RmiServiceExporter 使得 account 服务支持远程服务。在客户端,我们用 RmiProxyFactoryBean 连接它。

 

 

 


Spring对RMI支持的实际应用实例

OMAS 系统中提供给业务系统的 RMI 客户反馈服务的实现服务暴露是通过 R esource/modules/interfaces/spring-conf/ serviceContext.xml 配置文件实现的,而远程接口的实现类必须序列化(即实现 Serializable 接口)。

R esource/modules/interfaces/spring-conf/ serviceContext.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 ="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 >


对应的暴露的服务接口如下:

public   interface  IFeedbackWebService {

/**

 * <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 ;

}

 

暴露的服务接口实现如下:

public   class  FeedbackWebServiceImpl  implements  IFeedbackWebService,  Serializable {

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 客户端的配置如下:

R esource/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 >

 

 

客户端调用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;

}

猜你喜欢

转载自itlive.iteye.com/blog/1754522
Rmi