级别: 初级 Dave Bartlett (dbartlett@pobox.com), 顾问,作家,讲师
2000 年 10 月 01 日 这篇文章开始阐述 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 基本数据类型
- 所有的 IDL 结构数据类型
- IDL 中定义的常量
- IDL 中定义的对象引用
- 对操作的调用,包括传递参数和接收结果
- 异常,包括当操作产生异常时的处理和访问异常参数的方法
- 属性访问
- ORB 定义的操作符号,如动态调用接口,对象适配器等等。
一个完整的语言映射允许程序员以对于某种特定编程语言来说很便捷的方式来访问所有的ORB 功能。为了支持源代码的可移植性,所有的 ORB实现都要支持某种语言的同一映射。
MotherIDL
在本文中将使用一个叫做 MotherIDL 的文件--
motheridl.idl 。以上要求这一 IDL文件都可以做到,它的目的就是示意并检验部分映射。你最好先浏览它一下,这样我们逐步讲述的时候你就会对它比较熟悉了。
为了检验映射,你需要一个 IDL-to-Java 的编译器。每个 CORBA ORB都至少带有一个 IDL 到某种语言的编译器。它们大多针对 C++ 或 Java编程语言,当然也有其它的。新增加的还有针对 Python 和 Delphi的。本栏我们要使用 Object Oriented Concepts, Inc. 的 Orbacus ORB所带的 JIDL。(见
参考资料)当然你也可以使用任何 IDL 到 Java 的编译器,但是要确定它兼容 CORBA2.3,因为如果编译器的版本较早的话你的结果可能有本质的区别。
现在首先要做的就是运行 IDL-to-Java 的编译器来编译
motheridl.idl .
jidl --output-dir . . \. . \. . MotherIDL.idl
|
这一步很奇妙,它在提供支持和通用 CORBA 通道的同时给出了一大堆IDL 文件所映射的 Java 文件。
IDL-to-Java的库结构
我们要学习的第一个映射是 IDL 关键词
module 到 Java的关键词
package 的映射。这是一个简单的完全映射。如果在 IDL 文件中有关键词
module ,你的 IDL 到 Java 编译器将会用关键词
package 生成一个 Java类的目录结构和库结构。(如果你对于 Java 程序中如何使用关键词package还有什么困惑的话,那你现在应该去复习一下了。)这一映射规定了从 IDL生成的大部分结构,对 IDL 到 Java 的映射有着重要影响。
以
motheridl.idl 为例,你将在 IDL文件中看到如下结构:
module corbasem {
module gen {
module motheridl {
module holderexample {
...
};
module conflicts {
...
};
module basictypes {
...
};
module constructedtypes {
...
};
module holderexample2 {
...
};
module MI {
...
};
module MI2 {
...
};
};
};
};
|
这转换为以下的目录结构:
E:\_work\TICORBA\Projects\corbasem\gen\motheridl>dir /AD /S
Volume in drive E has no label.
Volume Serial Number is B415-7161
Directory of E:\_work\TICORBA\Projects\corbasem\
07/15/2000 01:41p <DIR> .
07/15/2000 01:41p <DIR> ..
0 File(s) 0 bytes
Directory of E:\_work\TICORBA\Projects\corbasem\gen
07/15/2000 01:41p <DIR> .
07/15/2000 01:41p <DIR> ..
0 File(s) 0 bytes
Directory of E:\_work\TICORBA\Projects\corbasem\gen\motheridl
07/15/2000 01:41p <DIR> .
07/15/2000 01:41p <DIR> ..
07/15/2000 01:41p <DIR> basictypes
07/15/2000 01:41p <DIR> conflicts
07/15/2000 01:41p <DIR> constructedtypes
07/15/2000 01:41p <DIR> holderexample
07/15/2000 01:41p <DIR> holderexample2
07/15/2000 01:41p <DIR> MI
07/15/2000 01:41p <DIR> MI2
0 File(s) 0 bytes
|
将 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,它提供了库结构和最小功能集,使 JavaORB中可以使用可移植的存根和骨架。因为 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 类型
|
Java 类型
|
异常
| |
boolean
|
Boolean
| | |
char
|
Char
|
CORBA::DATA_CONVERSION
| |
wchar
|
Char
|
CORBA::DATA_CONVERSION
| |
octet
|
Byte
| | |
string
|
java.lang.string
|
CORBA::MARSHAL, CORBA::DATA_CONVERSION
| |
wstring
|
java.lang.string
|
CORBA::MARSHAL, CORBA::DATA_CONVERSION
| |
short
|
Short
| | |
unsigned short
|
Short
|
large number mismatch ?test
| |
long
|
Int
| | |
unsigned long
|
Int
|
large number mismatch ?test
| |
long long
|
Long
| | |
unsigned long long
|
Long
|
large number mismatch ?test
| |
float
|
Float
| | |
double
|
Double
| | |
long double
|
**unmapped
|
现在还不清楚是否会增加这一类型作为新的基本类型或者是类库的补充,如
java.math.BigFloat
|
要浏览练习所有这些基本类型的 module,请访问
motheridl.idl 的例子.
整型(Integer)
IDL 符号整型(signed integer)到 Java 类型的映射不存在任何问题。IDL的
short 类型映射到 Java 的
short ,IDL 的
long 映射到 Java 的
int ,IDL 的
long long 映射为 Java 的
long 。这些都是直接映射,不会给你带来什么麻烦的。
问题在于 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 类型到 Java 类型的映射也是直接映射。IDL 的布尔常量
TRUE 和
FALSE 映射到相应的布尔量
true 和
false 。
IDL 的字节型
octet 长度为8位,它映射为 Java 的
byte 类型。
字符型(Character)和字符串类型(string)
字符类型的映射有一些困难。首先是所使用的字符集,其次是表示整个字符集所需要的编码位数。不同的语言有不同的字符,它们分别被国际标准化组织映射为不同的字符集。这些字符集代表了某种语言的字母或符号到数字的映射。一种语言中符号的数量决定了这种语言所需要的位宽。现在有8位和16位两种字符集。
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)
因为OMG IDL 和 Java 的浮点型数据都遵从
IEEE 754-1985 Standardfor Binary Floating Point Arithmetic,所以浮点型数据的转换没有问题。然而,目前 Java 编程语言中还没有对IDL 的
long double 类型的支持。 现在还不清楚
java.math.* 是否会增加这一类型作为基本数据类型或新的包,或者说什么时候会增加,也许会作为
java.math.BigFloat 吧。这一问题就留待以后修订了。
映射的合法性检查
我们可以用 Orbacus ORB 带有的 IDL 到 Java 编译器来运行我们的
motheridl.idl 文件:
jidl --output-dir . . \. . \. . MotherIDL.idl
|
运行结果所产生的 Java 接口定义在
UseAllTypesOperations.java 文件中给出 。
这一生成的文件检查映射的合法性。所有的 Java类型都像我们所预期的那样。 唯一的诀窍大概就在于
inout 和
out 参数位置上的数据类型--它们都是 holder。
holder 类
在任何语言中,参数传递都是一个有趣的话题,当要把OMG IDL这样独立于语言的体系结构进行语言映射时,它又是一个伤脑筋的问题。Java程序总是采用传值的方式。这意味着要把原语传递到方法中时,会得到一个原语的本地拷贝。然而,如果方法的参数是Java对象的话,则不会传递对象本身而是对象的引用。因此,被传递的是引用的拷贝,但是这个引用通过值来传递。
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 生成并传递一个适当的holderJava 类实例,这个实例的值在 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 接口。
结论
这里只是 IDL 到 Java 映射的第一部分。你应该认识到,用 Java编程语言编写基于 CORBA 的应用程序和构件要求理解 IDL 到 Java语言映射。CORBA规范包括很多语言映射。它们在你工作的各方面中都会有所体现。你的代码的可移植性不仅取决于OMG的库结构,而且也取决于你自己所生成的库结构。映射的目的是将接口定义转化为某种特定语言的实现,这种转化可能会以牺牲定义的完美性来换取实际的可行性。正如我们在无符号整型的例子中所看到的,你必须保持一定的警惕性以保证你的应用程序能够正常工作。
语言映射是一种翻译,因为你在使用一种语言,而要把它转换为另一种同样可以工作并且可以理解为一种实现的语言。正如一些短语和结构没法在两种自然语言之间很好的翻译一样,有些结构也不太容易映射。我们必须找到解决的方案,虽然它们可能使映射变得更复杂,但是在实现时用起来更容易。holder类就是这样;开始理解它可能要花些功夫,但是长远来看,它提供了一种通用的、一致的解决方案。
下个月,我们将更加深入的讲解 IDL 到 Java的映射。我们要来研究一些更有用的类,更加复杂的映射如和
enum 和
union ,以及用户定义的类型。
参考资料
关于作者  | 
|  | Dave Bartlett 住在 Pennsylvania 州的
Berwyn,兼任顾问、作家和教师。他是
Hands-On CORBA with Java
的作者,这是一本通过公开会议或内部组织赠送的5天教程。目前,Dave
正致力于将原始讲义改编为
Thinking in CORBA with Java
一书。Dave
具有宾夕法尼亚州工程和商学双硕士学位。如果你有什么问题或者对某一主题感兴趣的话,可以和
Dave 联系,他的邮件地址是
dbartlett@pobox.com。
|
对本文的评价
|