基于TAO(The ACE ORB)的CORBA编程

更新时间:2024-04-01 12:22:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

CORBA Programming with TAO - 1.Start(基本概念)

摘要:

简要介绍CORBA的基本原理,并解释POA、stub、skeleton、servant等重要概念。

一、CORBA及TAO简介

CORBA是一个为简化跨平台应用而提出的规范,它独立于网络协议、编程语言和软硬件平台,支持异构的分布式计算环境和不同编程语言间的对象重用。CORBA可以作为不同平台应用间信息传递的中间件,CORBA通过引入经过充分验证的有效的框架结构和通信手段,最大限度地简化了网络通信相关应用的设计与开发,使得我们可以专注于业务逻辑的实现,而无需关心通信的细节。CORBA曾在无数文章中被称作“软总线”,以表明它作为数据传递通道的基本特性。 现在存在众多CORBA实现,既有商用的ORBacus、VisiBroker,也有一些优秀的开源实现,如:TAO、omniORB、MICO等。由于各实现遵从相同的规范,接口基本一致,所以在熟练应用一种CORBA实现后,转而使用其它实现时,一般不会存在太大的障碍。

TAO(The ACE ORB)是美国华盛顿大学的Douglas C. Schmidt教授领导开发的一个实时CORBA平台,它是一个免费的开放源码项目,用C++语言开发,符合CORBA2.6规范。 支持语言: C++

支持平台: Win32,常见的各种Unix/Linux,实时操作系统如VxWorks等等。在所有的CORBA实现中,TAO支持的平台是最多的。 支持的服务: Naming、Event、Notification、Security、Time、Scheduling、Logging、Lifecycle、Trading、Concurrency、Lifecycle、A/V Streaming、Load balancing等。

本系列文章将以当前最新的ACE-5.5+TAO-1.5+CIAO0.5为例,简要介绍如何应用TAO进行CORBA C++编程,其中部分内容(尤其是编译器配置相关的内容)是Windows平台特有的,但其它大多数信息在各平台上都是相同或者类似的。

二、基本概念

本文不打算深入介绍CORBA相关的理论基础(已有很多书籍、文章讨论了这些内容),但在进入下一主题前,为了便于后续问题的讨论,这里简要介绍一下CORBA的基本原理,并对几个重要的基本概念进行解释,以便为没有相关知识的朋友扫清障碍。下图是CORBA的基本模型:

图中除上面那根灰线(有点浅,看不清的请慢慢找,^_^)以上的两个部分需要我们直接介入外,其他部分基本可以由IDL编译器自动生成或由CORBA实现为我们完成。借助CORBA提供的中间件机制,我们在进行方法调用时,无需考虑被调用对象所在的具体位置,而是像调用一个本地模块的方法一样“直接”与其通信(因此,有些应用中,CORBA也被用于本地进程间通信,以降低各模块间的耦合度,同时也便于系统结构的调整)。 上图主要包括以下几个组成部分:

? Client 即客户程序,Client通过调用服务程序提供的接口实现特定功能。

? Object CORBA Object是一个语言无关的虚拟的实体,它可以由ORB定位,并可被客户程序请求调用,

它最终由某种语言来具体实现,从而成为应用程序的一部分。 ? Servant 即实例化的Object,Servant是组成服务程序,具体为Client提供服务的某个运行中(且被注册到

OA)的C++/Java(或其他语言)对象。Object与Servant是一对多的关系,一个Object可以被实例化多次,

注册到一个或多个OA中为一个或多个Client提供服务,各Servant对应不同的IOR(多个IOR具有相同的Type信息,但唯一标识不同),并且,每个Servant都可以由IOR唯一确定,对于持久Object,该IOR在两次启动同一系统时不会发生变化,除非服务程序所在主机IP或配置信息等发生改变,而对于非持久Object,该IOR在两次启动同一服务程序时,总是变化的(因为其中往往包含一个时间戳)。

? Stub 即桩,或称存根,Stub负责“假扮”Servant为Client提供一个本地化的接口,并负责与ORB进

行交互,完成调用请求的列集(marshaling,即打包)与散集(unmarshaling,即解包)。列集的主要作用是按双方ORB可以识别的方式对需要传递的数据进行格式化,而散集则将收到的数据中还原成上层应用可以理解的内存数据形式,对于远端指针,散集时ORB还负责分配相应的内存空间,并将指针的内容复制到本地内存空间以为本地应用提供一个远端指针的镜像,从而保证本地应用对远端指针的“透明”访问。 ? Skeleton

与Stub对应,负责调用参数在Server端的解包和调用返回的打包。

? ORB Core 屏蔽底层通信细节的中间件,所有远程访问请求在Client和Server端的ORB间被传递,使

得上层应用无需关注平台、语言、网络结构等方面的差异。 ? Object Adapter(OA) OA是ORB与Servant之间的桥梁,主要负责:

1、产生对象引用;

2、激活Servant或反之;

3、将请求分解后传递给对应的Servant;

4、与Skeleton配合完成对Servant提供方法的调用。 OA与Servant也是一对多的关系。

下面的图形进一步说明了ORB、OA、Skeleton、Servant之间的关系:

关于OA的更为详细的信息,详见参考1。

三、编译TAO

TAO可以从如下地址获得:

http://deuce.doc.wustl.edu/Download.html

由于TAO是以源码包的形式发布的,因此,获得TAO源码包后,需要自行在所使用的平台上编译TAO。由于下一系列《体验CORBA对象模型CCM》同样涉及CIAO的编译问题,因此下面将单独用一节内容来介绍如何在Windows平台上编译ACE+TAO+CIAO源码包。

参考:

1、Irfan Pyarali, Douglas C. Schmidt. An Overview of the CORBA Portable Object Adapter.

http://www.cs.wustl.edu/~schmidt/PDF/POA.pdf

2、Michi Henning, Steve Vinoski. Advanced CORBA Programming with C++. Addison-Wesley, 1999. 3、Mentoya. TAO使用指南. http://www.huihoo.com/ace_tao/tao_guide.html

CORBA Programming with TAO - 2.Compile Source Code(编译ACE/TAO/CIAO)

摘要:

简要介绍如何在Windows平台上编译ACE/TAO/CIAO,及编译过程中需注意的问题。

正文:

随着ACE/TAO/CIAO研究者研究的不断深入,整个平台变得越来越庞大,涉及的工程及目录数目也越来越多,下面简要介绍一下如何在Windows平台上编译ACE/TAO/CIAO,其过程在其它平台上是类似的。

一、目录结构

在开始之前,先简要介绍一下整个产品的目录结构。ACE(Adaptive Communication Environment)是整个ACE/TAO/CIAO体系的基础,由操作系统适配层、对并发/进程间通信/虚拟内存管理等的OO封装、ACE框架、

网络服务组件等几个部分组成;而TAO(The ACE ORB)则是在ACE基础上建立起来的一个CORBA ORB实现;CIAO(Component Integrated ACE ORB)则是在TAO基础上的一个CORBA Component Model实现,同时CIAO也是目前相对比较成熟的一个CCM实现。

正是由于三者之间的上述关系,在目录结构上整个源码包也采用了类似下面的结构: ACE_ROOT ├─ace ├─docs ├─... ├─TAO │ ├─tao │ ├─docs

│ ├─orbsvcs(TAO实现的众多CORBA服务被放置在这个目录下) │ ├─... │ ├─CIAO │ │ ├─ciao │ │ ├─docs

│ │ ├─DAnCE │ │ ├─RACE │ │ ├─...

三个产品的帮助文档,可以在相应节点下的docs目录中找到,其中除了对相应产品特定模块或服务的说明外,还包括一些不错的Tutorial,对于我们理解相应产品很有帮助(ACE/TAO/CIAO的较完整的文档是收费的,这些随源码发布的文档虽然少,但仍颇为有用)。

二、关于编译器

