體驗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.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章