当前位置: 首页 > 图灵资讯 > 技术篇> 一个简单的 CORBA/java 示例

一个简单的 CORBA/java 示例

来源:图灵教育
时间:2024-03-01 15:57:23
6 月份,我们谈到了你为什么要使用它 CORBA 和 Java 技术。这个月,我想让你通过一个可用的简单示例来探索 CORBA 许多技术领域。然而,别忘了,我们的目标是创建这样一个分布式应用程序:让停留在一台计算机上的客户功能要求运行在另一台计算机上的服务。我们不想担心硬件或操作系统软件等细节,只想让这项服务响应客户机的要求。

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 神奇力量。