可以使用VC6/7.1/8来编译ACE/TAO/CIAO,不推荐使用VC7,但由于VC6的Project Settings不支持环境变量,会对我们使用CIAO带来一些麻烦(如需手动修改自动生成的工程文件),因此,笔者推荐读者使用VC8,即Visual C++ 2005来进行编译(注:现在VC++ 2005 Express可以从MS的网站上免费获得,但若安装该版本的话,需按照http://lab.msdn.microsoft.com/express/visualc/usingpsdk/default.aspx的指示安装SDK并进行相关设置)。VC8在调试器的设计上有了很大改进,同时对C++标准的支持也有了很大进步(但也变得非常耗资源),因此,下面的讨论将基于ACE-5.5+TAO-1.5+CIAO-0.5 + VC2005 Express,不保证在其它ACE或编译器版本下完全一致。

三、基本设置

好啦,废话说完了,重新回到我们的主题。 首先,设置好如下环境变量: ACE_ROOT TAO_ROOT CIAO_ROOT

分别指向ACE根目录,TAO根目录(即?E_ROOT%/TAO),CIAO根目录(即%TAO_ROOT%/CIAO)。 在?E_ROOT%/ace目录下添加一个文件config.h,并在其中添加如下内容:

/* -*- C++ -*- */

//============================================================================= /**

* @file config.h * */

//============================================================================= #define ACE_HAS_STANDARD_CPP_LIBRARY 1

#include \

若是在其它平台上进行编译,还需要添加文件:

$ACE_ROOT/include/makeinclude/platform_macros.GNU 具体方法见ACE Installation Guide。

此外,为了便于今后使用ACE/TAO/CIAO,应当将 ?E_ROOT%/bin(部分通用工具的输出目录)

?E_ROOT%/lib(所有.lib、.dll文件的输出目录) 添加到PATH环境变量中。

并且,需打开VC8,选择Tools -> Options -> Projects and Solutions -> VC++ Directories,添加如下编译器环境变量:

Executable files: $(ACE_ROOT)\\bin $(CIAO_ROOT)\\bin

Include files: $(ACE_ROOT) $(TAO_ROOT)

$(TAO_ROOT)\\orbsvcs $(CIAO_ROOT) $(CIAO_ROOT)\\ciao

Library files: $(ACE_ROOT)\\lib

四、开始编译

现在可以开始编译了,但是,在笔者的Win2000中编译ACE5.5/TAO1.5/CIAO0.5时遇到的第一个问题就是:源码包中的VC Workspace/Solution打开之后一片空白,一个工程也没有。(上一版本没有这个问题。最终通过比较源码包中的.sln文件和通过下面的方法得到的.sln文件,发现其实是由于文件是Unix格式造成的) 几经周折,终于想到了一个办法:自己运行mwc.pl重新生成工程文件。 要运行mwc.pl自然需要安装Perl,安装过程略。

下面简单说说mwc的工作原理:

mwc.pl根据参数中指定的.mwc文件的内容到相应目录下查找.mpc文件,在没有指定.mwc文件或.mwc文件中没有指定查找目录的情况下会尝试进入各子目录,查找.mpc文件(相当于一个makefile文件),按照.mpc文件生成工程信息,若连一个.mpc文件也找不到,就将当前目录下所有可识别的文件当作源文件,产生一个默认的工程文件。最终,mwc.pl将所有工程文件合并到一个独立的Solution/Workspace。 .mwc文件的结构其实很简单,下面是%TAO_ROOT%/TAO_ACE.mwc的全部内容:

// -*- MPC -*-

// TAO_ACE.mwc,v 1.1 2006/02/16 09:20:48 jwillemsen Exp

workspace { ../ace

../apps/gperf/src ../ACEXML/common

../ACEXML/parser/parser ../ACEXML/apps/svcconf

../Kokyu/Kokyu.mpc ../protocols tao

TAO_IDL utils orbsvcs exclude {

orbsvcs/tests

orbsvcs/performance-tests orbsvcs/examples } }

其作用就是告诉mwc.pl到哪些目录下去查找.mpc文件(若没有指定,则表示应检查所有子目录),而其中的exclude则表示应略过那些目录,如果你愿意,完全可以自己编写.mwc文件(不过,工程具体的描述文件.mpc则比较复杂,一言难尽,想一探究竟的话,最好还是看看MPC的说明文档

http://www.cs.wustl.edu/~schmidt/ACE_wrappers/MPC/USAGE或http://downloads.ociweb.com/MPC/MakeProjectCreator.pdf)。

对mwc.pl的工作原理有了基本的了解后,就可以运行mwc.pl生成工程文件了。执行mwc.pl的办法很简单,如要生成使用CIAO的基本工程,可以在%CIAO_ROOT%下运行: mwc.pl -type vc8 CIAO_TAO.mwc

上述命令将产生一个名为CIAO_TAO.sln的Solution文件,有了.sln文件,就可以正式进行编译了。

应用TAO进行CORBA应用开发,下面的几个工程是必须的:

? TAO_IDL_EXE(即tao_idl,也就是我们的idl编译器,自然少不了,并且后续工程也需要用到该编译器。

该工程依赖于ACE、TAO_IDL_BE、TAO_IDL_FE、gperf等多个工程,编译TAO_IDL_EXE是会自动先编译这几个工程)

? PortableServer(即POA库,编写CORBA应用程序哪能离得开POA,该工程依赖于ACE、TAO、CodeSet、

AnyTypeCode等工程) ? Naming_Service(即命名服务,这是对于我们开发CORBA应用相当有用的一个基本服务,该工程依赖的

其它工程就更多了,就不再一一列举了) 而要使用CIAO,则还必须编译%CIAO_ROOT%/DAnCE下的多个工程,这些工程包括: ? ExecutionManager ? NodeManager ? Plan_Launcher ? NodeApplication

这几个工程是部署与配置引擎(Deployment And Configuration Engine,DAnCE)的一部分,负责处理面向组件的应用的部署与配置。

由于工程之间存在的复杂依赖关系,你只需编译最后这三个工程,就可以自动完成约30个工程的编译工作。编译完以上工程后,你的?E_ROOT%/lib和?E_ROOT%/bin下会多出来一些.lib、.dll和.exe文件。作为一个ORB服务,Name_Service.exe被放在其对应的子目录%TAO_ROOT%/orbsvcs/Name_Service下。

注:

1、为了编译CIAO_XML_Utils,需要安装Xerces-C++ 2.7.0,但http://xml.apache.org/xerces-c/提供的下载采用VC6编译,该版本不能用于VC7及以上版本,如果你用的不是VC6,则需要下载源码重新编译。如果你用的是VC++ 2005 Express,由于没有ATL及MFC,在编译时会遇到一些麻烦,可以在SDK Dir\\include下自行添加一个WINRES.h,在其中添加如下代码(两行,空行是必须的): #include

2、如果要试用CIAO,还需用到cidl编译器cidlc,该工具可以从http://www.dre.vanderbilt.edu/cidlc/binary/下载。

参考:

1. Building and Installing ACE and Its Auxiliary Libraries and Services.

http://www.cs.wustl.edu/~schmidt/ACE_wrappers/ACE-INSTALL.html

2. Building and Installing CIAO.

http://www.cs.wustl.edu/~schmidt/ACE_wrappers/TAO/CIAO/CIAO-INSTALL.html 3. The Makefile, Project, and Workspace Creator (MPC).

http://downloads.ociweb.com/MPC/MakeProjectCreator.pdf CORBA Programming with TAO - 3.IDL Data Type(数据类型与Mapping) 摘要: 简要介绍TAO支持的IDL数据类型及其C++ Mapping关系。 一、简单基本数据类型(Simple Basic Types) TAO支持以下简单基本数据类型(%TAO_ROOT%/tao/Basic_Types.h): IDL boolean char octet short unsigned short long unsigned long long long wchar float double long double C++ CORBA::Boolean CORBA::Char CORBA::Octet CORBA::Short CORBA::UShort CORBA::Long CORBA::ULong CORBA::LongLong CORBA::WChar CORBA::Float CORBA::Double CORBA::LongDouble unsigned long long CORBA::ULongLong 以上各简单基本类型对应的C++类型只是对应平台上基本类型的typedef(Java虚拟机之所以能够在各平台上保证统一的数据长度,其实现原理也是如此),但在编写应用程序时,为了保证程序的可移植性,应该总是使用CORBA命名空间中的类型标识。此外,需要注意:在上面的所有类型中,没有我们熟悉的C++基本类型byte(被Octet取代)、int(被Long取代)。 二、复杂基本数据类型(Complex Basic Types) 除以上简单基本数据类型外,TAO还支持以下复杂基本数据类型: IDL string any C++ CORBA::TAO_String_Manager(%TAO_ROOT%/tao/Managed_Types.h) CORBA::Any(%TAO_ROOT%/tao/AnyTypeCode/Any.h) wstring CORBA::TAO_WString_Manager(%TAO_ROOT%/tao/Managed_Types.h) 其中两种String类型在使用时需要注意: 1、应该总是使用TAO提供的(也是CORBA规范规定的)如下字符串操作函数: char * string_alloc(ULong len); char * string_dup(const char *); void string_free(char *);

WChar * wstring_alloc(ULong len); WChar * wstring_dup(const WChar *); void wstring_free(WChar *);

来进行字符串操作,以提高系统的可移植性。

2、(w)string_alloc/(w)string_dup后必须调用(w)string_free来释放分配的资源,为了避免忘记(w)string_free带来的麻烦,有些情况下,我们可以考虑使用String_var类型(String_var是String类对应的智能指针类,除了TAO本身支持的智能指针类型外,tao_idl在生成代码时会自动为每个Object添加一个对应的var类型)。 3、(w)string_alloc(n)会分配n+1个字符(不是字节)空间。 以下是一个简单的字符串操作的例子:

#include

#include

using namespace std;

int main() {

char * p = CORBA::string_alloc(5); // Allocates 6 bytes }

cout << p << endl; CORBA::string_free(p);

CORBA::String_var s = CORBA::string_dup(\); cout << s.in() << endl; return 0;

strcpy(p, \); // OK, \

注:如果你在编译该程序时遇到困难,请在阅读完本系列的第五篇文章后再来测试该程序(下同)。

CORBA::Any与Windows开发中常用的VARIANT类型相当,可以在其中存入任意其他类型的数据,但Any更具有面向对象的风格。在Windows应用中向VARIANT写入信息时需要先设置写入的数据类型,而从中读出数据时往往需要用switch进行判断,相比之下Any的使用就简单多了,我们可以通过<=操作符来向Any类型变量写入信息,而通过>=操作符从Any变量中读出信息。与从VARIANT中解析数据不同的是,由于重载的>=操作符返回的是一个表示转换成功或者失败的标志,因此,我们总是使用if...else而不是switch来对解析结果进行判断。下面是一个使用Any的例子:

#include

#include using namespace std;

int main() {

CORBA::Any a;

CORBA::Octet o; CORBA::Long l;

a <<= CORBA::Long(1); // a contains 1 with CORBA::Long type if (a >>= l) {

cout << \ << l << endl; } else { }

cout << \ << endl;

a <<= CORBA::Any::from_octet(65); // a contains 64 with CORBA::Octet type if (a >>= CORBA::Any::to_octet(o)) { cout << \ << o << endl; } else {

cout << \ << endl; }

return 0; }

注:过度使用Any类型将对程序的处理性能造成影响。

此外,CORBA规范还规定了一种不太常用的数据类型:CORBA::Fixed,它是一种特殊的浮点类型,在构造fixed类型变量时必须指定两个参数:总位数(不含小数点)与精度,而CORBA::Fixed则提供了多种从其它基本类型构造Fixed类型的方法,你甚至可以从字符串类型构造一个Fixed类型变量。但是,目前TAO尚不支持Fixed类型。

三、构造类型(Constructed Types)

除了上面的基本类型外,我们还可以在idl中使用struct、sequence、union、array等几种构造类型: ? struct

struct与C语言中的struct基本上是等价的,其中只能包含变量定义,不能定义方法,对定义的变量进行初始化,或定义union型变量。 ? sequence

sequence与STL中的vector比较类似,可以用它来存储相同类型的变量,并且可以“自由”扩充,因此,sequence往往可以在传递“变长”参数时发挥重要作用。经过idl处理,sequence会被映射成相应的类。下面举一个sequence的例子:

1、首先,定义如下的idl文件:

typedef sequence StrSeq;

将其存为strseq.idl,并在控制台下对其进行编译,命令如下: tao_idl -Sc strseq.idl

编译后,我们将得到一个名为strseq.h的头文件;

2、新建一控制台工程,并在主文件中添加如下代码(注:此例全部代码取自):

#include \

#include

using namespace std;

int main() {

const char * values[] = { \, \, \, \ }; }

? union

idl中的union类型与C++中的union不是等价的,它实际上被映射成了对应的class,同时在使用上与C++中的union也有极大区别。以下面的idl为例:

union U switch (char) { case 'L':

long long_mem; case 'c': };

其意义可以解释为:

类型U可以用于存放三种类型的值:Long型、Char型、Short型,你可以分别用long_mem、char_mem、short_mem取出这些值。当使用一个Long型变量初始化该union变量时,类型标志为L(或者反过来,当类型标志为L时,可以在其中存放一个Long型的值);当用一个Char型变量初始化该union变量时,类型标志为c或者C;当用一个Short型初始化该union变量时,类型标志为其它值。

case 'C':

char char_mem; default:

short short_mem;

StrSeq myseq; // Create empty sequence // Create four empty strings myseq.length(4);

for (CORBA::ULong i = 0; i < myseq.length(); i++) myseq[i] = values[i]; // Deep copy // Print current contents

for (CORBA::ULong j = 0; j < myseq.length(); j++)

cout << \ << j << \ << myseq[j].in() << \ << endl; cout << endl;

// Change second element (deallocates \ myseq[1] = CORBA::string_dup(\); // Truncate to three elements

myseq.length(3); // Deallocates \

// Grow to five elements (add two empty strings) myseq.length(5);

// Initialize appended elements myseq[3] = CORBA::string_dup(\); myseq[4] = CORBA::string_dup(\);

// Print contents once more

for (CORBA::ULong k = 0; k < myseq.length(); k++)

cout << \ << k << \ << myseq[k].in() << \ << endl; return 0;

但是,在使用union类型时需要特别注意:不要尝试将类型标志设置为一种类型(通过_d方法),而用另一种类型去初始化它;或者,反过来,用一种类型去初始化它,然后又尝试将类型标志修改为其它值。 下面是一个使用上述idl定义的例子:

#include using namespace std;

#include

int main() {

U my_u; // 'my_u' is not initialized

// my_u._d('c');

my_u.long_mem(99); // Activate long_mem

assert(my_u._d() == 'L'); // Verify discriminator assert(my_u.long_mem() == 99); // Verify value // my_u._d('c');

cout << my_u.char_mem() << endl; }

注:请自行添加相关头文件。

当union中还包括其他变长类型时,情况将变得很复杂,但使用上并没有太大差异,这里就不深入讨论了,感兴趣的朋友可以阅读参考1的相关章节。 ? array

array并不是一个idl所使用的关键字,它表示的是普通定长数组,它被映射成C++代码时也是一个普通的定长数组,因此,其用法比较简单。下面是一个使用array的idl的例子:

typedef float FloatArray[4];

typedef string StrArray[15][10]; struct S {

string s_mem;

long l_mem;

};

typedef S StructArray[20];

相关测试代码如下:

#include using namespace std;

int main() {

FloatArray my_f = { 1.0, 2.0, 3.0 };

my_f[3] = my_f[2]; StrArray my_str;

my_str[0][0] = CORBA::string_dup(\); // Transfers ownership my_str[0][1] = my_str[0][0]; // Deep copy

}

StructArray my_s;

my_s[0].s_mem = CORBA::string_dup(\); // Transfers ownership my_s[0].l_mem = 5;

注:请自行添加相关头文件。

参考:

1. Michi Henning, Steve Vinoski. Advanced CORBA Programming with C++. Addison-Wesley, 1999. 2. OMG. C++ Language Mapping, v1.1. http://www.omg.org/cgi-bin/apps/doc?formal/03-06-03.pdf

CORBA Programming with TAO - 4.Basic Functions(常用基本方法解析)

摘要:

简要介绍CORBA规范定义的几个常用基本方法的功能及应用中需要注意的问题。

正文:

idl编译器会为每个在idl中声明的interface生成一个对应的代理基类: class InterfaceName; 以及两个对象引用类型: InterfaceName_ptr 和

InterfaceName_var

前者是一个指针类型,其定义往往是:

typedef InterfaceName* InterfaceName;

因此,不作过多讨论。

InterfaceName_var则是一个智能指针类,通过使用智能指针类,免去了我们手工维护指针引用计数的工作,大大简化了应用程序的编写。

下面重点对代理基类和_var智能指针类中的几个基本方法进行简单说明。

一、代理基类中定义的几个基本方法

每个代理基类(以及CORBA::Object)都提供如下几个静态方法: ? _duplicate

返回一个参数指针的拷贝(为了免去对象深拷贝所引起的消耗,实际是通过调整引用计数的方法实现的) ? _nil

返回相应接口类型的空引用 ? _narrow

尝试将参数指针转型为目标指针,若转换失败,则返回目标类型的空引用。 除了以上静态方法,每个代理类还包括两个方法: ? _is_a

该方法接收一个类型id信息const char *type_id,并返回一个CORBA::Boolean,用于判断某个引用是否是type_id所指示的类型。

? _this

该方法返回当前对象的拷贝的引用。 ? _add_ref/_remove_ref

用于增加引用计数和减小引用计数,在我们编写接口实现时可能会用到(客户程序中无法使用这两个方法,因为只有Skeleton代码中会生成这两个方法,客户程序代码也没有必要使用这两个方法来维护引用计数)。

此外,在CORBA命名空间中还定义了: ? CORBA::is_nil 判断某个ptr是否为空 ? CORBA::release

释放参数对象。

而为了比较两个引用是否相同,CORBA::Object中定义了方法: ? is_equivalent

按照CORBA规范,不使用以上基本方法对CORBA对象或对象指针进行比较、类型转换、非空测试等所产生的行为都是未定义的。

这几个方法本身比较简单,并且后续的文章中将看到上述各基本方法的使用,这里就先不举例了。

二、_var智能指针类中的基本方法

按照CORBA规范,每个_var智能指针类都包括如下几个方法: ? in ? out ? inout ? _retn

? ptr

掌握这几个方法的最简单的方法是学习CORBA::String_var类的实现。CORBA::String_var是CORBA::String类的智能指针类,其中封装了一个char*指针。我们来看看TAO是如何实现这几个方法的(见%TAO_ROOT%/tao/CORBA_String.inl):

ACE_INLINE const char *

CORBA::String_var::in (void) const {

return this->ptr_; }

ACE_INLINE char *&

CORBA::String_var::inout (void) {

return this->ptr_; }

ACE_INLINE char *&

CORBA::String_var::out (void) {

CORBA::string_free (this->ptr_); this->ptr_ = 0; return this->ptr_; }

ACE_INLINE char *

CORBA::String_var::_retn (void) {

char *temp = this->ptr_; this->ptr_ = 0; return temp; }

/// TAO extension.

ACE_INLINE char *

CORBA::String_var::ptr (void) {

return this->ptr_; }

表面看来似乎没有什么值得注意的,只是返回了几个不同类型的指针:in方法返回一个指针(因仅作为传入参数),inout方法返回一个指针的引用(因不仅要作我传入参数,还要通过函数调用修改其内容),out方法同样返回一个指针的引用(因需要通过函数调用修改其内容),inout方法返回一个指针(因仅作为返回参数,不能修改),ptr也返回一个普通指针,同样也不能修改内容。

但仔细看一看_retn的实现,你会发现,该方法首先保存了指针的内容,然后将指针清0(这样当String_var被释放时就不会free原来的地址空间),最后将该指针返回。也就是说,通过该方法调用,String_var对象所指向的地址空间的控制权被交给了调用该方法的一方,同样,释放地址空间的工作也应该由调用该方法的一方来完成。

而对于out方法,为了保证原指针被安全释放,该方法先释放原来指向的地址空间,并将指针清0,最后返回该指针的引用。因此,被调用的方法内部应负责为该对象分配空间,而释放该空间的工作则是由调用方完成的。 其他对象上述方法的实现与String_var在原理上是基本一致的,只是IDL编译器tao_idl在生成stub和skeleton代码时会分别为定长结构体和变长结构体应用不同的类模板,从而生成不同类型的_var类。对于定长结构体,应用的类模板为TAO_Fixed_Var_T,而对于变长结构体,应用的类模板为TAO_Var_Var_T。二者的区别在于对于定长结构体而言,_retn和out方法与inout方法的实现是一样的,没有清0的过程,以下是TAO_Fixed_Var_T模板类中的相关代码:

// Mapping for fixed size. template ACE_INLINE

T &

TAO_Fixed_Var_T::out (void) {

return *this->ptr_; }

template ACE_INLINE

T

TAO_Fixed_Var_T::_retn (void) {

return *this->ptr_; }

关于_var类的更多信息,可参考%TAO_ROOT%/tao/VarOut_T.inl,或:

http://www.dre.vanderbilt.edu/Doxygen/Current/html/tao/classTAO__Var__Var__T.html http://www.dre.vanderbilt.edu/Doxygen/Current/html/tao/classTAO__Fixed__Var__T.html

三、典型问题解析

上面简单介绍了CORBA编程中常用的几个基本方法,下面在此基础上对内存管理相关的几个问题进行简要分析。

? 包含变长成员变量的结构体的内存释放问题 以下面的idl为例:

struct DemoStruct { string name_; };

对于如下的代码:

int main() { }

由于我们使用了_var智能指针类,通过new动态分配的空间会在demo对象被销毁时被释放,但是其中通过CORBA::string_dup为成员变量name_所分配的空间是否会泄漏呢?

答案是:不会。这是因为idl文件中结构体的string成员变量经过tao_idl编译后,被映射为TAO_String_Manager,这种类型与String_var基本是一样的,只是String_var不带任何参数的构造函数会将指针ptr_初始化为0,而TAO_String_Manager不带任何参数的构造函数则会将ptr_初始化成一个空字符串,具体可见%TAO_ROOT%/tao/Managed_Types.i及%TAO_ROOT%/tao/CORBA_String.inl。

因此,当结构体被释放时,我们就无需为其中的string的释放问题担心。对于其它包含变长类型的结构体而言,情况是一样的。

? _var类使用的误区 在使用

String_var

时有一个问题需要注意,String_var

提供了三个构造函数

//...

DemoStruct_var demo = new DemoStruct; demo.name_ = CORBA::string_dup(\); //...

(见%TAO_ROOT%/tao/CORBA_String.h):

CORBA::String_var::String_var (char *p) : ptr_ (p) { }

ACE_INLINE

CORBA::String_var::String_var (const char *p) : ptr_ (CORBA::string_dup (p)) { }

CORBA::String_var::String_var (const CORBA::String_var& r) {

this->ptr_ = CORBA::string_dup (r.ptr_); }

第一个构造函数仅对指针p进行浅拷贝,保存到内部的ptr_中,而后面两个则通过深拷贝来构造String_var对象。

两种不同的构造方式区别虽然比较小,但可能引起一些十分隐蔽的问题。如下面的代码就存在问题:

String_var str(\);

因为上述代码会使用第一个而不是第二个构造函数来构造str,从而使得str获得静态地址空间\的控制权,并在str被析构时尝试释放该空间,这显然是错误的。要避免该错误,我们应该总是强制使用第二个构造函数,或在构造Stirng_var对象前主动复制String的内容,如:

String_var str1((const char*)\);

String_var str2(CORBA::string_dup(\));

除非你清楚地知道第一个构造函数是你需要的。 对于其它_var类型而言,不存在与上面第二种构造方式等价的构造函数,我们总是使用第一种形式的构造函数,即新构造的_var对象会获得指针的控制权。

与上面第三种构造方式类似,如果你传入的是一个_var引用,则使用的是如下的构造函数:

template

TAO_Var_Base_T::TAO_Var_Base_T (const TAO_Var_Base_T & p) : ptr_ (p.ptr_ ? new T (*p.ptr_) : 0) { }

该构造函数会对传入的_var引用进行深拷贝。 ? 对远程方法调用内存管理问题的解释

CORBA内存分配/释放的原则很简单:各自负责自己分配空间的释放,C/S两端内存的分配与释放(以及更新)不会自动通知另一方。

对于Client代码而言,由Client代码负责释放的空间还包括ORB在unmarshalling期间创建的Server的指针的镜像,即Server端指针的本地拷贝,这些空间可能是out/inout参数或者作为接口方法的返回值通过方法调用获得的。

这在理解上应该没有什么问题。但是,其中对于Server方的内存管理,我们没有考虑。以如下代码为例:

DemoStruct* DemoIntf::foo() {

DemoStruct_var var = new DemoStruct;

}

//...

return var._retn();

当函数返回时,Client方ORB会通过unmarshalling创建一个该指针所指内存区域的镜像,然后访问该指针,最后由Client方负责该区域的释放;但是我们说过,C/S双方分配和释放内存并不会自动通知另一方,对于Server方而言,由于_retn方法会释放原_var对象对其ptr_指针所指地址空间的控制权,原来由_var对象所管理的空间似乎变得失去了控制,那么这一部分内存是否会泄漏呢?

同样,答案是不会,这是因为ORB在将该指针进行marshalling并传递给Client后,会负责指针所指空间的释放,同样对于in、out、inout参数的管理也是类似的。

结合上面对Server方内存管理方式的讨论,我们来看看下面的例子。 对于如下的idl文件:

struct DemoStruct { string name_; };

interface DemoIntf { DemoStruct get(); };

有类似下面的实现:

class DemoIntf : ... {

private:

DemoStruct_var demo_; public: };

上述实现初看起来可以正常工作,但是根据上面的讨论,ORB会释放返回值指针的地址空间,这将导致demo_被意外释放,最终导致错误的发生。因此,我们应该将实现代码改为:

DemoStruct* get() {

::Device::DeviceID_var dev_id = new ::Device::DeviceID; dev_id->device_name = CORBA::string_dup(id_->device_name);

return dev_id._retn(); }

而客户方代码的代码是这样的:

::Device::DeviceID_var device_id = this->device_id();

DemoStruct* get() { return demo_; }

参考:

1. Michi Henning, Steve Vinoski. Advanced CORBA Programming with C++. Addison-Wesley, 1999.

2. Douglas C. Schmidt & Bala Natarajan. CORBA Tutorial. http://www.cs.wustl.edu/~schmidt/PDF/corba4.pdf

CORBA Programming with TAO - 5.Hello World(简单实例)

摘要:

前面对CORBA编程的基础知识进行了简单介绍,本篇中,我们将实例讲解如何利用TAO中进行CORBA编程。

一、编写IDL

1、建一个空的Solution,然后在该Solution目录下新建一个名为idl的Win32 Static Library工程(对于一般的TAO工程,无需单独为idl建一个Project,但由于我们要重用该idl,所以单独为其建立一个工程比较便于管理)。 然后新建一个文本文件,添加如下内容:

#ifndef __ECHO_IDL__ #define __ECHO_IDL__

interface Echo {

string echoString(in string mesg); };

#endif // __ECHO_IDL__

将该文件命名为echo.idl,保存在idl工程目录下,并将其添加到idl工程中。

2、在echo.idl上点击右键,选择Properties,将其编译工具由默认的midl改为Custom Build Tool,选择Apply,在Custom Build Step标签页Command栏内填入:

tao_idl -Sc $(InputFileName)

Outputs栏填入:

echoC.h echoC.cpp echoC.inl echoS.h

echoS.cpp echoS.inl

点击OK,保存设置,在echo.idl上点击Compile编译该idl文件,编译后,将在idl工程目录下产生上述各文件。 将echoC.h/echoC.cpp/echoS.h/echoS.cpp添加到idl工程中,在idl工程上点击右键,选择Properties,在Librarian下添加如下Additional Dependencies: aced.lib taod.lib

TAO_AnyTypeCoded.lib 按F7编译上述工程。

二、直接通过IOR访问Servant

新建一个eg1_svr工程,添加到当前Solution,在该工程中添加一个名为eg1_svr.cpp的文件,其内容如下:

#include

#include \ using namespace std;

class Echo_i : public POA_Echo,

public PortableServer::RefCountServantBase {

public: };

char* Echo_i::echoString(const char* mesg) {

cerr << \ << mesg << endl; return CORBA::string_dup(mesg); inline Echo_i() {} virtual ~Echo_i() {}

virtual char* echoString(const char* mesg);

}

////////////////////////////////////////////////////////////////////// int main(int argc, char** argv) {

try { }

catch(CORBA::SystemException&) {

cerr << \ << endl; }

catch(CORBA::Exception&) {

cerr << \ << endl; }

catch(...) {

cerr << \ << endl;

// initialize ORB

CORBA::ORB_var orb = CORBA::ORB_init(argc, argv); // find RootPOA

CORBA::Object_var obj = orb->resolve_initial_references(\); PortableServer::POA_var poa = PortableServer::POA::_narrow(obj); // create servant object and activate it Echo_i* myecho = new Echo_i();

PortableServer::ObjectId_var myechoid = poa->activate_object(myecho); // Obtain a reference to the object, and print it out as a // stringified IOR.

obj = myecho->_this();

CORBA::String_var sior(orb->object_to_string(obj)); cerr << (char*)sior << endl; myecho->_remove_ref();

// find and activate POAManager

PortableServer::POAManager_var pman = poa->the_POAManager(); pman->activate(); orb->run();

}

}

