最近要研究EJB了,RMI-IIOP这个概念一定要研究学习下,最为分布式设计的基础,掌握RMI-IIOP对EJB运行的机制才会更深入。下面就来总结下学习心得吧!
RMI-IIOP 概览
RMI-IIOP 让您仅需极少修改就可以在 IIOP 上运行RMI调用。借助于RMI-IIOP,您可以编写简单易懂的 Java 代码,同时使用CORBA提供的丰富的企业功能套件。而且,代码的灵活性足够大,可以运行在RMI或IIOP上。这意味着,您的代码可以在纯Java环境中运行(当小的资源占用量和灵活性很关键时),或者对代码作少量修改后集成到现有的CORBA基础架构中。
RMI-IIOP很强大的功能之一是,它让您编写纯Java客户机/服务器实现而不丧失 RMI 类序列化的灵活性。RMI-IIOP通过覆盖Java序列化并在导线上将 Java 类转换成 IIOP 做到这一点。在另一端,Java类被作为IIOP从导线上读下来,接着创建这个类的一个新实例(使用反射),类的所有成员的值都完整无缺 ― 这就是IIOP上的Java序列化!
为了让RMI-IIOP实现透明的对象定位,ORB 供应商历史上曾经使用Java CosNaming 服务提供者。该插件在JNDI API之下工作,访问CORBA命名服务。尽管我没有在这里花篇幅来说明原因,但这种命名解决方案并不理想。许多供应商 ― 尤其是应用服务器供应商 ― 为RMI-IIOP开发了专门的对象定位机制。
RMI-IIOP 也支持作为Java CosNaming 服务的一个扩展的INS。因为我相信INS将确定对象定位的未来方向,所以我们在本文将讨论的代码示例使用INS。
因为RMI-IIOP数据编入是建立在CORBA传输协议(IIOP)的基础上的。我在查点资料解释该传输协议和ORB(object request broker,对象请求代理)在网络上发送请求、定位远程对象和传输对象方面的基本功能。
远程对象传输
对CORBA请求进行数据编入是通过使用IIOP协议做到的。简言之,IIOP将以标准化格式构造的任何IDL(Interface Definition Language,接口定义语言)的元素表示为一系列字节。那就假设有一个Java客户机正在将一个CORBA请求分派到C++服务器吧。客户机应用程序以Java接口的形式拥有远程对象的引用,并调用该接口的一个操作。本质上是,接口调用它对该操作的相应实现,这个实现将位于存根(stub)(存根是您将已经用idlj从IDL生成了的)。
存根把方法调用分派到ORB中,ORB 由两部分组成:客户机ORB和服务器ORB。客户机 ORB 的职责是对请求进行数据编入,放到网络上,传往特定位置。服务器ORB的职责是侦听从网络上传下来的请求,并将这些请求转换成语言实现能够理解的方法调用。
存根分派了方法调用之后,客户机ORB将请求和所有参数转换成标准化字节格式,在这种情况中是IIOP。接着,请求通过导线被发送到服务器ORB,服务器ORB应该正在侦听传入请求。服务器端ORB将读进数据的字节并将请求转换成对C++服务器实现有意义的东西。C++ 服务器方法将执行它的功能(即调用所请求的方法)并使用相同的机制通过IIOP将结果返回给客户机。
RMI以类似的方式处理请求,但是它使用JRMP(Java Remote Messaging Protocol,Java远程消息传递协议)作为其传输协议。当然,RMI 传输还涉及 Java 对象的序列化。
看到这里,我们应该知道CORBA和RMI的区别了吧。
远程对象定位
CORBA使用CosNaming 命名服务定位远程对象。CosNaming 为名称服务器保存对 CORBA 服务器进程的绑定(或引用)提供了一个框架。当CORBA 客户机向名称服务发送 CosNaming 请求,请求给定名称的服务器进程时,名称服务返回该进程的 可互操作对象引用(interoperable object reference(IOR))。接着,客户机使用该 IOR 直接与服务器进程通信。
IOR包含关于服务器进程的信息,例如服务器进程的位置。CosNaming 服务的缺点之一是,IOR对人类而言是难以看懂的 ― 至少对我们这些没有电子大脑的人来说是这样。相反地,RMI对用户则要友好一些。它使用运行在JNDI之上的 注册中心(与命名服务极为相似)来定位远程对象。RMI注册中心使用Java Reference对象(它由若干个RefAddr对象组成)来识别和定位远程对象。这些Java对象比IOR对用户更加友好。
不久前,COBRA将可互操作命名服务(Interoperable Naming Service(INS))结合进了它的对象-定位(object-location)模式。INS 在 CosNaming 上运行,使用人类可以阅读的URL作它的对象位置。INS 不使用命名服务;相反地,它将调用直接发送到指定的 URL。
现在提供一个客户/服务器代码供参考学习吧。代码是转载的,大家都可以好好看看,理解是关键。J2EE的目的不是告诉我们JAVA这个技术,是要告诉我们JAVA的设计思路。
第 1 部分:定义接口
public interface RMIInterface extends java.rmi.Remote { public String hello() throws java.rmi.RemoteException; public SerClass alterClass(SerClass classObject) throws java.rmi.RemoteException; }
RMIInterface定义一个 hello() 方法和一个alterClass(SerClass) 方法。后一个方法用 SerClass 作参数, SerClass是一个实现Serializable的Java类, alterClass(SerClass)方法返回一个类型与其参数的类型相同的类。SerClass 是一个有几个成员的简单的类,每个成员有相应的getter方法。这些方法如下面所示:
/** * This class is intended to be serialized over RMI-IIOP. */ public class SerClass implements java.io.Serializable { private int x; private String myString; public SerClass(int x, String myString) throws java.rmi.RemoteException { this.x=x; this.myString=myString; } public int getX() { return x;} public void setX(int x) { this.x=x; } public String getString() { return myString; } public void setString(String str) { myString=str; } }
第 2 部分:构建服务器
我们将使用一个既充当RMIInterface 实现类又包含 main 方法(以启动我们的服务)的服务器类(Server.java )。Server.java 继承 javax.rmi.PortableRemoteObject 。这样,它就包含了将自己作为Remote 接口绑定到ORB和开始侦听请求所需要的全部功能。代码如下:
首先,如前面提到过的, Server类实现RMIInterface并为它的所有方法提供实现。您可以在代码的前面部分看到RMIInterface 的 hello()方法和alterClass(SerClass) 方法的实现。 hello() 方法只是返回字符串“Hello there!”。 alterClass(SerClass) 方法用 SerClass 对象作参数,修改成员的值,然后返回新的对象 ― 全都通过RMI-IIOP。
Server.java 的 main 方法初始化一个 ORB。这个 ORB 将设置为 8080 的 com.ibm.CORBA.ListenerPort 属性作为参数传入。这将使得 ORB 在端口 8080 上侦听传入请求。请注意, com.ibm.CORBA.ListenerPort 是一个专有的 IBM 属性。如果您想在另一供应商的 ORB 上运行这些代码,那您应该参阅该供应商的文档,找到适当的属性。(Sun使用com.sun.CORBA.POA.ORBPersistentServerPort,但它只在您使用POA(portable object adapter,可移植对象适配器)伺服器(servant)时才能够工作。)
初始化ORB后,main方法接着对Server对象进行实例化。因为这个server对象也是一个PortableRemoteObject,所以缺省构造函数会自动调用exportObject(this) 。这个对象现在已经就绪于接收远程调用。
接着,我们需要通过调用ORB.register_initial_reference(String,orb.omg.CORBA.Object) 注册这个对象。为此,我们需要把我们的server 对象作为org.omg.CORBA.Object的引用。调用PortableRemoteObject.toStub(s)实现了这一点,因为所返回的对象都实现了java.rmi.Remote和org.omg.CORBA.Object 。
然后,返回的org.omg.CORBA.Object 对象向服务器端ORB注册为“OurLittleClient”。为了确保 INS 请求能够定位对象,我们使用注册调用 register_initial_reference 。当 INS 调用进入ORB时,ORB 将查找已经以正在被请求的名称注册的对象。由于我们将对象注册为“OurLittleClient”,所以,当一个 INS 调用进入我们的服务器ORB要求“OurLittleClient”时,我们将知道客户机正在查找的是哪个对象。
最后,我确信您已经注意到我们将 ORB 强制转型成com.ibm.CORBA.iiop.ORB 。因为Sun尚未公开org.omg.CORBA.ORB 接口的register_initial_reference ,所以 IBM SDK 也不能将它公开。因此,我们必须将我们的ORB强制转型成IBM ORB。随着Sun 越来越遵循 OMG,JDK 的未来版本(1.4.0后)将可能不需要这种强制转型。
服务器现在正在等待传入客户机INS请求。但客户机怎么样呢?
第 3 部分:构建客户机
/* * Client application */ import javax.rmi.PortableRemoteObject; import org.omg.CORBA.ORB; public class Client { public static void main(String[] args) { try { ORB orb = ORB.init(args, null); // here's the URL for the local host String INSUrl = "corbaloc:iiop:1.2@localhost:8080/OurLittleClient"; // get the reference to the remote process org.omg.CORBA.Object objRef=orb.string_to_object(INSUrl); // narrow it into our RMIInterface RMIInterface ri = (RMIInterface)PortableRemoteObject.narrow(objRef, RMIInterface.class); // call the hello method System.out.println("received from server: "+ri.hello()+"\n"); // try RMI serialization SerClass se = new SerClass(5, "Client string! "); // pass the class to be altered on the server // of course behind the scenes this class is being // serialized over IIOP se = ri.alterClass(se); //see the result System.out.println("Serialization results :\n"+ "Integer was 5 now is "+se.getX()+"\n"+ "String was \"Client String! \" now is \""+se.getString()+"\""); } catch (Exception e) { e.printStackTrace(); } } }
客户机代码比服务器代码要简单一些。我们初始化一个ORB,然后调用 string_to_object(String) ,其中的 string 是我们的INS URL。构造 INS URL 相当简单:首先,我们指定使用 corbaloc URL(请参阅 参考资料)和IIOP协议版本 1.2。接着,将主机名和要连接的端口添加进去。最后,指定要查找的服务的名称。结果 INS URL 是corbaloc:iiop:1.2@localhost:8080/OurLittleClient。
当将这个 URL 传递到ORB.string_to_object(String) 时,ORB 将分派一个请求到所指定的服务器,以请求所请求的服务。假设一切运转正常,则ORB将接收回该服务的一个对象引用(实际上是一个IOR)。然后,将该对象引用强制转型(narrow)成我们能够使用的东西,即RMIInterface,这样,就为开始调用方法做好了准备。
在调用了简单的hello方法(它应该不需要任何解释吧)之后,我们可以开始探讨 RMI-IIOP 的序列化功能了。首先,创建一个SerClass ,一个可序列化的 Java 类,并初始化它的成员变量。接着,将这个类传入到方法,方法通过IIOP将类写出到服务器。服务器读入类并将它重创建为服务器端 Java 对象,修改它的成员值,然后返回它(使用IIOP)作为方法的返回值。当接收到在远程方法调用之后重创建的对象时,可以看到它的成员确实已被服务器修改了。就是这么简单:在IIOP 上进行Java序列化。
如果你想正常运行这段代码,可能要要辛苦,去下载IBM Developer Kit for Java technology1.3.1或更高版本中运行。因为IBM和SUN提供的CORBA的接口在这里我还不确认是否按照同一标准,我是按照IBM提供的JDK编写的。
最后还要补充的是,EJB2.0规范指出,EJB 组件必须能在RMI和RMI-IIOP 上运行。添加RMI-IIOP作为针对EJB组件的在线协议,已经给将J2EE环境集成到现有的企业基础设施(多数是CORBA相当密集的)带来了很大帮助。