IDL 接口 全部 CORBA 结构从接口开始。理解接口的最好方法是想象我的车,是的,我的车。虽然你不熟悉它,但如果我对你说:“开我的车,带一些三明治作为午餐回来”,恐怕你不会怀疑你是否能开我的车。你可能想知道它停在哪里,驾驶它是否安全,但你会相信驾驶我的车和驾驶你的车没有太大区别。这是因为在各种汽车中,人与汽车之间的界面已经高度标准化。我的车和你的跑车可能有一些区别,但是油门踏板、刹车和方向盘的安装是标准的,你一定能轻松快速地上路。
因为 CORBA 它与语言无关,因此它依赖于界面来定义语言 (IDL),表达客户机如何向实现接口的服务提出请求。我们的接口是一种方法:add()。这种方法将取两个数(两个数) IDL 的 long 型数)并返回这两个数之和。以下是我们的接口计算程序:
清单 1. calcsimpl.idl
module corbasem { module gen { module calcsimpl { interface calculator { long add(in long x, in long y); }; }; }; };
在这个接口中 IDL 关键字有:module、interface、long 和 in。IDL 使用关键字 module 为了创建名称空间,这个关键字准确地映射成 Java 关键字 package。运行 IDL-to-Java 生成编译器 Java 这些文件将存储在名称中 calcsimpl 子目录中。IDL 关键字 interface 完美地映射为 Java 界面,并代表一种抽象类型,因为它们都只定义你与对象沟通的方式,而不涉及对象的实现。IDL 关键字 long 它是一种基本的整数类型,至少映射成一个 4 字节类型,这种类型在 Java 代码中就是 int。
考虑到远程方法调用机制的实施,您会发现定义参数传输方向(客户机到服务器、服务器到客户机或双向传输)是多么有意义。在 IDL 这些方向在操作中使用 in、out 和 inout 为了声明关键字,每个参数都必须声明方向,以便对象要求代理程序 (ORB) 了解参数的去向。这将影响参数包装、参数解包和内存管理的发送。ORB 对参数了解得越多,效率就越高。关键字 in 表明 long x 和 long y 从客户机传输到服务器。
图 1. 参与 CORBA 请求的各个部分
IDL 编译器
需要 IDL 编译器吗? 你可能已经有了 ORB 供应商和 IDL-to-Java 编译器。但是如果还没有,你从哪里得到呢?这里有很多,有些可以免费下载。我推荐 Object Oriented Concepts, Inc. 的 Orbacus ORB。如果不用于商业目的,也可以免费下载,完全符合要求 CORBA 2.3 规范。另一个可以试用 60 天的编译器是 Inprise 的 Visibroker,也完全符合 CORBA 2.3 并且可以下载规范。如果您想获得这两种产品,请参考参考资料。
界面定义后,界面必须定义 ORB 供应商提供的 IDL-to-Java 操作在编译器上。IDL 编译器是一个精致的实用程序,它生成 IDL 的 stub 和 skeleton 以及其他支持文件。这些源文件的大部分生成都会得到加强 CORBA 标准中定义的特定性 IDL 类型包装功能。编译器生成大部分网络探测器 (plumbing),这在分布式系统中非常重要。在最基本的层次上,IDL-to-Java 编译器只是一个按压 CORBA 2.3 从规范的定义来实现 IDL 到 Java 语言映射程序。手动生成这些代码既枯燥又耗时,而且容易出错;IDL-to-Java 编译会处理这一切,所以你不用担心;同时,它会用一定的规则约束你,强迫你包装。IDL-to-Java 编译器将把 CORBA-land 规则强加给你的系统。输入以下命令,从 Orbacus 执行 IDL-to-Java 编译器将所有生成的文件放在编译器中 CLASSPATH 输出目录下。
清单 2. 调用 IDL-to-Java 编译器
jidl --output-dir c:\_work\corbasem calculator.idl
产生了什么?这个命令产生了构建和实现所需的一切 Java 源文件。IDL-to-Java 编译器可以确保定义的接口遵守 CORBA 规范规则。
图 2. IDL-to-Java 生成编译器文件
以下是这些文件: calculator.java - 该文件称为标记接口文件。CORBA 该规范指出,该文件必须扩展 IDLEntity,并且与 IDL 接口同名。该文件提供类型标记,使接口能够用于其他接口的方法声明。 calculatorOperations.java - 本文件包含 Java 公共接口 -- calculatorOperations。该规范指出,该文件应与所有权相匹配 Operations 后缀的 IDL 接口同名,该文件包含该接口映射的操作标记。上面定义的标记接口 (calculator.java) 这个接口可以扩展。 calculatorHelper.java - 设计 helper 类别的目的是使许多所需的内部处理功能与我们的界面分离,但在实现过程中随时可用。帮助程序文件包含重要的静态 narrow 方法,这种方法 org.omg.CORBA.Object 引用收缩为更具体类型的对象;在这种情况下,它将是一种计算程序类型。 calculatorHolder.java - holder 该类是一种特殊类型,它是为需要通过引用来传递参数的任何数据类型而生成的。不会使用这个例子 holder 类,但我们将在以后的栏目中经常看到它。 calculatorPOA.java - skeleton 类为 CORBA 该功能提供了请求-响应检测的很大一部分。生成 calculatorPOA.java,这是因为缺省的实现是基于继承的,如果我们选择基于委托的实现,输出就会有所不同。这些主题将在以后的专栏中详细介绍。 _calculatorStub.java - 顾名思义,这是一个 stub 类。您的客户机将需要这类工作。
服务器 现在生成的文件必须在服务器上工作,以实现我们的接口。幸运的是,大多数测试都适合我们的要求,但不要太高兴 -- 还有很多工作要做;也就是说,所有这些文件都必须在正确的地方使用。
让我们从 add() 实现方法的开始。(您可以下载完整的 SimpleCalcSvr.java 文件。)
清单 3. SimpleCalcSvr.java -- add() 方法
SimpleCalcServantextends calculatorPOA { publicint add(intx,inty) { return x + y; } }
请注意,我们的实现类扩展了已生成的类别 calculatorPOA。从客户机发送请求时,请求通过 ORB 进入 skeleton,skeleton 最终将调用 SimpleCalcServant,完成请求并启动响应。我们的界面很简单,所以我们的实现也很简单。
实现服务器的其他部分涉及如何围绕这个接口设置 CORBA 由于可移植性和灵活性的原因,系统结构中有许多这样的调用需要遵循 CORBA 规范执行。
我们需要完成的第一项任务是详细说明使用哪一项 ORB,然后初始化。以下代码(文件) SimpleCalcSvr.java 的第 18 行到第 29 处理此任务:行):
清单 4. SimpleCalcSvr.java -- 初始化 ORB
java.util.Propertiesprops = System.getProperties(); props.put("org.omg.CORBA.ORBClass", "com.ooc.CORBA.ORG"); props.put("org.omg.CORBA.ORBSingletonClass", "com.ooc.CORBA.ORBSingleton"); org.omg.CORBA.ORBorb =null; // 初始化 ORB orb = org.omg.CORBA.ORB.init(args, props);
初始化 ORB 需要准确告诉它哪个类将被用作 ORBClass,将使用哪个类别? ORBSingleton 类。我们的实现不会考虑这些,但所有相关的探测都会考虑这些。正如我前面所说,在这种情况下,我使用它 Object Oriented Concepts, Inc. 的 Orbacus ORB,而 OOC 类在那两个 props.put() 已在调用中给出。属性一旦填入,props 仅作为一个参数传递给它 ORB.init() 方法。如果我们想把这个服务器移到另一个服务器,实际情况可能不是这样; ORB,不想重新编码服务器。因此,在理想情况下,我们宁愿改变一个配置文件来指向另一个 ORB 类,然后直接重新启动。
现在,ORB 它已经到位并初始化,实现已经到位,但尚未创建。此时,有必要为实现创造一个完美的生存场所,这并不像听起来那么容易。在分布式环境中,每个实现要求的环境可能略有不同。实现许多特征是可以赋予的。实现既可以是单线程,也可以是多线程;既可以是高度可伸缩的对象池,也可以是单元素。可移植对象适配器已经产生了许多不同的服务器特征 (POA)。POA 让我们创造一个完美的环境,让我们留在其中。所有符合 2.3 规范的 ORB 每个人都会有根 POA,所有其它 POA 都是从根 POA 创建的。在这个简单的例子中,我已经将专用代码分解成了自己的方法。 runcalc()。
我们的第一项任务是创造一个环境,所以我们必须设置一个环境 POA。本来,CORBA 服务器使用基本对象适配器 (BOA),但是每个供应商的 BOA 都不一样,最新版本 CORBA 规范中,POA 已经完全取代了 BOA。
清单 5. SimpleCalcSvr.java -- 设置 POA
// 它始终存在 rootPOA // 设置可移植对象适配器 org.omg.PortableServer.POArootPOA = org.omg.PortableServer.POAHelper.narrow( orb.resolve_initial_references("RootPOA")); org.omg.PortableServer.POAManagermanager = rootPOA.the_POAManager();
从标题和定义可以看出,这是一个简单的例子。使用根 POA 而不创造新的 POA,让事情变得简单。POA 管理器是一种包装 POA 因此,我们使用处理状态的对象 POA 将发送管理器 servant 排队的请求。
还需要实例化实现:
清单 6. SimpleCalcSvr.java -- 实例化实现
// 创建计算程序接口 servant SimpleCalcServantcalcSvt =new SimpleCalcServant(); calculatorcalc = calcSvt._this(orb);
按照 CORBA 2.3 规范,所有 skeleton 均提供一个 _this() 方法,这种方法 servant 能得到目标 CORBA 引用对象的对象,servant 正是用目标 CORBA 与这些请求相关的对象。
实例化完成后,必须将机制放在适当的位置,以便客户机能够找到它们。有许多不同的方法和服务可以用来找到满足接口要求的对象。CORBA Service 定义 Naming Service 和 Trader Services,帮助客户机找到对象来处理请求。也可以通过调用方法传递对象。
在这个例子中,我们将使用最直接的方法之一 — 将对象引用并写入客户机选择的文件。对于所有的文件 ORB 创建对象引用的字符串表示,或者反过来,从字符串到对象引用的创建,都是必要的功能。
清单 7. SimpleCalcSvr.java -- 引用编写对象
// 将对象引用并写入文件 PrintWriterrefstr =new PrintWriter( new FileWriter("calcref.ior")); refstr.println(orb.object_to_string(calc)); refstr.close();
最后要做的一件事,就是激活它 POA,将客户机请求排队,并强制服务器输入其事件循环,以接收这些请求。
清单 8. SimpleCalcSvr.java -- 激活 POA
// 使实现成为可用 manager.activate(); System.out.println("SimpleCalcSvr is running!"); orb.run();
客户机 假如你考虑到正在发生的事件的机制,你就会明白,客户机和服务器实际上是相互映像的。客户机将所有参数打包以创建请求,然后以自己的方式发送请求。服务器只解包请求中的参数,执行操作,包装返回值和输出参数,然后回复客户机。客户机解包返回值和输出参数,然后继续处理。这样,服务器就可以解包客户机打包的东西,反之亦然。
这意味着您将看到客户机和服务器具有相似的结构。还必须创建和初始化客户机 ORB。我们正在使用它。 ORB,它也可以由另一个供应商提供 ORB;但是,不能是任意的 ORB,而且应该是支持 IIOP 的 ORB,IIOP 集团由对象管理 (OMG) 定义,基础 TCP/IP 互操作协议。如果您的 ORB 如果是旧的,请小心,它可能无法与其他相比 ORB 通话。
首先,我们以同样的方式创建它 ORB,就像创建服务器一样。(您可以下载完整的 SimpleCalcClient.java 文件。)
清单 9. SimpleCalcClient.java -- 初始化 ORB
java.util.Propertiesprops = System.getProperties(); props.put("org.omg.CORBA.ORBClass", "com.ooc.CORBA.ORG"); props.put("org.omg.CORBA.ORBSingletonClass", "com.ooc.CORBA.ORBSingleton"); org.omg.CORBA.ORBorb =null; // 初始化 ORB orb = ORB.init(args, props);
看起来眼熟吗?应该是这样的,它看起来和服务器完全一样。现在客户机已经连接到一个了 ORB 但我们的目标是调用系统中其他地方提供的服务,需要找到能够响应请求的对象。在这个例子中,这意味着从创建在服务器上的文件中引用一个对象。为了找到计算程序服务器,需要获得存储在本文件中的对象引用的字符串版本,然后将其转换为对象引用,可以通过该对象引用进行调用。
清单 10. SimpleCalcClient.java -- 引用获取对象
System.out.println("Getting reference from string..."); BufferedReaderin = new BufferedReader( new FileReader("calcref.ior") ); Stringior = in.readLine(); in.close(); calculatorcalc = calculatorHelper.narrow( orb.string_to_object(ior));
请注意,这里使用的原因 IDL-to-Java 由编译器生成 calculatorHelper 类。calcref.ior 该文件包含对象引用,而不是计算程序引用。calculatorHelper 类有一个 narrow 该方法可用于将抽象类型集中在特定的计算程序类型中。
仔细看看计算程序 calc,它表示计算机空间中另一个地方的服务器。最后要做的一件事就是调用 calc 上的方法 add()。
清单 11. SimpleCalcClient.java -- 调用 add()
System.out.println( calc.add(2,3) );
结论 已经讨论了很多内容,但请想想你学到了什么。我们的客户机与服务器完全隔离。客户机不知道服务器在哪种硬件上运行,使用什么操作系统,用什么语言编写,是否多线程,它在哪里。 — 是在隔壁,还是离半个地球很远。它只知道一点,也就是说,如果它被调用 calc 中的 add(),会得到预期的响应。
提供服务的情况都是这样,电话或电力公司也是这样。当你拿起电话时,你所期望的是听到拔号声,然后你的电话可以顺利连接。你不在乎电话是通过光缆传输还是通过卫星转发。同样的情况也在信息行业成为现实。多亏了 OMG 只有通过这种基本结构,我们才能加入这个简单而有说服力的例子。
下个月,我们将对表面现象下发生的事情进行一点深入的探索 IIOP 神奇力量。
![](/images/780-200-2.jpg)