return 0;

使该工程依赖于前面的工程idl。由于这是一个Server程序,需要链接TAO_PortableServerd.lib。 按F7,编译该工程。

新建一个eg1_cli工程,添加到当前Solution,在该工程中添加一个名为eg1_cli.cpp的文件,其内容如下:

#include #include \ using namespace std;

static void hello(Echo_ptr e)

{

CORBA::String_var src = (const char*) \;

CORBA::String_var dest = e->echoString(src);

cerr << \ << (char*)src << \ << endl

<< \ << (char*)dest <<\ << endl; }

////////////////////////////////////////////////////////////////////// int main(int argc, char* argv[]) { try {

// initialize orb

CORBA::ORB_var orb = CORBA::ORB_init(argc, argv); // check arguments

if (argc != 2) {

cerr << \ << endl; throw 1; }

// Obtain reference from servant IOR

CORBA::Object_var obj = orb->string_to_object(argv[1]); Echo_var echoref = Echo::_narrow(obj);

if( CORBA::is_nil(echoref) ) {

cerr << \ << endl; return 1; }

for (CORBA::ULong count=0; count<10; count++) // communicate with servant

hello(echoref);

orb->destroy(); }

catch(CORBA::COMM_FAILURE&) {

cerr << \ }

<< \ << endl;

