UML软件工程组织 |
IDL-to-Java 的映射:第一部分 怎样将离散的构件接口定义转换为 Java 元素 |
Dave
Bartlett 顾问,作家,讲师 2000年10月 |
这篇文章开始阐述 IDL-to-Java 的映射。这个月的专栏介绍基本的数据类型、结构和数据传递。下个月我们将会介绍更加复杂的类型。语言映射并非无足轻重,COBRA 规范中有很大一部分阐述的就是多语言映射。 COBRA 规范详细说明了接口定义语言(IDL)并详细说明了 IDL 到几种编程语言的映射, 如 C,C++,ADA,COBOL,Lisp,Smalltalk 和 Java。IDL 的优势在于它完整而详细描述了接口及操作参数。 IDL 接口提供了开发使用接口操作的 Client 和实现接口的 Server 所需要的信息。 当然,Client 和 Server 并没有在接口中编写,IDL 只是一种纯粹的描述性语言。 Client 和 Server 用完全意义上的编程语言来编写。IDL 语言映射应该为接口定义提供一致的、可移植的构架。那么这些映射的接口就可以在 Server 端实现,或者像其它方法一样由 Client 端调用。对于使用的每一种编程语言来说,都需要有一种映射来方便的转换已定义的 IDL 概念。 IDL 概念到 Client 语言结构的映射取决于编程语言的结构和能力。 例如,一个 IDL 异常(exception)在不提供 exception 的语言中可能被映射为结构(structure), 而在提供 exception 的语言中就可以直接映射为 exception。每种语言的特性和效率必须与之相适应。 IDL 到编程语言映射的前提 所有的 IDL 基本数据类型 MotherIDL 为了检验映射,你需要一个 IDL-to-Java 的编译器。每个 CORBA ORB 都至少带有一个 IDL 到某种语言的编译器。它们大多针对 C++ 或 Java 编程语言,当然也有其它的。新增加的还有针对 Python 和 Delphi 的。本栏我们要使用 Object Oriented Concepts, Inc. 的 Orbacus ORB 所带的 JIDL。(见 参考资料) 当然你也可以使用任何 IDL 到 Java 的编译器,但是要确定它兼容 CORBA 2.3,因为如果编译器的版本较早的话你的结果可能有本质的区别。 现在首先要做的就是运行 IDL-to-Java 的编译器来编译 motheridl.idl. jidl --output-dir . . \. . \. . MotherIDL.idl 这一步很奇妙,它在提供支持和通用 CORBA 通道的同时给出了一大堆 IDL 文件所映射的 Java 文件。 IDL-to-Java 的库结构 以 motheridl.idl 为例,你将在 IDL 文件中看到如下结构: module corbasem { 这转换为以下的目录结构: E:\_work\TICORBA\Projects\corbasem\gen\motheridl>dir /AD /S Directory of E:\_work\TICORBA\Projects\corbasem\ 07/15/2000 01:41p <DIR> . Directory of E:\_work\TICORBA\Projects\corbasem\gen 07/15/2000 01:41p <DIR> . Directory of E:\_work\TICORBA\Projects\corbasem\gen\motheridl 07/15/2000 01:41p <DIR> . 将 IDL 映射为 Java 元素的一个基本思想是一致性。如同其它任何类库一样,它们必须存在稳定性,也就是说在修改库中的类时不必删除现有的方法;不改变接口。CORBA 在这方面应该做的更好,因为多个 ORB 提供商将要使用这一映射来生成类库。我们希望在不同 ORB 实现之间有着一致性;这意味着可移植性。如果 ORB 提供商A的 IDL 到 Java 映射在 org.VendA.Excep 的 package 中提供了 UserException ,而提供商B在 org.VendB.UtilTypes 的 package中也提供了相同的 UserException ,Client 或 Server 的代码将不能够移植。Client 或 Server 移动到另一个 ORB 时需要改变代码并重新编译。这可不是我们选择 CORBA 的原因!我们希望并要求可移植性;因此 OMG 规定了库结构。 在 Java 编程语言中,关键词 package 将 Java 类组成类库,并控制对类库中的构件的访问。我们的 Java 类库中将包含 Java 编程语言中需要编译的所有素材,这些我们已经在 IDL 中描述过了。但是为了不同提供商的类库之间的可移植性,必须定义类库结构或者类库包,并且严格遵守这些定义。只有这样 Client 才能依赖于代码并且保证不用在不同提供商的ORB之间移植时重写代码。 在 IDL 到 Java 映射的可移植性部分中规定了 API,它提供了库结构和最小功能集,使 Java ORB中可以使用可移植的存根和骨架。因为 Java 类经常被下载,它们往往来自独立于ORB提供商的代码,因此对于 Java 编程语言的互操作性需求就超过了其它语言。出于这些原因,定义存根和骨架使用的接口是最基本的要求。如果这些结构不加以定义的话,存根(或骨架)的使用将需要一个或两个方案。一个要求由 IDL 到 Java 编译器或ORB提供商提供的类似工具(或者与ORB所使用的兼容)生成存根(或骨架),以使生成的存根能够适合ORB提供商的类库结构,另一个方案要求下载存根或骨架时同时下载整个ORB运行时环境。我们不希望采用这两种方案中的任何一个。理想的情况是将 IDL 发送到 Client 端或者由 Client 下载,利用 Client 端选择的工具生成存根,并从它们的环境连接到我们的 Server。 因此,Java 语言映射高度依赖于标准的结构,它在一套标准的 Java 包中实现 -- org.omg.*。IDL 到 Java 映射中重要的一项就是包含 PIDL,本地类型和ORB可移植接口的压缩文件。它给出了包中确切内容的定义性声明。当然,如同这一行中的其它任何事一样,在不远的将来 IDL 到 Java 映射的版本也会发生改变。但是以你对目前的映射的理解,你一定会注意到将来的版本中任何可能的二进制不兼容性。 基本数据类型 IDL 类型 要浏览练习所有这些基本类型的 module,请访问 motheridl.idl 的例子. 整型(Integer) 问题在于 IDL 的无符号整型(unsigned)。Java 编程语言没有 unsigned 类型,并且它所有的整数类型都是有符号的。在多数情况下这不会发生问题,但是当一个 IDL 无符号整数取值正好落在最高位所限制的取值范围中时,类型转换就会发生不匹配的错误。如果不检查并改正这种错误,转换为 Java 后的结果就会是一个负数,而不是一个接近无符号整数类型取值上限的数值。 例如,假设有一个从 IDL 接口返回的 unsigned short 类型值,这一类型的取值范围是0到65535。 Java 的有符号 short 类型能够接收的取值范围是-32768到32767。那么你可以看到,对于任何在32767到65535之间的值映射为 Java 的 short 以后将成为负数。这就造成了不匹配的障碍,必须进行测试。 对于 unsigned short, unsigned long, 以及 unsigned long long 来说也是这样。 这意味着什么呢?首先,我建议以后写 IDL 定义时不要再使用无符号整型。这会使事情简单的多。其次,如果你在使用或者支持现有的 IDL 接口,那你必须测试 输入的无符号整数,并确保它在 Java 程序中被作为负数正确的处理,或者被拷贝到取值范围较大的变量类型中。 布尔型(Boolean)和8位字节型(octet) IDL 的字节型 octet 长度为8位,它映射为 Java 的 byte 类型。 字符型(Character)和字符串类型(string) IDL 的字符型数据用8位来表示字符集中的一个元素,而 Java 的字符型数据用16位无符号整数来表示 Unicode 字符。要正确的进行类型转换,Java CORBA 运行时环境验证了所有由 IDL 的 char 型映射来的 Java char 型的有效性范围。这一方向的映射没有什么困难,因为我们是把8位的数值映射为16位的数值,Java 程序碰到的任何问题都很容易处理,因为空间是足够的。 然而,要把 Java 的 char 型映射为 IDL 的 char 型,Java 的 char 型就有可能会超出 IDL 使用的字符集所定义的范围。这种情况下就会产生 CORBA::DATA_CONVERSION 的异常。 IDL 的 wchar 仅仅是映射为16位字符集的 IDL 类型,它映射为 Java 的基本类型 char。 如果 wchar 超出了字符集所定义的范围,将会产生 CORBA::DATA_CONVERSION 的异常。 IDL 的 string 类型映射为 java.lang.String。别忘了 IDL 的 string 是一个 char 的序列。这意味着 IDL 的 string 必须满足 IDL char 和 IDL sequence 的要求。因此,在编译过程中要进行字符串中的字符范围检查和字符序列的越界检查。字符范围非法会引起 CORBA::DATA_CONVERSION 的异常。越界会引起 CORBA::BAD_PARAM 的异常。对于 IDL 的 wstring 类型来说其注意事项和使用规则也是一样的。 浮点型(Floating-point) 映射的合法性检查 jidl --output-dir . . \. . \. . MotherIDL.idl 运行结果所产生的 Java 接口定义在 UseAllTypesOperations.java 文件中给出 。 这一生成的文件检查映射的合法性。所有的 Java 类型都像我们所预期的那样。 唯一的诀窍大概就在于 inout 和 out 参数位置上的数据类型--它们都是 holder。 holder 类 CORBA 规定了 in 参数和返回类型采用"值调用"(call-by-value)的方式,而 CORBA 的 out 则是"结果调用"(call-by-result)。inout 的参数类型在输入服务器时通过"值调用"的方式来传递,而在输出时则采用"结果调用"的方式。 out 和 inout 的参数传递模式不能被直接映射到 Java 的参数传递机制。 要支持 out 和 inout 的参数传递模式需要另外使用 holder 类。 IDL 到 Java 的映射为所有的 IDL 基本类型定义了 holder 类,它同时也作为用户定义类型的标准格式。IDL 到 Java 编译器可以为用户定义的类型生成 holder 类,以便以后在 Java 程序中实现这些参数模式时使用。 Client 生成并传递一个适当的holder Java 类实例,这个实例的值在 Java 程序中被传递给每一个 IDL out 或者 inout 参数。Holder 实例的内容(而不是实例本身)被调用它的程序所修改,Client 使用的是调用返回后(有可能)改变了的内容。 CORBA-Java 类库中提供了 IntHolder 的例子。要注意 Java 中 public int 的值,以及 _type() 方法的返回值--它的类型是 long。记住这点是因为 Java 的 int 映射为 IDL 的 long。 记住org.omg.CORBA 包中提供了所有基本 IDL 类型的 holder 类,并为所有已命名的用户定义 IDL 类型生成 holder 类,那些用 typedef 定义的除外。 对于用户定义的 IDL 类型来说,holder 类根据映射的(Java)类型名再附加一个 Holder 来命名。对于 IDL 基本数据类型来说,holder 类的名字就是数据类型映射的 Java 类型名,开头字母大写, 并附加一个 Holder(例如,IntHolder)。 每个 holder 类都有一个来自实例的构造函数,一个默认的构造函数,以及一个公有的实例成员(value)),它是一个有类型的值。默认构造函数将值域设置为 Java 语言为不同类型所定义的默认值:boolean 为 false,numeric 和 char 类型为 0,string 为 null,对象引用也是 null。为了支持可移植的存根和骨架,holder 类也实现 org.omg.CORBA.portable.Streamable 接口。 结论 语言映射是一种翻译,因为你在使用一种语言,而要把它转换为另一种同样可以工作并且可以理解为一种实现的语言。正如一些短语和结构没法在两种自然语言之间很好的翻译一样,有些结构也不太容易映射。我们必须找到解决的方案,虽然它们可能使映射变得更复杂,但是在实现时用起来更容易。holder 类就是这样;开始理解它可能要花些功夫,但是长远来看,它提供了一种通用的、一致的解决方案。 下个月,我们将更加深入的讲解 IDL 到 Java 的映射。我们要来研究一些更有用的类,更加复杂的映射如和 enum 和 union,以及用户定义的类型。 参考资料 见上个月的 CORBA 综合栏目 "OMG 接口定义语言"。
|
版权所有:UML软件工程组织 |