服务热线:13616026886

技术文档 欢迎使用技术文档,我们为你提供从新手到专业开发者的所有资源,你也可以通过它日益精进

位置:首页 > 技术文档 > JAVA > 新手入门 > 基础入门 > 查看文档

一个简单的 corba/java 示例


  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() 方法 simplecalcservant extends calculatorpoa {
public int add(int x, int y) {
return x + y;
}
}



请注意,我们的实现类扩展了已生成的类 calculatorpoa。从客户机发来一个请求时,该请求通过 orb 进入 skeleton,skeleton 最终将调用 simplecalcservant,来完成请求并启动响应。我们的接口很简单,所以我们的实现也很简单。

服务器其余部分的实现,涉及如何围绕这个接口实现来设置 corba 体系结构,由于可移植性和灵活性方面的原因,许多这些调用要按 corba 规范执行。

我们需要完成的第一项任务是,详细说明要使用哪一个 orb,然后予以初始化。下面的代码(文件 simplecalcsvr.java 的第 18 行到第 29 行)处理此任务:

清单 4. simplecalcsvr.java -- 初始化 orb java.util.properties props = 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.orb orb = 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.poa rootpoa =
org.omg.portableserver.poahelper.narrow(
orb.resolve_initial_references("rootpoa"));

org.omg.portableserver.poamanager manager =
rootpo

扫描关注微信公众号