catch(CORBA::SystemException&) {

cerr << \ << endl;

}

catch(CORBA::Exception&) {

cerr << \ << endl; }

catch(...) {

cerr << \ << endl; }

return 0; }

使该工程依赖于前面的工程idl,编译该工程。

代码比较简单,请自行参照注释阅读源代码。

三、运行

运行时应先启动eg1_svr,该程序会在标准输出上输出一长串字符,即servant的IOR信息,我们有了该信息就可以通过orb->string_to_object(...)连接到相应的servant了。

现在可以启动客户程序eg1_cli了,启动eg1_cli时应将上面获得的IOR串作为参数传递给该程序(注意,从Console上拷贝下来的时候会折行,将其拷贝到文本编辑器中去掉折行再拷出来即可)。

附:本文示例源代码 参考:

1. Duncan Grisby, Sai-Lai Lo, David Riddoch. The omniORB Version4.0 User's Guide.

http://omniorb.sourceforge.net/omni40/omniORB.pdf

CORBA Programming with TAO - 6.Naming Service(使用命名服务)

摘要:

命名服务(Naming Service)是CORBA定义的诸多服务中比较常用的一个, Naming Service提供的助记符解析服务使得应用程序可以借助助记符,而不是一大串IOR来访问到相应的Servant。

一、简介

直接将IOR串在程序间传递的做法往往比较不方便,特别是,对于非持久servant来说,每次重启Server程序时,这个IOR都会发生变化,在使用上比较不便,而且,当我们需要访问的servant不是一个,而是多个时,使用IOR就更加不便了。为此,CORBA定义了Naming Service服务,命名服务(Naming Service,不过大家叫的更多的是命名服务)是CORBA最基本的服务之一。Server程序将servant注册到Naming Service中,Client程序就可以借助助记符,而不是一大串IOR来访问到相应的Servant了。

TAO对标准的名字服务进行了扩展,使得可以利用multicast来得到名字服务。此外,TAO还提供了NT_Naming_Service,可以将名字服务注册为NT服务进行管理。

二、启动Naming Service

名字服务的文件名为:Naming_Service.exe,它的启动参数有: % Naming_Service [-ORBNameServicePort nsport] [-o ior_output_file] [-p pid_file_name] [-s context_size]

[-t time]

[-f persitence_file_name]

[-b base_address]

