内容


IDL-to-Java 的映射

第二部分

使用 IDL 映射创建组件接口

Comments

系列内容:

此内容是该系列 # 部分中的第 # 部分: IDL-to-Java 的映射

敬请期待该系列的后续内容。

此内容是该系列的一部分:IDL-to-Java 的映射

敬请期待该系列的后续内容。

上个月,在IDL-to-Java映射的第一部分中,我们研究了基本数据类型、结构和数据传送。本月,我们将集中精力研究映射常数和结构,讨论某些更复杂的类型,例如,序列、数组、异常和Any类型。最后,将研究辅助类和它们的功能。

首先,应该提醒您我们正在使用接口定义语言,任何 IDL的目的就是创建某些组件或服务器的接口。这意味着我们正在创建新的类型。因此,让我们从interface关键字的映射和使用 OMG IDL创建新类型的机制开始讨论。

接口


我们将回到所有 IDL 文件的母板 --MotherIDL.idl。我使用这个文件来研究IDL-to-Java 映射各个方面。在 MotherIDL 中,有一个名为 FindPerson的接口。显示如下:

清单 1. FindPerson 接口

  interface FindPerson {
      Person GetByName(in string sName);
      Person GetBySSN(in string sSSN);
   };

我们现在只集中讨论接口和两个方法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:
jidl --output-dir ..\..\.. motheridl.idl

由于 MotherIDL中有许多内容,因此将会得到大量生成代码的文件。而我们对于放入corbasem\gen\motheridl\holderexample 目录下的文件FindPerson.javaFindPersonOperations.java感兴趣。

FindPerson.java称为签名接口。它很简单:

Listing 2. FindPerson.java

   public interface FindPerson extends FindPersonOperations,
                                       org.omg.CORBA.Object,
                                       org.omg.CORBA.portable.IDLEntity
   {
   }

接下来如何?毫无结果!而事实上有很多,只不过是隐藏的。首先注意两个Java 关键字:public interface。是的,Java编程语言有自己的interface关键字。它很重要,因为它使映射既快速又顺畅。

Java 编程语言的interface关键字生成不提供任何实现的完全抽象类。然而,Java 中的interface关键字的设计意图是允许接口创建程序建立类的形式:方法名、变量列表和返回类型,但不允许建立方法主体。那么GetByName()GetBySSN()在哪里呢?

方法签名在FindPersonOperations.java中。称为操作接口的FindPersonOperations.java是包含如名称、参数、返回类型和导致的异常等方法细节的文件。

清单 3. FindPersonOperations.java

   public interface FindPersonOperations
   {
   
       public Person GetByName(String sName);
   
       public Person GetBySSN(String sSSN);
    }

这更像人们预期的接口文件。但为什么有两个接口文件?这是出于灵活性的考虑。以前版本的映射根据要使用继承还是使用授权实现接口来生成不同的文件。Java编程语言不支持实现多继承,因此基于继承的实现并不总是最好的。授权与继承相比,是具有相同强大功能的的组合机制,当基于继承的方案不适合时,通常由它提供解决方案。这个版本的规范中,只有一种映射适用于接口。在IDL-to-Java编译器上有“连接”开关,因为这两种实现方法之间的区别需要一些下游调整。关于使用继承的实现和使用授权的实现的话题将留到下个月讨论,这里需要另外讨论一下设计模式。

(如果对继承和授权的详细介绍感兴趣,请阅读“四人组”所著的Design Patterns 一书 -- 请参阅参考资料以获取详细信息。)可以说,映射生成了一系列文件,它们给了您选择适合需要的实现设计的灵活性。

常量


变量和常量 --它们是非常直截了当的。变量的值可以更改,常量的值却不能更改。在某些编程语言中,二者的差异很大-- 在某些编程语言之间却是完全混淆的。例如,在标准版之前的 C语言中,如果要创建常量,必须使用预处理器。这将在编译器作用域外对那些常数进行控制,因此不执行类型检查。当然,这种情况在C++ 和最终在 C中已改变,但可以看到,它并不象想像中的那样简单,关键是将常量映射到OMG 支持的各种语言。

Java 编程语言处理常量的能力很强,尽管它不像 C++ 一样使用constant关键字。在 Java编程语言中,常量必须是原语并用final关键字表示。定义时必须给定值。static 和 final的字段只有一个不能更改的存储块。如果常量是对象引用而非原语,它必须一直指向同一个对象而不能指向另一个。

IDL-to-Java映射以两种方式处理常量:在接口内声明的常量和在接口外声明的常量。在接口内声明的常量容易处理,因为它们简单地映射到签名接口类中的字段。在MotherIDL 中,我有一个名为 ConstructedTypes的模块。在那里我们创建了使用常量关键字的一系列测试。这部分的回顾,如下:

清单 4. 常量示例

   // Constant examples
   
   // Constant inside an interface
   interface PhysicalConstants {
      const float EquatorialRadiusEarth = 6378.388; // km
   };
   
   // A constant outside an interface
   const float MeanDensityEarth = 5.522; // g/cm^3

可以看到 EquatorialRadiusEarth 在接口内,MeanDensityEarth在任何接口外,仅驻留在模块嵌套中。文件corbasem\gen\motheridl\constructuredtypes\PhysicalConstants.java由先前给定的jidl命令生成,看起来如下:

清单 5. PhysicalConstants.java

   public interface PhysicalConstants extends
   PhysicalConstantsOperations,
                                              org.omg.CORBA.Object,
   
   org.omg.CORBA.portable.IDLEntity
   {
   
      public static final float EquatorialRadiusEarth =
   (float)(6378.38818359375D);
   }

这非常简洁。常量是在接口内定义的并且它符合接口的根本目的,即允许设计程序建立类的形式。现在接口的用户可以在接口实现的全过程中使用EquatorialRadiusEarth 了。

但如果使用跨越几个接口的常量会如何呢?这对于 Java编程语言是需要技巧的,因为事实上每样东西都是对象,创建新的类型就意味着创建新的类。这就是为常量MeanDensityEarth 所做的一切。创建了新的接口类MeanDensityEarth.java

清单 6. MeanDensityEarth.java

   public interface MeanDensityEarth   {
       public static final float value = (float)(5.5219998359680176D);
   }

接口类 MeanDensityEarth 封装名为value的单精度浮点成员中的常量。请注意,这是个公共 (public) 接口,并且和IDL 中定义的常量同名。其它 Java 代码使用这个类时,大部分 Java编程语言编译器直接插入值。虽然这没有您想像的那么直接,最终由编译器插入值,Java编程语言坚持它的设计原则 --每样东西都是对象。(好吧,几乎每样东西。)

结构


OMG 的 IDL 有可以用关键字struct表示结构的概念。基本上,您可以使用它将几个原语或复杂类型结合成一种类型。Java编程语言没有struct关键字,如前所述,鼓励程序员将每样东西放入对象(例如,类)。由于这些原因,IDL结构以相同的名称映射到 final 的类。类向 IDL成员定序的字段提供实例变量,并向所有值提供构造器,并实现IDLEntity。还提供了一个空构造器,可以让程序员稍后填充。

让我们回到 MotherIDL 的constructedtypes模块并找到结构 SurfReport的定义:

清单 7. SurfReport 结构

   // use of struct
   struct SurfReport {
      Weather        presentWeather;
      waveHeightT    waveHeight;
      waterTempT     waterTemp;
      windDirectionT windDirection;
      windSpeedT     windSpeed;
   };

本 IDL 结构映射成 Java 编程语言文件SurfReport.java,可在 MotherIDL的constructedtypes目录中找到它:

清单 8. SurfReport.java

   final public class SurfReport implements
   org.omg.CORBA.portable.IDLEntity
   {
       public
       SurfReport()
       {
       }
   
       public
       SurfReport(Weather _ob_a0,
                  short _ob_a1,
                  short _ob_a2,
                  char[] _ob_a3,
                  short _ob_a4)
       {
           presentWeather = _ob_a0;
           waveHeight = _ob_a1;
           waterTemp = _ob_a2;
           windDirection = _ob_a3;
           windSpeed = _ob_a4;
       }
   
       public Weather presentWeather;
       public short waveHeight;
       public short waterTemp;
       public char[] windDirection;
       public short windSpeed;
   }

枚举


已证实枚举总是有用的。程序员经常使用枚举,并且它很容易理解。但我很难理解为什么Java 编程语言不包含enum关键字。它容易编码,而且我确信它不会增加编程量,我很想念它。这种映射是如何发生的?让我们看一下MotherIDL enum Weather:

清单 9. enum Weather

   enum Weather{cloudy,
                sunny};

IDL enum Weather 将映射成 Java 编程语言类,使用与 enum类型相同的名称 -- Weather。这个类声明一种值方法,它返回 Weather的值、每个enum元素两个静态数据,整数转换方法from_int()和 protected构造器。下面显示了这个类:

清单 10. Weather 类

   final public class Weather implements org.omg.CORBA.portable.IDLEntity
   {
       private static Weather [] values_ = new Weather[2]; 
       private int value_; 
   
       public final static int _cloudy = 0; 
       public final static Weather cloudy = new Weather(_cloudy); 
       public final static int _sunny = 1; 
       public final static Weather sunny = new Weather(_sunny); 
   
       protected Weather(int value) 
       {
           values_[value] = this; 
           value_ = value; 
       }
   
       public int value() 
       {
           return value_; 
       }
   
       public static Weather from_int(int value) 
       {
           return values_[value]; 
       }
   }

