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

体验CORBA组件模型CCM2、实例

摘要:

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

正文:

前面讲过,CCM是以EJB为蓝本来定义的,因此,二者在组件分类(与EJB被分为SessionEntityMessage Driven三种类型一样,CCM组件被分为ServiceSessionProcessEntity四种类型)、组件的基本组成、开发/部署基本流程等方面十分相似,但由于目前CCM组件应用服务器/应用框架等远不如EJB成熟,在开发环境的支持方面也远不如EJB完善,因此,其开发过程还比较繁琐。

 

下面将围绕如下的应用需求详细讨论CIAO平台下CCM开发的详细过程:

一个Monitor程序负责监视多台设备的状态,且每个设备上均运行一个设备控制程序ControllerController的客户端通过该组件提供的接口来操控Controller;按照常规的方法,可以采用轮询的方式,Monitor定期向Controller查询设备目前的状态信息,也可以通过Event ServiceControllerMonitor间建立事件通道。在这里,我们采用CCM来解决上述问题。

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

一、编写IDL

IDL文件被用于描述组件ControllerMonitor之间的通信接口、组件的基本组成(包括组件所支持的Facet/ReceptacleeventtypeEvent Source/Event SinkComponent Home、组件的Attribute等)以及组件对外提供的接口。

步骤:

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

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

 

//DeviceBase.idl

#include <Components.idl>

 

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表示设备的状态信息,StatusEventControllerMonitor间传递的通知事件,DeviceIDAllocator是由Monitor提供的一个Facet接口,DeviceOperateController组件支持的一个普通接口。

 

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

 

//Controller.idl

#include "../DeviceBase/DeviceBase.idl"

 

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 "../DeviceBase/DeviceBase.idl"

 

module Device

{

      /**

      * @class Monitor

      *

      * @brief component

      */

      component Monitor {

            provides DeviceIDAllocator id_allocator;

            consumes StatusEvent notify_in;

      };

 

      /**

      * @class MonitorHome

      *

      * @brief home for Monitor component

      */

      home MonitorHome manages Monitor {};

};

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

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

步骤:

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

 

//Controller.idl

#include "Controller.idl"

 

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.hController_exec.cpp(以及servant类和其它几个文件,但只有上述两个文件是我们需要手工修改的),这是我们实现Controller组件方法和ControllerHome接口的地方,其中包含了为实现组件需要实现的各方法(包括属性的accessor/mutator方法、组件支持的接口所包含的方法、其它CCM相关的基本方法,如ccm_activate(), ccm_passivate(), ccm_remove()set_session_context()等)的声明和空的函数体,你可以无需任何修改即可将上述文件加入组件工程完成编译。

 

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

 

//Monitor.idl

#include "Monitor.idl"

 

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.hMonitor_exec.cpp

三、实现组件

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

步骤:

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

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

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

由于DeviceBase工程仅用于编译我们的组件工程依赖的基本接口及结构信息,并不是一个组件,因此,我们需要手动删除DeviceBase_svnt工程的部分内容。打开DeviceBase.mwc文件,删除DeviceBase_svnt工程CIDL_FilesIDL_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下包含有8Project,分别是:

DeviceBase_stub

DeviceBase_svnt

DeviceBase_Controller_exec

DeviceBase_Controller_stub

DeviceBase_Controller_svnt

DeviceBase_Monitor_exec

DeviceBase_Monitor_stub

DeviceBase_Monitor_svnt

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

仔细查看自动生成的Monitor_exec.h/Monitor_exec.cppController_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.cppController_exec.h/Controller_exec.cpp几个文件中。

四、运用CoSMIC描述组件

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

作为一个MDD工具,CoSMIC允许我们用类似绘制UML图的方式来描述系统内各组件间的关系,以及系统内包含的等。

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

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

2、添加ComponentImplementation,以虚拟组件的形式描述各组件间Facet/ReceptacleEvent 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 DescriptorsGenerate Domain DescriptorsGenerate Flattend DeploymentPlan生成工程的部署描述文件,将所有输出文件保存到descriptors目录下。(提示:如果你在使用GME时无法看到整个CoSMIC工具栏,请将其拖到可见的范围内。)

由于CoSMICgenerate_component_mpc.pl采用了不同的Home方法命名方式,你需要手工修改最后生成的Plan.cdp文件中的各Home方法的声明,如:将createControllerHome_Servant改为create_Device_ControllerHome_Servant,将createControllerHome_Impl改为create_Device_ControllerHome_ImplMonitor组件的两处修改类似。

五、编写客户程序

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

附件中的Operator客户程序以Controller组件的IOR为输入参数,对Controller组件支持的所有接口方法进行了测试,该程序与普通的CORBA客户程序并无任何差异。

六、部署与运行

步骤:

1、先通过NodeManager启动可供组件部署、运行的节点

descriptors目录下执行:

run_NodeDaemons.pl

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

%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信息。

注:如果你在这一步失败了,请认真查看错误提示确定错误原因。在执行123步之前执行:

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 DevelopmentCBD)这一概念的提出来而迅速发展,从而使得基于组件的开发并不可能那么轻松和自由,但作为软件重用方法的一种较为高级的形式,CBD仍是十分有益的。

EJB相比,CCM大量借鉴了EJB的设计思想,可以认为是EJB面向多语言层次的扩展,但由于这种扩展所带来的众多问题,使得CCM容器比EJB容器更难实现;与众多EJB服务器不同,CIAO没有一个便于操作和管理的CCM服务器,同时也没有一个合适的集成开发环境可以支撑整个CCM组件的开发过程,使得其开发过程还比较繁琐,这些都在一定程度上制约了CCM的发展;C++Java语言的差异和适用范围,也是造成CCM/EJB命运迥异的一个重要原因;由于CCM的复杂性,研究人员预期的EJB标准进行扩展以与CCM融合的局面也没有出现。

对于CIAO而言,设计者的高明之处在于,CIAO被设计成一个面向分布式嵌入式应用的CCM容器,在该领域,C++语言相对于Java具有明显的优势,因此也使得CCMEJB更适用。在其他EJB广泛应用的领域,虽然已有OpenCCMK2-CCM等面向JavaCCM实现,但在这些领域向应用开发者推广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.

你可能感兴趣的:(manager,ejb,facet,interface,Descriptor,accessor)