[-m (1=enable multicast responses,0=disable(default)]

-ORBNameServicePort nsport:指定名字服务监听multicast包的端口号。只有在使用了'-m 1'时才有效.

-o ior_output_file: 指定文件名,用来保存名字服务的根上下文的IOR. -p pid_file_name: 指定文件名,用来保存名字服务程序的进程id

-s context_size: 当创建上下文时, 指定新上下文使用的哈希表的大小,默认1024 -t time: 名字服务监听的时间,单位:秒。默认: 无限

-f persistence_file_name: 指定文件名,用来存放名字服务中被绑定的对象引用。 -m (1=enable multicast responses, 0=disable(default): 是否使用多播功能。

运行名字服务时,可以使用-ORBEndpoint参数来指定其运行的端点(有关-ORBEndpoint的详细内容参见文档: $TAO_ROOT\\docs\\ORBEndpoint.html):

%TAO_ROOT%/orbsvcs/Naming_Service/Naming_Service -ORBEndpoint iiop://localhost:2809 -m 0

(名字服务在本机的2809端口监听请求,不使用多播方式)

若要使用多播方式启动Naming_Service时,只需执行

%TAO_ROOT%/orbsvcs/Naming_Service/Naming_Service -m 1

三、参数配置

启动完成后,Naming Service就默默在那里工作了,但是,你的servant和client如何访问它呢? 要在Server和Client程序中访问NameService有两种方法: 1、配置InitRef信息:

可以在启动程序时通过-ORBInitRef来指定,如: -ORBInitRef NameService=IOR:00ff... 或使用corbaname,如:

-ORBInitRef NameService=corbaname::localhost:2809

指定NameService在本机的默认端口上监听。

也可采用corbaloc来指定NameService的运行信息,如:

-ORBInitRef NameService=corbaloc:iiop:1.2@localhost:2809/NameService

在这里,corbaloc/corbaname被用于以更具有可读性的方式来定位servant,关于corbaname、corbaloc的更为详细的信息见参考6。

配置好InitRef后,程序中就可以通过:

orb->resolve_initial_references(\ 访问到NameService了。

2、访问Naming Service的另一种方式则是传递Naming Service的IOR串,然后通过: orb->string_to_object(...);

从IOR串连接到相应的Naming Service,因为NameService只不过是一个特殊的servant。

四、实例

下面新建一个工程eg2_svr,添加到当前Solution,在该工程中添加一个名为eg2_svr.cpp的文件(由于代码较多,文件内容略,详见附件),使该工程依赖于前面的idl工程。由于这是一个Server程序,并且还要用到命名服务,需要链接以下两个链接库:

TAO_CosNamingd.lib

TAO_PortableServerd.lib

编译该工程。

新建一个eg2_cli工程,添加到当前Solution,在该工程中添加一个名为eg2_cli.cpp的文件(由于代码较多,文件内容略,详见附件),使该工程依赖于前面的idl工程,并在工程链接选项中添加TAO_CosNamingd.lib,以使用命名服务,编译该工程。 在Server程序中,我们在创建好servant object后,将其bind到了Naming Service中;在Client程序中,通过resolve助记符,我们从Naming Service那里取得了servant的IOR信息,进而便可与相应的servant进行连接。

在这里Naming Service一定程度上扮演了一个DNS的角色,DNS完成了地址到IP的转换,而Naming Service完成的则是助记符到IOR的转换。

下面是几组正确的启动Naming Servie和eg2_svr/eg2_cli的方法:

%TAO_ROOT%/orbsvcs/Naming_Service/Naming_Service -m 1 eg2_svr eg2_cli

%TAO_ROOT%/orbsvcs/Naming_Service/Naming_Service -m 0 -ORBEndpoint iiop://localhost:2809

eg2_svr -ORBInitRef NameService=iioploc://localhost:2809/NameService eg2_cli -ORBInitRef NameService=iioploc://localhost:2809/NameService

%TAO_ROOT%/orbsvcs/Naming_Service/Naming_Service -m 0 -ORBEndpoint iiop://localhost:2809

eg2_cli -ORBInitRef NameService=corbaname:iiop:1.2@localhost:2809 eg2_svr -ORBInitRef NameService=corbaname::localhost

%TAO_ROOT%/orbsvcs/Naming_Service/Naming_Service -m 0 -ORBEndpoint iiop://localhost:2809

eg2_cli -ORBInitRef NameService=corbaloc:iiop:1.2@localhost:2809

eg2_svr -ORBInitRef NameService=corbaloc::localhost

(注:iiop协议标记可以省略,另IANA已将2809端口分配给corbaloc,当服务程序在该端口上监听时可以省略端口号。)

五、其它

%TAO_ROOT%/utils目录下有两个工具:NameViewer/nslist,可用于查看当前Naming_Service中注册的

NamingContext的信息,其中NameViewer是一个基于MFC的可视化的工具,如果你使用的是VC2005 Express将无法编译该工程。

此外,该目录下还有一个工具catior可用于查看IOR的详细信息,你可以指定一个存有IOR信息的文件作为输入参数(catior -f file_name)或者在Naming_Service运行过程中,指定一个有效的NamingContext作为参数,如:

catior -n test.my_context/Echo.Object

附:本文示例源代码 参考:

1. Duncan Grisby, Sai-Lai Lo, David Riddoch. The omniORB Version4.0 User's Guide.

http://omniorb.sourceforge.net/omni40/omniORB.pdf

2. OMG. Naming Service Specification, V1.3. http://www.omg.org/cgi-bin/apps/doc?formal/04-10-03.pdf

3. Carlos O'Ryan. TAO's Naming Service.

http://www.cs.wustl.edu/~schmidt/ACE_wrappers/TAO/docs/tutorials/Quoter/Naming_Service/index.html 4. Object Computing, Inc. TAO FAQ. http://www.theaceorb.com/faq/index.html 5. Mentoya. TAO使用指南. http://www.huihoo.com/ace_tao/tao_guide.html

6. Ciaran McHale. The corbaloc and corbaname URLs. http://www.iona.com/devcenter/corba/corbaloc.pdf

CORBA Programming with TAO - 7.IIOP(体验IIOP--一个Java作客户端的例子)

摘要:

CORBA2.0引入的IIOP(Internet Inter-ORB Protocol)是GIOP(General Inter-ORB Protocol)针对TCP/IP协议的一个实现,IIOP使得不同ORB间可以相互访问,同时也使得基于不同ORB的复杂分布式应用成为可能。

一、简介

TAO是一个C++ ORB,因此,我们在没有其它附加“转接”模块的情况下,仅能使用C++来直接使用该ORB进行程序设计,但是,由于TAO同时也支持IIOP(Internet Inter-ORB Protocol),因此,我们的客户程序并不一定要用C++来编写;反之,我们基于TAO的客户程序也可以与基于其它ORB实现的Server程序进行通信。 IIOP是不同ORB实现间进行交互的桥梁,由于这种支持是内建的,因此,对于应用程序来讲,并不需要为此额外做些什么。

Java从1.3开始,提供了一个内建的ORB实现--Java IDL,该ORB被定义在org.omg.CORBA及其子包中。 本文中,我们将通过一个Java作客户端的例子向大家讲述如何在TAO与Java IDL间进行通信。

二、实例

这里,我们尝试编写一个Java Client与上一篇中的echo服务程序(eg2_svr)进行通信。具体步骤如下: 1、在原echo工程目录下新建一个目录:java;

2、到idl子工程目录下执行如下命令编译echo.idl,生成存根(Stub,桩)代码: idlj -td ../java echo.idl

注:其中的-td用于将输出目录改为../java。 通过编译,我们将得到如下几个.java文件:

Echo.java

接口类,从EchoOperations接口及org.omg.CORBA.Object等接口派生,从而保证其实现者除具有idl中定义的接口特性外,还具有org.omg.CORBA.Object所规定的特性。 EchoHelper.java

辅助支持类,为对Echo对象提供读写、类型检查等辅助支持。 EchoHolder.java 另一辅助支持类。 EchoOperations.java idl直接映射的接口声明。

_EchoStub.java

这是一个stub类,为Client程序提供Echo接口的Stub实现。

对于我们的客户程序而言,我们一般仅需与Echo接口及EchoHelper类直接打交道。 下面是直接通过IOR访问TAO Server程序的代码:

// EchoClient_IOR.java

import org.omg.CORBA.*;

import org.omg.CosNaming.*;

public class EchoClient_IOR {

public static void main(String args []) { if (args.length != 1) {

}

try {

ORB orb = ORB.init(args, null);

org.omg.CORBA.Object objRef = orb.string_to_object(args[0]); Echo h = EchoHelper.narrow(objRef);

System.out.println(\); System.out.println(\);

System.out.println(\);

return;

System.out.println(\ + h.echoString(\World!\)); }

启动eg1_svr后只需将程序输出的IOR拷贝传递给EchoClient_IOR即可。

同样,我们还可以通过访问TAO Naming Service来访问对应的服务程序。下面是Naming Service版本的EchoClient:

import org.omg.CORBA.*;

import org.omg.CosNaming.*;

public class EchoClient_NS {

public static void main(String args []) {

try {

ORB orb = ORB.init(args, null); }

} catch (Exception e) {

System.out.println(\ + e); e.printStackTrace(System.out); }

org.omg.CORBA.Object objRef =

orb.resolve_initial_references(\);

for (int i = 0; i < path.length; i++) {

System.out.println(i + \ + path[i].id + \ + path[i].kind); }

Echo h = EchoHelper.narrow(ncRef.resolve(path)); System.out.println(\); System.out.println(\);

NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef); String name = \; NameComponent path [] = ncRef.to_name(name);

4. Carlos O'Ryan. TAO's COS Event Service.

http://www.cs.wustl.edu/~schmidt/ACE_wrappers/TAO/docs/tutorials/Quoter/Event_Service/index.html 5. Chris Gill, Tim Harrison, and Carlos O'Ryan. Using the Real-Time Event Service.

http://www.cs.wustl.edu/~schmidt/events_tutorial.html

CORBA Programming with TAO - 9.Postscript(后记)

摘要:

这是整个系列的最后一篇,对整个系列及该系列未涵盖的内容进行了总结,同时对CORBA及分布式对象计算技术的发展状况进行了简单分析。

正文:

CORBA本身是一个复杂的体系,本系列只是初步介绍了用TAO进行分布式CORBA应用的基础知识,并有选择性地略去了部分内容(如DynAny、POA Policy、Persistent Object等,关于POA Policy的相关内容可阅读参考文献5),此外,还有一些主题(如Implementation Repository、Interface Repository、Trading Service、Notification Service等)及很多实际应用中需要注意的问题也是本系列没有覆盖的,还需要大家通过深入研究和实践来掌握。

就目前来看,CORBA规范已逐步趋于成熟,并仍在不断发展、改进,一些已有的组成部分被更新、完善的同时,也有一些新的与分布式应用相关的内容被OMG组织纳入其研究范围,如:异步通信机制、针对嵌入式和实时系统等具体领域的CORBA规范、反射机制、与EJB/SOAP等的互通等,CORBA正在随着研究人员研究的深入,变得越来越庞杂。

与此同时,由于2.x规范中定义的CORBA架构存在一些局限性,其中最为典型的问题是:CORBA不支持在不破坏向前兼容的情况下为对象增加新的接口,即不能在不改变接口定义的情况下为对象增加新的功能。为此,OMG在CORBA3中引入了CORBA Component Model(CCM)以在一定程度上解决这一问题。

CCM是基于EJB和COM提出的一种新的组件模型,它在结构上更接近EJB。在CCM中,Component是支持多个接口的、可配置且可导航(即通过一个接口查询到其它接口,类似于COM中的IUnknown::QueryInterface,这在CORBA 2.x中是不可能实现的)的二进制单元。CCM一定程度上改变了我们进行CORBA应用设计的模式,使得我们由以接口设计为中心转为以组件(Component)设计为中心。

由于CCM体系十分复杂,涉及组件容器、组件实现描述语言、组件打包与部署、与EJB的互通等许多方面,虽然CCM规范发布已有数年,目前仅有少数几个部分支持CCM的商业CORBA实现面世, 著名ORB提供商BEA、IONA等已宣布将支持CCM,但相关产品尚未发布,此外,也有一些开源项目,如CIAO(Component Integrated ACE ORB)、OpenCCM等已取得一定进展,已有一些试验产品可供研究参考。

此外,CORBA提供“至多一次”的语义来确定一次访问成功与否,客户端发起一次调用,在服务器端也只能引起一次反应。CORBA没有提供功能来检查一次访问究竟是否成功的修改了要访问的对象的状态,因为缺乏这样的机制,使得提供“故障恢复”机制变得十分困难,因此CORBA把本来可能恢复的故障处理成不可恢复的。

为此,一些研究者开发出了用于分布式对象计算的新模型,如ICE就是在CORBA的基础上为简化应用复杂度,并克服CORBA存在的没有多重接口(该问题随着CCM的提出已被解决)、没有异常继承等方面存在的问题而提出的一个新的应用模型。可以相信随着研究的深入,CORBA或相关的分布式对象计算技术将变得更加易用也更加强大(当然,对于CORBA实现提供商或开源CORBA实现提供者来讲,要做的工作也越来越多)。

参考:

1. Douglas C. Schmidt and Steve Vinoski. Evolving Towards Component Middleware.

http://www.cuj.com/documents/s=9039/cujexp0402vinoski/

2. Paul Harmon. OMG的CORBA 组件新模型.

http://www-128.ibm.com/developerworks/cn/java/corba-comp-model/ 3. Michi Henning. A New Approach to Object-Oriented Middleware.

http://www.triodia.com/staff/michi/ieee/ieee.pdf

4. OMG. CORBA Components, v3.0 full specification. http://www.omg.org/cgi-bin/apps/doc?formal/02-06-65.pdf 5. Sun Microsystems, Inc. CORBA Programming with J2SE 1.4.

http://java.sun.com/developer/technicalArticles/releases/corba/

体验CORBA组件模型CCM:1、引言

摘要:

在比较CORBA2.x与CORBA3.x的基础上,简要介绍CCM引入的新特性。

一、概述

虽然CORBA2.x已成功应用于诸多软件系统的开发,但CORBA2.x仅是一个分布式对象计算模型,并不是一个组件对象模型,OMG组织考虑到CORBA2.x存在的以下问题: ? 缺乏功能分界

在CORBA2.x对象模型中,所有的接口均被视为是面向客户程序的,而缺乏在不同接口实现间进行通信的通用规范。

? 难于扩展对象的功能

在CORBA2.x中,对象只能通过继承(而不是组合)来扩展,应用程序开发人员必须先定义一个IDL接口、实现该接口、并在所有Server中配置该实现。但CORBA IDL的多重继承是有缺陷的,因为有的语言不支持重载;另外,应用程序可能用同一个IDL接口多次发布服务的多个实现或多个实例,但多重继承使得不可能不止一次的发布同一接口,或由客户确定哪一个是最下层的版本。

? 缺乏统一的软件配置、部署规范等

缺乏统一的软件配置、部署规范使得开发人员需要用各自不同的方式,来进行应用系统的初始化及配置、管理,使得整个软件系统变得难于管理与维护。

同时,考虑到组件化中间件的必然趋势,OMG于1999年正式发布了CORBA3.0,CORBA3.0除将IDL2.x扩展到IDL3.0外,还将CORBA组件模型(CCM)引入到了CORBA的世界,使得CORBA成为一种组件中间件技术。

(注:CORBA2.x规范所定义的CORBA仅为一种分布式对象计算技术,有些文章中以CORBA2.x实现为基础讨论CORBA组件技术,并将其与EJB、COM等进行比较,是完全错误的。)

组件中间件技术通过更高层次的抽象定义,弥补了面向对象中间件技术的一些缺陷,定义了组件的虚拟边界、定义了组件的发布方法,以及组件与其他组件和客户端进行交互的方式。同时,组件中间件的定义也支持运行时环境、封装和发布的机制和标准,所有的这些机制和标准都使得了软件组件的重用变得更加简单。通过使用组件中间件,系统可以使用一种“装配的”方式来搭建,而不是使用传统的工程方法,即CORBA2.x中,没有标准支持二进制级别的重用,所有的重用基本是源码级别的,而在CCM中,我们可以以组件为单位在二进制级别上进行实现重用,虽然这在目前来讲还比较困难。

OMG的CCM规范是以EJB为蓝本提出的,并且,为了兼容EJB,CCM还对与EJB的互操作进行了讨论,但目前CCM的商业应用远不如EJB广泛,这一方面与CCM本身的复杂性有关,同时也与CORBA中间件提供商及客户的关注程度有很大关系。

CIAO(Component Intergrated ACE-ORB)是一个基于ACE/TAO的开源CCM实现,它主要面向分布式实时嵌入式(Distributed Real-time Embeded,DRE)应用,是目前所有的CCM实现中相对比较成熟的一个(注:可从http://deuce.doc.wustl.edu/Download.html下载到CIAO的最新版本源代码)。本文将以CIAO0.5为平台,实例讲解如何在CIAO0.5中运用CCM提供的新特性进行CORBA组件的设计。

二、CCM新特性

CCM鼓励通过组合而不是继承来重用组件,为使用组合,组件需要将它的部分操作委派给其它组件。为此CCM规范定义了两种级别的组件类型:基本(basic)组件类型、扩展(extend)组件类型。基本类型组件,主要用于对传统的CORBA分布对象进行组件化实现;扩展组件支持新的接口外观特征,这些新的接口外观特征被称作端口(port),用于支持客户与组件以及组件间的互连,而基本组件类型不支持新的接口外观特性。 CCM支持五种基本端口:刻面/插座(facet/receptacle)、事件源/事件槽(event sources/sink)和属性(attribute)。五种Port的表示方法如下图所示:

刻面(facet):组件实现且可供组件客户使用的接口; ? 接插口(receptacle):组件使用外部对象(或组件)引用的连接点(一般用于分布组件之间的互连或组

? ? ? ?

装);

事件源(event source):命名了的组件事件源(事件发送)连接点,用于向一个或多个关注特定事件类型的事件消费者或事件通道发送该类型的事件; 事件槽(event sink):命名了的组件事件槽(事件接收)连接点,用于接收外界向其推入的特定类型事件。

组件属性(attribute):是组件向外界显露的可被访问(accessor)或可被定制的内容(mutator),主要用

于定制组件。

此外组件还支持其它一些新特性,包括:

组件标准Home接口:提供组件工厂(创建)模型或查找模型的方法接口; ? 主特征键值(primary keys):使得客户可以(根据特征键值)查找或确认特定的组件实例。

?

2.1 组件的多重刻面(接口)和刻面导航接口

组件可以提供多重对象引用,称之为刻面,用于向客户提供该组件的不同内容的CORBA接口。组件还可以生成一个称之为组件等价接口的单一的对象引用,用于导航至其它组件刻面。

组件内部封装了不同组件刻面的实现,对客户不透明。客户可以通过组件的任何刻面或组件的等价接口导航至组件的其它刻面接口。

在声明组件时,可通过provides关键字来指定组件对外提供的Facet,如: provides Interface_Name facet_name;

对于每个Facet,都有一个独立的实现类与之对应,该实现类的名称为: class Interface_Name_exec_i;

通过编译IDL文件,自动化工具会为组件的最终实现类生成一个名为: get_facet_name

的方法,以便在容器向组件请求Facet服务时返回对应接口实现的实例。

2.2 组件接插口

组件接插口提供了组件间互相连接的模型描述,用于描述组件与其它被连接对象(组件)接口之间的关系。组件接插口可以比较简单(如只管理单一的对象/组件引用),也可以较为复杂(管理多重连接的对象/组件引用)。

Receptacle可通过uses关键字来指定,如:

uses Interface_Name receptacle_name;

通过编译IDL文件,自动化工具会在为组件对应的Context类中添加一个名为: get_connection_receptacle_name

的方法,以便通过容器向另一组件请求对应的Facet接口,以调用Facet提供的方法,完成相应的逻辑处理。 一个Facet可对应多个Receptacle,而一个Receptacle只能有一个对应的Facet。 2.3 组件事件

CCM支持发布/征订(publish/subscribe)式的事件模式。并使用推技术,提供简单的API,支持对CORBA通知(Notification)服务的使用。 组件事件源:

组件事件可以以发射方式(emitter)或发布方式(publisher)向外界发送事件,其中发布方式的组件事件源允许组件发布到事件通道上的事件可以被多个事件征订者所获取。事件客户可通过事件发布器直接访问组件产生的事件流;发射方式的组件事件源则只允许组件发布到事件通道上的事件被单个指定的事件征订者所获取(而其它的征订者则不能获取)。发射方式的组件事件源主要用于组件的配置,一般在初始化或配置时刻被连接到系统的相关单元上,并且不暴露给一般用户。

在声明组件时,可通过publishes或emits来指定Event Source,如: publishes EventType_Name event_name;

EventType本身是一个值类型,应该以eventtype关键字来声明,如: eventtype EventType_Name { public event_attr attr_name; };

通过编译IDL文件,自动化工具会为组件对应的Context类添加一个名为: push_event_name

的方法,以便通过容器向其它组件发布通知事件。 组件事件槽:

事件槽定义了组件隐含接收事件的事件类型。外部实体可以连接事件槽提供的事件消费者接口,与其进行关联。与事件源不同,事件槽并不区分特定或一般的事件发送者。通过定义事件槽,组件可以声明其感兴趣的事件类型。

Event Sink可以用consumes关键字来指定,如: consumes EventType_Name event_name;

通过编译IDL文件,自动化工具会为组件的最终实现类生成一个名为: push_event_name

的方法,以便在容器向组件转发通知事件时对该事件进行处理。 2.4 主特征键值

组件可以向外界显露一个主特征键值。客户可以通过该值查找、创建、删除相关的组件实例。 2.5 Home接口

组件Home是管理组件实例的元类型。组件Home接口提供了管理组件生命周期的接口,以及根据主特征键值

关联相关组件实例的功能接口,从概念上理解,组件Home是其相关组件类型实例的管理器。

当组件被装配到组件容器中后,Home对象即被创建。组件的主特征键值通过Home创建并设置,与组件实例进行关联。在CCM规范中,主特征键值可显露给组件客户使用,辅助其确认与相关的组件实例连接。尽管不同的Home类型可以管理同一种组件类型,但在运行时刻,一个组件实例只能被一个Home对象所管理。 Home提供工厂模式的创建操作或查找模式的查询操作来管理组件实例。另外,除了隐式定义的工厂创建或查找操作外,组件开发人员还可以通过扩展的IDL定义其它任意格式的组件创建(factory)或查找(finder)操作,如:

factory create_func_name(arg_list); finder find_func_name(arglist);

通过编译IDL文件,自动化工具会为对应的组件类生成一个返回值为组件类指针类型的工厂方法和查找方法,而方法的名称是你在IDL文件中定义的名称。

开发人员甚至可以在Home接口中定义其它操作供用户使用,如查询内部接口含义等自省机制的操作。 2.6 组件属性和配置

CORBA组件模型支持组件的属性和配置特征,使得设计者可以在配置组件的过程中通过属性来区分组件接口,或在运行组件的过程中通过属性定义来配置不同的组件实例,以与其它组件实例相区分。

在声明组件时,可以在attribute前添加readonly关键字以表明属性是只读的,如: readonly attribute Attribute_Type attr_name;

通过编译IDL文件,自动化工具会为组件的最终实现类生成相应的accessor方法和mutator方法,以用于读取和修改属性的值(对于readonly属性,仅有accessor方法),这些方法除可供配置时使用外,在组件实现内部也可使用。

组件的配置过程主要是设置组件属性的过程,在CCM规范中把组件的配置生命周期(Configuration Life Cycle)分为配置阶段(Configuration Phase)和操作阶段(Operational Phase),在配置阶段组件不响应客户端任何请求,由代理调用一些特殊的操作来配置或修改组件的属性集;当配置完毕之后,调用configuration_complete操作标志配置阶段结束,组件进入可操作阶段。

CCM定义了配置(configurator)对象,用于封装组件的属性配置功能,它通过调用一组组件支持的属性定制方法对组件属性进行配置。 2.7 组件的继承

组件类型通过继承通用CCM Object接口,以提供CORBA规范定义的组件特征,如接口导航、组件接插口、事件接口等等,而组件Home则继承了CCM Home接口。

三、组件实现

CCM规范定义了一种描述语言,组件实现定义语言(Component Implementation Definition Language CIDL),用于定义组件及组件Home和相关抽象状态的实现。CIDL编译器使用CIDL描述生成组件的实现骨架,其中自动集成了大部分基本的组件功能,如接口导航、组件确认、组件查询、组件激活、状态管理、生命期管理等等。组件的实现者通过扩展这些实现骨架(放入业务逻辑),实现最终的组件。

CIDL是持久状态定义语言(Persistent State Definition Language PSDL)的超集,它允许描述组件内部状态与外部抽象存储类型的联系,组件实现框架CIF和组件容器则可以通过协作自动对组件的持久状态进行管理。 一个组件类型是一组基于IDL组件定义以及其它相关结构描述信息的集合,它具有特定的状态和特征值,并封装了内部的表示和实现。

组件实现以组件包的形式存在,它包含了一种或多种组件实现的可执行代码,以及描述该组件类型的元数据。组件包有两种隐含的使用方式,即在组件应用开发时,由应用组装工具对其进行定制和安装;或在运行时,直接通过配置工具将其装载入当前可运行的组件容器中,被组件应用的客户所使用。 组件在CORBA组件容器中被初始化和“存活”,不同的组件类别将使用到不同类别的组件容器。

组件的开发步骤可大致分为以下几步: 1. 组件应用的分析/设计

2. 组件定义(基于IDL/CIDL语言)

即定义接口和异常及需要用到的基本数据结构,并利用IDL3.x新增的CCM关键字(如component、supports、provides、uses、publishes、emits、consumes)来“组装”组件,并定义组件的home接口以声明用于创建组件的factory方法;

3. 组件实现(用户实现业务逻辑功能) 4. 组件打包:

组件打包文件包括组件实现和组件描述。一个完整的组件是可配置的,具有自描述能力的软件包(package)。组件包可以在运行时被安装或配置到组件应用中立刻为客户提供服务,也可以在设计时通过开发环境“可视的”进行装配,并生成装配信息。被打包的组件实现和组件描述等内容将以组件存档文件的形式进行存储。 5. 组件组装:

组件组装阶段可以对多个组件进行定制,连接(与其它组件)和分割,并形成组件组装存档文件。组件组装文档描述了一组(可以是已定制了的)物理组件集合及其之间的关系描述,包括定义组件实例之间的关系,配置组件的初始属性/状态,描述与其它组件的连接关系等内容。组件组装文档既可以直接进行配置,在运行时可被一个组件容器或位于不同主机的多个组件容器所动态装入和初始化,也可以继续被构造成更复杂的组装文档。 6. 组件的配置与安装:

提供配置/安装服务,可以将打包好的组件配置安装到网络上的特定主机上运行。 7. 组件实例激活与运行:

一旦组件被配置和安装成功,组件实例通过标准的CORBA ORB机制被激活运行并提供相应的服务。

下一篇文章将以一个实例,详细讨论基于CIAO的CCM组件的开发过程。

参考:

1. Sharplog. CCM简介. http://corbatime.diy.myrice.com/article/ccm_intro.htm

2. OMG CCM Implementers Group and MARS PTF & Telecom DTF, CORBA Component Model Tutorial, http://www.omg.org/docs/ccm/02-04-01.ppt, April, 2002. 3. Object Management Group, Inc. CORBA Component Model Specification, v4.0,

http://www.omg.org/cgi-bin/apps/doc?formal/06-04-01.pdf, Apr, 2006

4. Douglas C. Schmidt and Steve Vinoski, Object Interconnections: The CORBA Component Model: Part 1,

Evolving Towards Component Middleware, C/C++ Users Journal, February, 2004.

5. Douglas C. Schmidt and Steve Vinoski, Object Interconnections: The CORBA Component Model: Part 2,

Defining Components with the IDL 3.x Types, C/C++ Users Journal, April, 2004.

6. Bala Natarajan, Douglas C. Schmidt, and Steve Vinoski, The CORBA Component Model Part 3: The CCM

Container Architecture and Component Implementation Framework, C/C++ Users Journal, September, 2004. 7. Bala Natarajan, Douglas C. Schmidt, and Steve Vinoski, The CORBA Component Model Part 4: The CORBA

Component Model Part 4: Implementing Components with CCM, C/C++ Users Journal, October, 2004.

体验CORBA组件模型CCM:2、实例

摘要:

通过一个简单的实例,详细介绍基于CIAO的CCM组件开发过程。

正文:

前面讲过,CCM是以EJB为蓝本来定义的,因此,二者在组件分类(与EJB被分为Session、Entity、Message Driven三种类型一样,CCM组件被分为Service、Session、Process、Entity四种类型)、组件的基本组成、开发/部署基本流程等方面十分相似,但由于目前CCM组件应用服务器/应用框架等远不如EJB成熟,在开发环境的

支持方面也远不如EJB完善,因此,其开发过程还比较繁琐。

下面将围绕如下的应用需求详细讨论CIAO平台下CCM开发的详细过程: 一个Monitor程序负责监视多台设备的状态,且每个设备上均运行一个设备控制程序Controller,Controller的客户端通过该组件提供的接口来操控Controller;按照常规的方法,可以采用轮询的方式,Monitor定期向Controller查询设备目前的状态信息,也可以通过Event Service在Controller与Monitor间建立事件通道。在这里,我们采用CCM来解决上述问题。

整个系统包括两个组件Controller和Monitor;当设备启动时,Controller通过Monitor提供的DeviceIDAllocator接口获得由Monitor分配的唯一标识信息(如IP地址等信息,本例中为简化问题,该唯一标识仅由一个表示名称的字符串组成,且Monitor不会记忆已分配给每个设备的标识);当Controller状态发生变化时,向Monitor发布DeviceStatus状态变化通知事件。

一、编写IDL

IDL文件被用于描述组件Controller、Monitor之间的通信接口、组件的基本组成(包括组件所支持的Facet/Receptacle、eventtype、Event Source/Event Sink、Component Home、组件的Attribute等)以及组件对外提供的接口。 步骤:

1、建立一个目录DeviceAdmin,在该目录下创建三个子目录:Controller、DeviceBase、Monitor,分别作为Controller组件工程文件、组件公共工程文件、Monitor组件工程文件的存放目录。

2、在DeviceBase下建立如下idl文件,其中定义了一些组件间通信需要用到的基本接口及结构。

//DeviceBase.idl

#include

module Device {

const long MAX_RUN_LEVEL = 5; const long MIN_RUN_LEVEL = 0;

// a demo device unique id struct DeviceID {

string device_name; };

// a demo device status struct DeviceStatus { long run_level; };

struct StatusPair {

DeviceID device_id;

DeviceStatus device_status; }; /**

* @event DeviceStatus *

* @brief component event between Monitor and Controller * Controller publishes this event when status change.

*/

eventtype StatusEvent {

public StatusPair status_pair; };

/**

* @interface DeviceIDAllocator

*

* @brief Controller use this facet to get a unique id from Monitor */

interface DeviceIDAllocator { DeviceID get_id(); };

/**

* @interface DeviceOperate

*

* @brief a interface exposed to client by Controller */

interface DeviceOperate {

void power_on(); void power_off();

DeviceID get_device_id();

DeviceStatus get_device_status(); boolean tune(in boolean tune_up);

}; };

其中,DeviceID表示设备的唯一标识,DeviceStatus表示设备的状态信息,StatusEvent是Controller、Monitor间传递的通知事件,DeviceIDAllocator是由Monitor提供的一个Facet接口,DeviceOperate是Controller组件支持的一个普通接口。

3、在Controller目录下建立如下的idl文件,用于声明Controller组件对外的接口及与其他组件的通信接口。Controller组件支持DeviceOperate接口,并有一个DeviceIDAllocator类型的receptacle,和可对外发布StatusEvent类型的通知事件,此外,Controller组件还有两个属性,分别表示设备的唯一标识和设备的当前状态,其中唯一标识是只读属性。

//Controller.idl

#include \

module Device {

/**

* @class Controller *

* @brief component */

component Controller supports DeviceOperate { publishes StatusEvent notify_out;

};

uses DeviceIDAllocator id_allocator; readonly attribute DeviceID device_id; attribute DeviceStatus device_status;

/**

* @class ControllerHome *

* @brief home for Controller component */

home ControllerHome manages Controller { }; };

4、在Monitor目录下添加如下idl文件,用于声明Monitor组件对外的接口及与其他组件的通信接口。Monitor组件支持一个DeviceIDAllocator类型的facet,并可接收StatusEvent通知事件。

//Monitor.idl

#include \

module Device {

/**

* @class MonitorHome

*

* @brief home for Monitor component /**

* @class Monitor *

* @brief component */

component Monitor {

provides DeviceIDAllocator id_allocator; consumes StatusEvent notify_in; };

*/

home MonitorHome manages Monitor {}; };

二、编写cidl并生成程序架构

cidl文件用于描述组件和组件Home接口的实现和持久状态,cidl编译器cidlc可以根据idl和cidl文件为我们自动生成组件程序框架,从而大大简化组件的开发。CIDL所生成的实现称为executor,executor包含了一些自动实现,并提供了钩子方法以允许开发人员可以增加定制的组件专门的逻辑。executor可以打包到DLL中,并可以安装到支持特定目标平台和编程语言的组件Server中。 步骤:

1、进入Controller目录,添加如下cidl文件:

//Controller.idl

#include \

composition session Controller_Impl { home executor ControllerHome_Exec { implements Device::ControllerHome; manages Controller_Exec; } };

并执行:

%CIAO_ROOT%\\bin\\cidlc -I%CIAO_ROOT% -I%CIAO_ROOT%\\DAnCE -I%CIAO_ROOT%\\ciao -I%TAO_ROOT% -I%TAO_ROOT%\\tao -I%TAO_ROOT%\\orbsvcs --gen-exec-impl --

Controller.cidl

以生成最终实现类的基本结构,通过执行上述命令,我们将得到Controller_exec.h和Controller_exec.cpp(以及servant类和其它几个文件,但只有上述两个文件是我们需要手工修改的),这是我们实现Controller组件方法和ControllerHome接口的地方,其中包含了为实现组件需要实现的各方法(包括属性的accessor/mutator方法、组件支持的接口所包含的方法、其它CCM相关的基本方法,如ccm_activate(), ccm_passivate(), ccm_remove()、set_session_context()等)的声明和空的函数体,你可以无需任何修改即可将上述文件加入组件工程完成编译。

2、进入Monitor目录,添加如下cidl文件:

//Monitor.idl

#include \

composition session Monitor_Impl { home executor MonitorHome_Exec { implements Device::MonitorHome; manages Monitor_Exec; } };

并执行:

%CIAO_ROOT%\\bin\\cidlc -I%CIAO_ROOT% -I%CIAO_ROOT%\\DAnCE -I%CIAO_ROOT%\\ciao -I%TAO_ROOT% -I%TAO_ROOT%\\tao -I%TAO_ROOT%\\orbsvcs --gen-exec-impl -- Monitor.cidl 以生成最终实现类的基本结构,通过执行上述命令,我们将得到Monitor_exec.h和Monitor_exec.cpp。

三、实现组件

虽然上面已经生成了组件实现类的基本架构,但我们还需要借助其他CCM自动化工具生成我们的工程文件。 步骤:

1、进入DeviceBase目录,依次执行如下命令:

%CIAO_ROOT%/bin/generate_component_mpc.pl -n DeviceBase

生成DeviceBase基础工程描述文件。

由于DeviceBase工程仅用于编译我们的组件工程依赖的基本接口及结构信息,并不是一个组件,因此,我们需要手动删除DeviceBase_svnt工程的部分内容。打开DeviceBase.mwc文件,删除DeviceBase_svnt工程CIDL_Files、IDL_Files两个说明项,仅保留Source_Files说明项下的DeviceBaseS.cpp文件。修改后的

DeviceBase_svnt工程部分的内容应该是:

project(DeviceBase_svnt) : ciao_servant_dnc { after += DeviceBase_stub sharedname = DeviceBase_svnt libs += DeviceBase_stub

idlflags += -Wb,export_macro=DEVICEBASE_SVNT_Export -Wb,export_include=DeviceBase_svnt_export.h dynamicflags = DEVICEBASE_SVNT_BUILD_DLL

Source_Files { DeviceBaseS.cpp } }

2、进入Controller目录,依次执行如下命令:

%CIAO_ROOT%/bin/generate_component_mpc.pl -p DeviceBase Controller 生成Controller组件工程描述文件。

3、进入Monitor目录,依次执行如下命令:

%CIAO_ROOT%/bin/generate_component_mpc.pl -p DeviceBase Monitor 生成Monitor组件工程描述文件。

4、进入DeviceAdmin目录,并在该目录下执行: mwc.pl -type vc8

以生成整个Solution文件。打开生成的.sln文件,将看到该Solution下包含有8个Project,分别是: DeviceBase_stub DeviceBase_svnt

DeviceBase_Controller_exec DeviceBase_Controller_stub DeviceBase_Controller_svnt DeviceBase_Monitor_exec DeviceBase_Monitor_stub

DeviceBase_Monitor_svnt

且各工程间的依赖关系已建立好,你只需编译DeviceBase_Controller_exec、DeviceBase_Monitor_exec即可完成整个工程的编译,试试编译这两个工程,你应该可以顺利通过编译,但由于还没有添加实现代码,组件什么也做不了。

仔细查看自动生成的Monitor_exec.h/Monitor_exec.cpp、Controller_exec.h/Controller_exec.cpp会发现:

1)对于组件的每个普通attribute,会生成一个accessor和一个mutator方法,而对于readonly attribute,仅会生成一个accessor方法;

2)自动工具还自动生成了组件所支持的对外接口的框架;

3)对于组件所支持的Facet,会单独生成一个框架类,其中包含了该Facet所有方法的空的实现;而Receptacle一方可以通过相应Context类提供的“get_ + 接口名”方法来访问由对方组件提供的服务。

4)对于Event的接收方,会自动生成一个以“push_ + 事件名”命名的方法;而对于Event的发送方,相应的Servant类以包含了发送事件相关的代码,我们只需调用相应Context类的“push_ + 事件名”方法即可。

为了节省篇幅,这里不详细介绍实现的具体内容,读者可以比较刚创建的工程文件和附件中的工程文件,以找出其中被修改的地方,所有的修改均集中在Monitor_exec.h/Monitor_exec.cpp、Controller_exec.h/Controller_exec.cpp几个文件中。

四、运用CoSMIC描述组件

现在到了整个组件应用开发中最不令人愉快的阶段,我们要用CoMIC这个工具来描述我们的组件,以生成组件的descriptor文件。CoMIC是一个用于生成组件部署descriptor的MDD工具,其安装请参照该项目的安装说明,虽然CoMIC想用尽可能简便、直观的方式来帮助我们编写descriptor,但其过程仍然有些烦琐。我不久前曾建议CIAO的作者实现一个类似idl的组件描述语言,以便通过编译该文件获得组件的描述信息,但Schmdit博士似乎对此不感兴趣,并认为可以借助另一项目Cadena提供的支持来实现相关功能,而Cadena是一个Eclipse的插件(它主要面向的是另一基于Java的CCM实现OpenCCM,虽然它也支持CIAO),对于Java开发者来说,Eclipse简直太完美了,但用Eclipse来开发C++应用实在不是什么让人愉快的事情。 作为一个MDD工具,CoSMIC允许我们用类似绘制UML图的方式来描述系统内各组件间的关系,以及系统内包含的等。

运用CoSMIC描述组件的基本流程如下:

1、运行idl_to_picml命令解析各idl文件,生成可被CoSMIC导入的平台无关组件模型语言(PICML,Platform-Independent Component Modeling Language)xml描述文件。

2、添加ComponentImplementation,以虚拟组件的形式描述各组件间Facet/Receptacle、Event Source/Event Sink等组件端口(Port)的集成关系;

3、添加ComponentPackage,描述组件与组件实现、组件端口之间的集成关系。 4、添加PackageConfiguration,描述组件与相应ComponentPackage之间的集成关系。 5、添加ToplevelPackage,描述顶层包与虚拟组件包之间的集成关系。 6、添加Targets描述,以定义可供组件驻留的节点;

7、添加DeploymentPlan,以定义组件与节点之间的关系。

限于篇幅,这里不详细介绍CoSMIC描述组件的细节,具体过程请参照%CIAO_ROOT%/docs/tutorials/CoSMIC,附件中包含了最终完成的CoSMIC工程文件,可供读者对照。 完成CoSMIC工程后,在工程目录DeviceAdmin下创建一个目录descriptors目录,选择工具栏上的Generate Package Descriptors、Generate Domain Descriptors、Generate Flattend DeploymentPlan生成工程的部署描述文件,将所有输出文件保存到descriptors目录下。(提示:如果你在使用GME时无法看到整个CoSMIC工具栏,请将其拖到可见的范围内。) 由于CoSMIC与generate_component_mpc.pl采用了不同的Home方法命名方式,你需要手工修改最后生成的Plan.cdp文件中的各Home方法的声明,如:将createControllerHome_Servant改为create_Device_ControllerHome_Servant,将createControllerHome_Impl改为create_Device_ControllerHome_Impl,Monitor组件的两处修改类似。

五、编写客户程序

CCM所定义的组件模型并不会对我们的客户程序造成影响,所有IDL3所提供的新特性仅被用于组件之间的通信(除了supports关键字),因此我们可以像对待CORBA2.x服务程序一样的方式通过CCM组件所支持的接口来访问CCM组件程序。但同时,如果我们变换角度,将整个应用系统看作一个组件应用,则我们原来的客户程序可能变化成组件应用程序的一部分(但我们仍然需要一个客户程序来访问组件所提供的功能)。 附件中的Operator客户程序以Controller组件的IOR为输入参数,对Controller组件支持的所有接口方法进行了测试,该程序与普通的CORBA客户程序并无任何差异。

六、部署与运行

步骤:

1、先通过NodeManager启动可供组件部署、运行的节点 在descriptors目录下执行: run_NodeDaemons.pl

启动两个可供Controller、Monitor组件部署、运行的节点,你也可以分别在两个终端窗口下运行:

%CIAO_ROOT%/DAnCE/NodeManager/NodeManager -ORBEndpoint iiop://localhost:40001

-s %CIAO_ROOT%/DAnCE/NodeApplication/NodeApplication

%CIAO_ROOT%/DAnCE/NodeManager/NodeManager -ORBEndpoint iiop://localhost:40002 -s %CIAO_ROOT%/DAnCE/NodeApplication/NodeApplication 来完成相同的工作。建议读者用第二种方式,这种方式更加直观。

2、运行Execution_Manager完成逻辑节点与具体NodeApplication的关联

运行Execution_Manager需要一个节点与NodeApplication之间的映射关系的描述文件,其大致内容如下: ControllerNode corbaloc:iiop:localhost:40001/NodeManager MonitorNode corbaloc:iiop:localhost:40002/NodeManager 将上述内容保存为NodeManagerMap.dat,并执行:

%CIAO_ROOT%/DAnCE/ExecutionManager/Execution_Manager -o ior.out -i

NodeManagerMap.dat

即可完成逻辑节点与具体NodeApplication的关联。

以后,在Execution_Manager收到Plan_Launcher发送的部署描述信息时便可将组件部署到相应的NodeApplication上。

3、运行Plan_Launcher完成组件的部署 执行

%CIAO_ROOT%/DAnCE/Plan_Launcher/Plan_Launcher -p Plan.cdp -k file://ior.out 其中的Plan.cdp是本文第四节中生成的组件部署描述文件,ior.out则是上一步输出的Execution_Manager实例的IOR信息。

注:如果你在这一步失败了,请认真查看错误提示确定错误原因。在执行1、2、3步之前执行: set CIAO_DEBUG_LEVEL=11

打印调试信息可以帮助你定位错误。

4、运行测试程序

组件部署完毕,下面可以启动客户程序来操纵Controller组件了,在descriptors目录下运行如下命令: ..\\debug\\operator file://Controller.ior 将输出如下如下信息:

Power on Device...

Tune device run level... Device's name is [Device-0]

[Device-0] is running at level [2] Power off device...

而MonitorNode对应NodeApplication所在的终端将输出: Device: [Device-0] is running at level [1]

Device: [Device-0] is running at level [2] Device: [Device-0] is running at level [1] Device: [Device-0] is running at level [0]

这些信息是在Monitor组件收到Controller组件的DeviceStatus改变通知事件时输出的。

七、小结

CCM通过引入新的组件集成、配置规范,利用IDL3扩展简化和规范了对组件间通信机制的描述,使得基于组件的开发成为可能;同时,基于组件的开发方法由Container来完成组件间的通信,避免了组件间的直接访问,从而使得我们可以像组装零件一样通过集成、配置来进行系统的设计、开发。但目前组件市场并没有随着基于组件的开发(Component-Based Development,CBD)这一概念的提出来而迅速发展,从而使得基于组件的开发并不可能那么轻松和自由,但作为软件重用方法的一种较为高级的形式,CBD仍是十分有益的。

与EJB相比,CCM大量借鉴了EJB的设计思想,可以认为是EJB面向多语言层次的扩展,但由于这种扩展所带来的众多问题,使得CCM容器比EJB容器更难实现;与众多EJB服务器不同,CIAO没有一个便于操作和

管理的CCM服务器,同时也没有一个合适的集成开发环境可以支撑整个CCM组件的开发过程,使得其开发过程还比较繁琐,这些都在一定程度上制约了CCM的发展;C++与Java语言的差异和适用范围,也是造成CCM/EJB命运迥异的一个重要原因;由于CCM的复杂性,研究人员预期的EJB标准进行扩展以与CCM融合的局面也没有出现。

对于CIAO而言,设计者的高明之处在于,CIAO被设计成一个面向分布式嵌入式应用的CCM容器,在该领域,C++语言相对于Java具有明显的优势,因此也使得CCM比EJB更适用。在其他EJB广泛应用的领域,虽然已有OpenCCM、K2-CCM等面向Java的CCM实现,但在这些领域向应用开发者推广CCM是非常困难的,毕竟EJB在这些领域具有明显的优势:简单,而且成熟。

附:范例组件工程、测试程序源代码及CoSMIC工程文件

参考:

1.

Ming

Xiong,

Building

a

Stock

Quoter

with

CoSMIC

and

DAnCE.

http://www.dre.vanderbilt.edu/~mxiong/CoSMIC/.

2. Douglas C. Schmidt and Steve Vinoski, Object Interconnections: The CORBA Component Model: Part 1,

Evolving Towards Component Middleware, C/C++ Users Journal, February, 2004. 3. Douglas C. Schmidt and Steve Vinoski, Object Interconnections: The CORBA Component Model: Part 2,

Defining Components with the IDL 3.x Types, C/C++ Users Journal, April, 2004. 4. Bala Natarajan, Douglas C. Schmidt, and Steve Vinoski, The CORBA Component Model Part 3: The CCM

Container Architecture and Component Implementation Framework, C/C++ Users Journal, September, 2004. 5. Bala Natarajan, Douglas C. Schmidt, and Steve Vinoski, The CORBA Component Model Part 4: The CORBA

Component Model Part 4: Implementing Components with CCM, C/C++ Users Journal, October, 2004.

本文来源:https://www.bwwdw.com/article/wtjr.html

Top