概述
ORB提供了两个用于存储有关对象信息的在线数据库:接口存储库(InterfaceRepository)与实现存储库(Implementation Repository)。
其中,接口存储库负责对象的IDL接口定义的存储、分布和管理。它使用永久对象来表示IDL信息,使客户对象可以在运行时查阅接口存储库的内容,获得对象实现的IDL接口信息,从而向对象实现发出请求。使用接口存储库中的信息还可以对对象请求中操作的有效性进行检查。接口存储库可以从IDL语言编译器,或者通过自身的数据写入功能,直接控制这些定义。
CORBA通过定义一组类描述了信息如何在接口存储库中组织和检索,这些类的实例即表示存储库中的信息。接口存储库本身作为一个对象存在,应用程序可以像调用其他CORBA对象所提供的操作一样,来调用接口存储库接口中的操作。接口存储库结构灵活,能够跟踪按照类似IDL方式组织的对象集合。2
静态CORBA在软件体系结构中,OMG IDL可作为定义软件划分的通用规范,用IDL定义各种具有属性和方法的接口对象,并通过支持接口描述之间的继承来实现软件重用。
OMG IDL是一种面向对象的接口定义语言,能够支持复杂的数据类型。同时,它是一种说明性语言,而不是编程语言,主要用于指定对象包含方法和属性的接口,使对象行为与对象实现分离。OMG IDL支持的属性主要包括:模块(Module)、接口(Interface)、方法(Method)、属性(Attribute)、继承(Inheritance)和数据类型。这样,就实现了将对象的接口与实现分离,可以用不同的编程语言实现对象,而它们之间又可以进行互操作。当然,需要分别将IDL映射到具体的编程语言,目前,OMG已经为IDL到C,C++,Smalltalk,Ada,COBOL和Java制定了映射标准,最新的CORBA 3.0规范又增加了IDL到Python(一种类似于Perl的脚本语言,可参考http://www.python.org/)的映射标准。
OMG IDL编译器可以根据接口定义来产生分布式对象的客户方的存根(Stub)和服务方的骨架(skeleton)。存根代表客户创建并发出请求,骨架则把请求交给CORBA对象实现。具体来说,静态IDL存根是服务的静态接口,客户程序与它直接相连。存根的作用相当于本地调用,由存根向ORB透明地提供一个接口,以实现对操作参数的编码和解释,并以适当的格式进行传输。存根为客户机提供了这样一种机制,使得客户机能够不关心ORB的存在,而把请求交付给存根,由存根负责对请求参数的封装与发送,以及对返回结果的接收和去封装。
静态IDL骨架是静态IDL存根在服务器端的对应,在请求的接收端提供与存根类似的服务。当ORB接收到请求时,由骨架将请求参数去封装,识别客户所请求的服务,调用服务器中的对象实现,当服务器完成了对请求的处理后,骨架把执行结果封装,并将结果返回给客户程序。
由于存根和骨架都是从用户的接口定义编译而来,所以它们都和具体的接口有关,并且,在请求发生前,存根和骨架早已分别被直接链接到客户程序和对象实现中去。因此,通过存根和骨架的调用被通称为静态调用。
从创建IDL声明到使用预编译器和编译器编译从而生成静态IDL存根、静态IDL骨架等对象的全过程如图所示。
在图中各个步骤的含义如下:
1)用接口定义语言定义对象类。通过接口定义语言,对象可以告知潜在的客户何种操作是可用的以及应该如何调用它。接口定义语言定义了对象的类型、对象的属性、对象输出的方法以及方法的参数。
2)通过语言预编译程序运行接口定义语言文件。典型的CORBA兼容预编译程序可以处理接口定义语言文件,为实现服务器类产生语言框架。
3)增加实现代码。必须在上述框架里进一步提供实现代码,创建服务器类。
4)由编译器编译代码。CORBA兼容编译程序通常能够产生至少三种类型的输出文件,分别是:向接口存储库描述对象的输入文件;IDL所定义的方法的客户存根,这些存根能够被需要通过ORB静态访问IDL定义服务的客户所调用:在服务器上调用这些方法的服务器框架,也称为向上调用方法接口。用户必须提供实现这些服务器类的代码。存根的自动产生功能使开发者摆脱了编写存根程序的负担,也使应用程序变得与特定的ORB实现无关。
5)将类定义与接口存储库绑定。通常可以用相关的实用程序将IDL信息绑定(装入)到接口存储库,这样程序能够在运行时访问。
6)向实现存储库注册运行对象。对象适配器在实现存储库里记录对象标记以及在服务器上实例化的对象类型。实现存储库还知道哪一些对象类在某个特定服务器上得到了支持。ORB利用这个信息去查找某个特定服务器上的活动对象或者请求激活对象。
7)实例化服务器对象。在服务器对象适配器启动时,它可以将服务器对象实例化。这些运行时的对象是服务器应用程序类的实例。CORBA规定了不同的对象适配器策略,用来创建和管理运行对象。
在静态CORBA调用中,要特别注意的问题是存根的管理。客户在调用服务器对象的方法之前,必须同时拥有一个存根和一个对象标记,对象标记的传送相对比较容易实现,而客户对存根的拥有则通常是通过将存根放入客户代码中来实现。不过,新的CORBA规范中规定可以动态地从服务器上下载存根(如果是以Java字节码的方式则很适合下载),也就是说,静态CORBA正在变得越来越“动态”。 2
动态CORBA除了可以通过存根和骨架进行静态调用外,CORBA还支持两种用于动态调用的接口:动态调用接口(Dynamic Invocation Interface,简称DII)和动态骨架接口(Dynamic Skeleton Interface,简称DSI)。DII和DSI可以被分别视为通用存根和通用骨架。它们由ORB直接提供,不依赖于所调用对象的接口。
DII支持客户方的动态请求调用,使客户方应用可以在运行时向任何对象发出请求,动态地选择对象的实现接口和操作。而不像静态调用那样,必须在编译时就知道特定的目标对象的接口信息。DII通过函数create_request根据具体对象实现的IDL接口来动态创建对象请求,其IDL接口信息由接口存储库提供。
DSI是DII在Server方的对应,与DII允许客户不通过存根就可以调用请求类似,DSI允许用户在没有静态骨架信息的条件下来获得对象实现。DSI从进入的消息找出调用的目标对象及相应的方法,并提供运行时的连接机制。
总体来说,动态调用接口使客户可在运行时选择目标对象并动态调用其方法。客户不需要预编译存根,这意味着客户可在调用时发现接口的相关信息。但是,客户具体怎样来发现这些远程对象呢?实际上,有多种机制可以做到这一点,其中,最简单的办法是:给客户提供“字符串化的”的对象标记,然后客户通过把字符串转换成一个活动的对象标记再建立连接。当然,客户也可以利用CORBA名字服务查找对象,或者客户可以通过CORBA的“电话簿黄页”——交易器服务来发现这些对象。
假设用户已获得了想要动态调用的对象的标记,下面来描述一下如何调用这个对象上的远程方法。
1)获得接口名字。CORBA对象是自描述的,它们能够提供相当多有关自己的信息,因此,可以通过get_interface方法向这个对象询问其接口的名字,这个调用将返回一个InterfaceDef对象的引用,该对象是接口存储库中一个描述接口信息的对象。
2)从接口存储库获得方法描述。可以把InterfaceDef用作接口存储库的入口点,可以获得有关接口和它所支持的方法的各种详细信息。CORBA定义了约10个调用,用于接口存储库的浏览访问和对象描述。如,客户可以发出lookup_name以寻找它想调用的方法,然后再发出一个describe方法来取得被找到方法的全部IDL定义。或者发出一个describe_interface方法来获得接口的全部描述,再进一步找到想调用的方法。
3)创建参数列表。CORBA定义了一个自定义的数据结构以传递参数,被称为命名数值列表。用户可以使用一系列方法实现这个列表。
4)创建请求。请求是一个包含方法名称、参变量列表和返回值的CORBA准对象。可以通过调用create_request方法来创建请求。
5)调用请求。可以用以下三种办法中的任何一种调用请求:
用invoke调用发送请求并得到结果。
将控件返回给程序,再通过发送poll_reponse或get_reponse来论询响应。
发送调用可以通过发送Send_oneway被定义一个数据报,此时不需要响应。
这三种方式有时也被称为同步、延迟同步和单向调用。
可以看出,要动态地调用一个方法需要进行相当多的工作,最困难的部分是创建请求,用不同的方法创建和调用远程方法将使这项工作变得更加复杂,如必须权衡复杂度和性能,以得到更多的灵活性。
上述过程中所使用到的各个方法分散于CORBA的四个不同接口中,它们分别是:
1、CORBA::Object:一个准对象接口,定义了每一个CORBA对象必须支持的操作,是所有CORBA对象的根接口,其与动态调用有关的方法包括:getinterface方法、create_request方法、_request方法。
2、CORBA::Request:一个定义远程对象操作的准对象接口。其与动态调用有关的方法包括:add_arg、invoke、send_oneway、send_deferred、get_reponse、poll_reponse等。
3、CORBA::NVList:帮助用户创建参数列表的准对象接口。其与动态调用有关的方法包括:add_item、add_value、get_count、remove、free等。
4、CORBA::ORB:定义通用对象请求代理方法的准对象接口。可以从客户或者服务器实现中调用其提供的方法以操作ORB准对象。与动态请求相关的方法包括:create_list、poll_next_reponse、get_next_reponse等
一般来说,当客户频繁地调用服务器对象而且服务器对象基本上无变化时使用静态调用,当客户很少调用服务器对象或者客户在运行时发现服务器对象时使用动态调用。2