UML软件工程组织 |
IDL-to-Java 映射:第二部分 使用 IDL 映射创建组件接口 |
Dave
Bartlett 顾问、作家兼讲师 2000 年 11 月 |
我们就本月的 CORBA 连接中更复杂的类型和辅助类的问题,来继续研究 IDL-to-Java 映射。 上个月,在 IDL-to-Java 映射的第一部分中,我们研究了基本数据类型、结构和数据传送。本月,我们将集中精力研究映射常数和结构,讨论某些更复杂的类型,例如,序列、数组、异常和 Any 类型。最后,将研究辅助类和它们的功能。 首先,应该提醒您我们正在使用接口定义语言,任何 IDL 的目的就是创建某些组件或服务器的接口。这意味着我们正在创建新的类型。因此,让我们从 interface 关键字的映射和使用 OMG IDL 创建新类型的机制开始讨论。 接口 清单 1. FindPerson 接口
我们现在只集中讨论接口和两个方法 GetByName() 和 GetBySSN()。关键字 interface 的确切含义是什么?仔细研究 OMG CORBA 规范,就会发现 interface 关键字后带有标识(本例中是 FindPerson)。FindPerson 是接口名称,并且它定义了合法的类型名称。只要标识符合 IDL 的语法,就可以使用这种类型的标识。将 FindPerson 作为方法参数或结构成员在 IDL 中使用时,一定要记住 FindPerson 表示支持 FindPerson 接口的对象的引用。 IDL-to-Java 映射的目的是生成 Java 编程语言中的接口引用的表示,以供 Java 程序员使用,而且客户机程序员和服务器程序员都可以使用。我使用以下命令行,使用
Orbacus ORB、JIDL 附带的 IDL-to-Java 编译器来运行 MotherIDL: 由于 MotherIDL 中有许多内容,因此将会得到大量生成代码的文件。而我们对于放入 corbasem\gen\motheridl\holderexample 目录下的文件 FindPerson.java 和 FindPersonOperations.java 感兴趣。 FindPerson.java 称为签名接口。它很简单: Listing 2. FindPerson.java
接下来如何?毫无结果!而事实上有很多,只不过是隐藏的。首先注意两个 Java 关键字:public interface。是的,Java 编程语言有自己的 interface 关键字。它很重要,因为它使映射既快速又顺畅。 Java 编程语言的 interface 关键字生成不提供任何实现的完全抽象类。然而,Java 中的 interface 关键字的设计意图是允许接口创建程序建立类的形式:方法名、变量列表和返回类型,但不允许建立方法主体。那么 GetByName() 和 GetBySSN() 在哪里呢? 方法签名在 FindPersonOperations.java 中。称为操作接口的 FindPersonOperations.java 是包含如名称、参数、返回类型和导致的异常等方法细节的文件。 清单 3. FindPersonOperations.java
这更像人们预期的接口文件。但为什么有两个接口文件?这是出于灵活性的考虑。以前版本的映射根据要使用继承还是使用授权实现接口来生成不同的文件。Java 编程语言不支持实现多继承,因此基于继承的实现并不总是最好的。授权与继承相比,是具有相同强大功能的的组合机制,当基于继承的方案不适合时,通常由它提供解决方案。这个版本的规范中,只有一种映射适用于接口。在 IDL-to-Java 编译器上有“连接”开关,因为这两种实现方法之间的区别需要一些下游调整。关于使用继承的实现和使用授权的实现的话题将留到下个月讨论,这里需要另外讨论一下设计模式。 (如果对继承和授权的详细介绍感兴趣,请阅读“四人组”所著的 Design Patterns 一书 -- 请参阅参考资料以获取详细信息。)可以说,映射生成了一系列文件,它们给了您选择适合需要的实现设计的灵活性。 常量 Java 编程语言处理常量的能力很强,尽管它不像 C++ 一样使用 constant 关键字。在 Java 编程语言中,常量必须是原语并用 final 关键字表示。定义时必须给定值。static 和 final 的字段只有一个不能更改的存储块。如果常量是对象引用而非原语,它必须一直指向同一个对象而不能指向另一个。 IDL-to-Java 映射以两种方式处理常量:在接口内声明的常量和在接口外声明的常量。在接口内声明的常量容易处理,因为它们简单地映射到签名接口类中的字段。在 MotherIDL 中,我有一个名为 ConstructedTypes 的模块。在那里我们创建了使用常量关键字的一系列测试。这部分的回顾,如下: 清单 4. 常量示例
可以看到 EquatorialRadiusEarth 在接口内,MeanDensityEarth 在任何接口外,仅驻留在模块嵌套中。文件 corbasem\gen\motheridl\constructuredtypes\PhysicalConstants.java 由先前给定的 jidl 命令生成,看起来如下: 清单 5. PhysicalConstants.java
这非常简洁。常量是在接口内定义的并且它符合接口的根本目的,即允许设计程序建立类的形式。现在接口的用户可以在接口实现的全过程中使用 EquatorialRadiusEarth 了。 但如果使用跨越几个接口的常量会如何呢?这对于 Java 编程语言是需要技巧的,因为事实上每样东西都是对象,创建新的类型就意味着创建新的类。这就是为常量 MeanDensityEarth 所做的一切。创建了新的接口类 MeanDensityEarth.java: 清单 6. MeanDensityEarth.java
接口类 MeanDensityEarth 封装名为 value 的单精度浮点成员中的常量。请注意,这是个公共 (public) 接口,并且和 IDL 中定义的常量同名。其它 Java 代码使用这个类时,大部分 Java 编程语言编译器直接插入值。虽然这没有您想像的那么直接,最终由编译器插入值,Java 编程语言坚持它的设计原则 -- 每样东西都是对象。(好吧,几乎每样东西。) 结构 让我们回到 MotherIDL 的constructedtypes模块并找到结构 SurfReport 的定义: 清单 7. SurfReport 结构
本 IDL 结构映射成 Java 编程语言文件 SurfReport.java,可在 MotherIDL 的constructedtypes目录中找到它: 清单 8. SurfReport.java
枚举 清单 9. enum Weather
IDL enum Weather 将映射成 Java 编程语言类,使用与 enum 类型相同的名称 -- Weather。这个类声明一种值方法,它返回 Weather 的值、每个 enum 元素两个静态数据,整数转换方法from_int() 和 protected 构造器。下面显示了这个类: 清单 10. Weather 类
请注意,public static final 数据成员中有一个是 int 型变量,并且在 IDL enum 元素名称前面加上下划线 (_)。打算在 switch 语句中使用这个成员。其它数据成员是 enum 类型(本例中,Weather)。它与 IDL enum 元素标签同名(本例中,cloudy 或 sunny)。 联合 清单 11. 单元 BaraometricPressure
PressureScale 是英寸还是立方厘米决定了要使用的元素类型是 float 还是 short。Java 原本不支持联合,因此需要构建它。 IDL 联合的映射类似于所有结构的映射,只是添加某些方法来创建鉴别联合的功能。映射启动与 IDL 联合同名的 final 类。添加到这个类的是:
供应商的具体实现可能各不相同,但要符合规范,它们必须使用以上定义的方法。请参阅 BarometricPressure.java 以获取 Orbacus ORB 是如何实现 IDL 联合的示例。 序列和数组 让我们看一段从包含一对数组和一对序列的 MotherIDL 摘录的代码。 清单 12. 数组
在本例 IDL 中,我们正在创建数组结构,第一个是一维数组,第二个是二维数组,第三个是单个孤立数组 MyString[]。 下一步,创建了两个序列:一个无界限,另一个有界限。请记住,在 IDL 列中,数组不能是无界限的,而序列却可以。最后,将序列放入接口的方法。 数组 清单 13. ofArrays.java
IDL 数组直接映射到 与 IDL 数组标识同名的 Java 数组。数组机制不需要 Java 编程语言支持。我用机制限定,因为 ofArray 生成三个类:ofArrays.java、ofArraysHelper.java 和 ofArraysHolder.java,而 MyString 生成两个类:MyStringHolder.java 和 MyStringHelper.java。上个月的专栏文章(本两部分系列的第一部分,请参阅参考资料获得链接)研究了生成的 holder 类,很快我们将详细研究辅助类。但现在,请注意 IDL 中的数组是有界限的,ofArrays.java 中的数组没有界限。您认为在何处检查数组界限?没错,在结构的辅助类-- ofArraysHelper.java 和 MyStringHelper.java 中。在接口方法中打包数组(或结构)以便作为参数使用时,检查它的界限,如果它们超出了界限,将从辅助类的 write() 方法中抛出 CORBA::MARSHAL 异常。 序列 清单 14. testboundOperations 接口
异常 应该注意 IDL 异常映射与结构非常相似。它们被映射成提供异常和构造器的字段实例变量的 Java 类。CORBA 系统异常继承 java.lang.RuntimeException。通过扩展 IDLEntity 的 org.omg.CORBA.UserException 从 java.lang.Exception 继承用户定义的异常。 用户异常 清单 15. 用户异常 OilSpill
运行 IDL-to-Java 编译器后,生成 OilSpill 类: 清单 16. OilSpill 类
将 OilSpill 映射到指定成 final 并扩展 org.omg.CORBA.UserException 的 Java类,它有一个名为 leakage 的公有整型变量和三个构造器:缺省构造器、正常或满构造器、“非常完整”构造器。附加的“非常完整”构造器有附加的初始字符串原因参数,在调用基本 UserException 构造器前,将它并置到标识。同样生成普遍存在的 Helper 和 Holder 类。 系统异常 Any IDL 类型 Any 带有自身的“免烫”映射的 Java 类 -- org.omg.CORBA.Any -- 已经出现 CORBA 库中。它是很健壮的类,包含插入和抽取预定义类型实例的方法。 Any 类同样带有处理非原始 IDL 类型的一对类属可流化方法。之前,我们研究了使用 interface 关键字或 struct 关键字来创建新类型。这些类型是非原始类型或用户定义类型,Any 类型必须同样能够处理这些类型。要这样做,需调用 create_input_stream() 方法创建包含 Any 值的流,使用方法 read_value() 从输入流读取 Any 值。要从另一个方向进入,要清空输出流,调用 create_out_stream() 并使用 write_value() 向输出流写入 Any 值。但真正起作用的是那些我们一直讨论的辅助文件。这些辅助文件是为所有以前的类型生成的。它们无处不在--只要查看由 IDL-to-Java 编译器生成的目录便知。每个用户定义的 IDL 类型生成一种辅助类,它包含一对将它与 Any 相互转换的方法。这些生成的辅助类和 CORBA 提供的 Any 类的组合成了非常灵活的机制。因此让我们进一步研究辅助类。 辅助 提供的或生成的辅助类包含用不同方式操纵 IDL 类型的方法。辅助类提供客户机能用来操纵类型的静态方法。这些包含如上所述的 Any 插入和抽取的方法,获取库标识,获取类型代码,从流中读取类型或写入类型。对于映射的 IDL 接口,辅助类包含用来将 org.omg.CORBA.Object 的对象引用造型到基本辅助类型的 narrow() 操作。 结束语 要成功对 Java 语言使用 CORBA 需要彻底理解 IDL-to-Java 映射;但是因为要彻底理解映射而付出的努力会在您的项目和 CORBA 的成就上得到巨大回报。 参考资料 在以前的 CORBA 连接中:
在 Web 上:
有益的下载:
参考书籍:
关于作者
|
版权所有:UML软件工程组织 |