请注意,public static final 数据成员中有一个是int型变量,并且在 IDLenum元素名称前面加上下划线(_)。打算在 switch 语句中使用这个成员。其它数据成员是enum类型(本例中,Weather)。它与 IDLenum元素标签同名(本例中,cloudy 或 sunny)。

联合


实际上,判别联合只是一个为节省内存空间而设计的结构。联合将只引用基于鉴别器的几个数据成员中的一个。这些数据成员的类型可能不同。任何时候在内存中一次只会出现一个数据成员。例如,MotherIDL定义了联合 BarometricPressure:

清单 11. 单元 BaraometricPressure

   enum PressureScale{inches,cc}; 
   
   union BarometricPressure switch (PressureScale) {
     case inches : 
       float BarometerInInches; 
     case cc : 
       short BarometerInCCs; 
   };

PressureScale 是英寸还是立方厘米决定了要使用的元素类型是 float还是 short。Java 原本不支持联合,因此需要构建它。

IDL联合的映射类似于所有结构的映射,只是添加某些方法来创建鉴别联合的功能。映射启动与IDL 联合同名的 final 类。添加到这个类的是:

  • 缺省构造器
  • 名为discriminator()的判别器读方法
  • 每个分支的读方法
  • 分支修改方法
  • 具有多个条件标签的每个分支的修改方法
  • 如果存在缺省标签相应的分支修改方法
  • 如果需要缺省修改方法

供应商的具体实现可能各不相同,但要符合规范,它们必须使用以上定义的方法。请参阅BarometricPressure.java以获取 Orbacus ORB 是如何实现IDL 联合的示例。

序列和数组


每次通过 OMG IDL接口传递多个元素时,要使用数组或序列。何时使用数组或序列以及它们之间的差异已在两个月前的IDL 文章中讨论过(请参阅参考资料的链接)。现在我们要讨论的主题是 IDL序列和数组如何映射到 Java编程语言。映射是类似的--带有一些细微的差异--但非常简洁。

让我们看一段从包含一对数组和一对序列的 MotherIDL摘录的代码。

清单 12. 数组

   // array examples
   typedef char MyString[105]; 
   
   struct ofArrays {
      long shares[1000]; 
      string spreadsheet[100][100]; 
   }; 
   
   // bounded and unbounded sequence examples
   typedef sequence<long> Unbounded; 
   typedef sequence<long, 31> Bounded; 
           interface testbound {
                    void testStub(in Bounded inB, out Bounded outB); 
           };

在本例 IDL 中,我们正在创建数组结构,第一个是一维数组,第二个是二维数组,第三个是单个孤立数组MyString[]。

下一步,创建了两个序列:一个无界限,另一个有界限。请记住,在 IDL列中,数组不能是无界限的,而序列却可以。最后,将序列放入接口的方法。

数组


数组映射非常简单。不为 shares[] 或 spreadsheet[] 生成 java类。然而,为结构 ofArrays 生成 java 类。让我们看一下ofArrays.java

清单 13. ofArrays.java

   final public class ofArrays implements
   org.omg.CORBA.portable.IDLEntity
   {
      public
      ofArrays()
      {
      }
   
      public
      ofArrays(int[] _ob_a0,
               String[][] _ob_a1)
      {
          shares = _ob_a0;
          spreadsheet = _ob_a1;
      }
   
      public int[] shares;
      public String[][] spreadsheet;
   }

IDL 数组直接映射到 与 IDL 数组标识同名的 Java数组。数组机制不需要 Java 编程语言支持。我用机制限定,因为ofArray生成三个类:ofArrays.javaofArraysHelper.javaofArraysHolder.java,而 MyString生成两个类:MyStringHolder.javaMyStringHelper.java。上个月的专栏文章(本两部分系列的第一部分,请参阅参考资料获得链接)研究了生成的 holder类,很快我们将详细研究辅助类。但现在,请注意 IDL中的数组是有界限的,ofArrays.java中的数组没有界限。您认为在何处检查数组界限?没错,在结构的辅助类--ofArraysHelper.javaMyStringHelper.java中。在接口方法中打包数组(或结构)以便作为参数使用时,检查它的界限,如果它们超出了界限,将从辅助类的write()方法中抛出 CORBA::MARSHAL 异常。

序列


序列同样工整地映射到 Java 数组。使用序列标识作为 Java数组的名称。映射期间,凡是需要序列类型的地方(例如,方法参数),使用序列元素的映射类型数组,如同我们在 IDL数组示例中看到的。打包有界限的序列作为 IDL操作时,检查它们的界限,如果数组超出界限将发出 IDLCORBA::MARSHAL。下列代码是 testbound 接口的接口操作类,该接口在它的testStub()方法中使用两个序列:

清单 14. testboundOperations 接口

   public interface testboundOperations
   {
      public void
      testStub(int[] inB,
               BoundedHolder outB); 
   }

异常


IDL 异常映射对于 Java编程语言异常来说是如此工整,以至您会认为在设计IDL异常之前曾经参考过 Java异常。它是那么完美和不需更改的映射以至于简化了异常的使用。所以使用它们吧!

应该注意 IDL异常映射与结构非常相似。它们被映射成提供异常和构造器的字段实例变量的Java 类。CORBA 系统异常继承java.lang.RuntimeException。通过扩展 IDLEntity 的org.omg.CORBA.UserException 从java.lang.Exception继承用户定义的异常。

用户异常
在我们的示例中,创建用户异常的名称是 OilSpill:

清单 15. 用户异常 OilSpill

   // user defined exception
   exception OilSpill {
      long leakage;
   };

运行 IDL-to-Java 编译器后,生成 OilSpill 类:

清单 16. OilSpill 类

   final public class OilSpill extends org.omg.CORBA.UserException
   {
       public
       OilSpill() 
       {
           super(OilSpillHelper.id()); 
       }
   
       public
       OilSpill(int _ob_a0) 
       {
           super(OilSpillHelper.id()); 
           leakage = _ob_a0; 
       }
   
       public
       OilSpill(String _ob_reason,
                int _ob_a0) 
       {
           super(OilSpillHelper.id() + " " + _ob_reason); 
           leakage = _ob_a0; 
       }
   
       public int leakage; 
   }

将 OilSpill 映射到指定成 final 并扩展org.omg.CORBA.UserException的 Java类,它有一个名为leakage的公有整型变量和三个构造器:缺省构造器、正常或满构造器、“非常完整”构造器。附加的“非常完整”构造器有附加的初始字符串原因参数,在调用基本UserException 构造器前,将它并置到标识。同样生成普遍存在的 Helper和 Holder 类。

系统异常
CORBA 规范带有整套标准 IDL 系统异常。使用 CORBA若干时间后,您将十分熟悉某些系统异常。系统异常映射到扩展org.omg.CORBA.SystemException的 final 类。每个标准IDL异常 的 Java 类名与它的 IDL 同名,并在软件包org.omg.CORBA中说明。本类包含 IDL主要和次要异常代码和描述异常的字符串。缺省构造器为次要代码提供0,为完整代码提供 COMPLETED_NO,为原因代码提供""。同样有构造器接受原因并在其它字段使用缺省值,和需要指定所有三个参数的构造器相同。org.omg.CORBA.SystemException没有公共构造器,只示例化扩展它的类。

Any


正如几个月前我们讨论的 CORBA 连接,CORBAAny是维护其类型的自描述数据结构。Any让您在运行时使用类型-安全转换函数来抽取和插入预定义的 IDL类型值。可通过在 ORB 上调用create_any()方法来获取Any。Any是很有用的,它是一个与其它类型完全不同的类型,像一个超类型。如何将该事物映射到Java 语言?

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类的组合成了非常灵活的机制。因此让我们进一步研究辅助类。

辅助


现在您不会对 CORBA 有如此多的内容而感到惊奇了。如果 OMG映射器尝试将每件事放入一个生成的文件,它将变得过于庞大和笨重。因此,它们创建辅助类,它是一个用相应类型的名称并附加Helper 作为名称的类。类型 "My" 将有称为 "MyHelper" 的辅助类。

提供的或生成的辅助类包含用不同方式操纵 IDL类型的方法。辅助类提供客户机能用来操纵类型的静态方法。这些包含如上所述的Any插入和抽取的方法,获取库标识,获取类型代码,从流中读取类型或写入类型。对于映射的IDL 接口,辅助类包含用来将 org.omg.CORBA.Object的对象引用造型到基本辅助类型的 narrow() 操作。

结束语


此时,应该清楚要牢固和完全掌握CORBA,需要切实理解编程语言或用来实现 CORBA组件的语言。在上两个专栏文章中,我们已经研究了 IDL-to-Java映射,但还有几个其它映射,例如--C、C++、COBOL、ADA 和 Smalltalk等等(甚至未提到 Java-到-IDL映射,这是个新课题,与我们这里涉及的有些相反,它使 RMI开发人员能够将他们的接口用于 CORBA客户机)。我确信还遗漏了一些,并且 OMG总是添加更多语言(例如,Python 和 C# )的提议。映射对于理解 CORBA及其组件如何系统组合是重要的。

要成功对 Java 语言使用 CORBA 需要彻底理解 IDL-to-Java映射;但是因为要彻底理解映射而付出的努力会在您的项目和 CORBA的成就上得到巨大回报。


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Java technology
ArticleID=53128
ArticleTitle=IDL-to-Java 的映射: 第二部分
publish-date=112000