BCB环境下CORBA实做之服务器 品雪译 建立一个CORBA服务器 BCB带有多个向导（Wizards）以简化CORBA服务器的开发过程。以下几步描述了如何用BCB建立一个CORBA服务器。 定义对象界面 使用CORBA server wizard建立一个包含起始时初始化BOA和ORBCORBA代码的服务器应用程序 编译包含界面定义的IDL文件，得到框架及存根。 用CORBA object wizard定义（并实现）类的实现。 完成4建立的类实现CORBA 对象 必要时，修改CORBA界面并将修改加进类实现中。 另外，可以选择将IDL文件注册加入界面库和对象激活守护进程。 1-1 定义对象界面 参见OVERVIEW同主题部分。 1-2 使用CORBA server wizard建立一个包含起始时初始化BOA和ORBCORBA代码的服务器应用程序 要开始一个新的CORBA服务器工程，选择File|New，再从对话框的Multitier页选取题为CORBA Server的图标。CORBA Server wizard让你指定是生成控制台程序(console application)还是窗口应用程序(windows application)。 如果建立一个控制台应用程序，可以说明是否使用VCL类。如果不选，那么所有生成的代码可以移植到其它平台上。 另外，选择应用程序类型时,还可以将任何已经存在的IDL文件加进工程中，或指定你想加进一个空白的IDL文件。服务器工程最后必须一个或多个IDL文件，该文件定义客户用以与服务器通信的界面。 注意：如果在开始CORBA工程时没有加入IDL文件，你始终可以选择Project|Add to Project(对已经存在的IDL文件）或从New Items对话框的Multitier页选择CORBA IDL file（以定义新IDL文件）来加入。 当你选定服务器类型，单击OK，CORBA Server wizard将新建一个指定类型的服务器工程，将CORBA库加入工程文件，加入初始化ORB（对象请求代理）和BOA（基本对象配适器）的起始代码。 自动生成的代码定义两个变量：orb和boa，你可以用它们与ORB和BOA通信。 CORBA::ORB_var orb = CORBA::ORB_init(argc, argv); CORBA::BOA_var boa = orb->BOA_init(argc, argv); 如果指定为控制台应用程序，还会看到下面这行： boa->impl_is_ready(); 这个调用使你的程序可以从BOA接收消息，正如Windows消息循环允许一个窗口应用程序从Windows收到消息。（而窗口型应用程序使用Windows消息循环处理CORBA消息）。 ORB和BOA的细节见VisiBroker文档（可到inprise网站去下载）。 1-3 编译包含界面定义的IDL文件，得到框架及存根。 一旦你将描述CORBA界面的IDL文件加入到CORBA工程中，你就已经准备好生成存根和框架类了，这两个类将管理服务器与客户应用程序的通信。 生成存根和框架类，很简单，编译一下IDL文件就得到了。当IDL文件显示在代码编辑器和工程管理器中时选择Project|Compile Unit，或在project manager的IDL文件上单击鼠标右键并选Compile就可以了。IDL编译器生成两个文件，它们会出现在工程管理器和代码编辑器中： 服务器文件(xxx_s.cpp)。这个文件包含框架类的实现。 客户端文件(xxx_c.cpp)。这个文件包含存根类的实现。 工程Option对话框的CORBA页允许你影响从IDL文件生成stub和skeleton的方式。例如，你可以指定只想在工程中包含服务器单元，或指定要包含Tie类（用于当服务器使用代理模式delegation model时）。这些选项影响当前工程中所有的IDL文件。 注：　　delegation model，当使用已经写好的从不允许多实例VCL类衍生的CORBA服务器或想与其它非CORBA应用程序共享时，可以使用不其于CORBA或不生成服务器文件的服务器类。输出不是从框架类衍生的对象时，应用程序必须使用delegation model。它是这样的模式：框架类实例并不直接实现CORBA对象，而是将界面调用传给完全分离现实的类。即实现的类不是继承自skeleton而是被skeleton调用。 要使用delegation model就要在编译IDL文件时生成tie类（绑定类）。这样编译得到的框架类多出一个特殊的tie类。它充当你实现的类及其方法的代理，它将用户调用委派给你实现的类。 详情见联机帮助。 1-4 用CORBA object wizard定义（并实现）实现类。 //译者：这里大量使用“实现”一词，其意思基本上就是实际生成。 要实现定义在IDL文件中的CORBA对象，必须实现每个框架类。要完成它，选择File|New，从对话框的mutitier页中选择CORBA Object Implementation。这将触发CORBA Implementation Wizard。 CORBA Object Implementation Wizard列出工程包含的IDL文件中定义的所有界面。选择一个IDL文件，再从这个文件中选择要实现的界面。提供一个实现类的名字并指定命名.h、.cpp文件的单元名。这些文件将包含对象的定义和实现。 在向导中说明实现的类是否应该继承自动生成的框架类，或者要使用delegation model（tie类）。如果使用代理模式（且服务器是VCL可用的），还应该告诉向导使你实现的类成为一个数据模块（data module）。这允许你向实现的数据模块中加入所需的构件（components）。 可以告诉向导加入代码以便应用程序启动时实例化你的CORBA对象，这样他们就有能力收到客户请求。如果你启动时实例化对象，必须给他们提供名字供客户定位用。当客户应用程序动态绑定到你的CORBA对象时，他们使用这个名字指明想要的对象。 当你点下OK时，CORBA Object Implementation Wizard生成你实现的类的定义并生成其实现，这个实现的每个方法体都是空的。如果你指定，它也可以加入实例化对象的代码。 在这些改变加进工程之前，还可以查看、编辑他们。要这样做的话，在点OK之前选上标题为Show Updates的复选框。在你查看修改之后，他们被加进工程。如果你向导结束时没有选择查看和修改，你可以以后再在代码编辑器里编辑它们。 要实现多个界面，就要多次使用CORBA对象实现向导，每次一个界面。但是，你可以提供多个名字来让向导生成单个实现类的多个实例。 1-5 完成4建立的类实现CORBA对象 如果你没有使用delegation model（代理模式），那么实现类是将从框架类衍生而来，框架类是编译IDL文件时生成的。否则就你的实现类就不会有明确祖先。你可以改变这个类定义使它继承服务程序的其它类，但不要移去任何已有的对框架类的继承。 考虑如下取自IDL文件account.IDL中的片断（该文件在BCB例子中）： interface Account { float balance(); }; 当IDL文件被编译时（不带tie类），生成的服务器头（account_s.hh）包括了带有对应界面中每个方法的纯虚方法的框架定义。 CORBA Object Wizard创建一个实现单元，在该单元中从框架类（_sk_Account）衍生一个实现类。它的头文件看起来如下： #ifndef Interface1ServerH #define Interface1ServerH #include "account_s.hh" //--------------------------------------------------------------------------- class AccountImpl: public _sk_Account { protected: public: AccountImpl(const char *object_name=NULL); CORBA::float balance(); }; #endif 你可以在这个类定义加入所需的附加数据成员或方法的定义。例如： #ifndef Interface1ServerH #define Interface1ServerH #include "account_s.hh" //--------------------------------------------------------------------------- class AccountImpl: public _sk_Account { protected: CORBA::float _bal; public: void Initialize(CORBA::float startbal); // not available to clients AccountImpl(const char *object_name=NULL); CORBA::float balance(); }; #endif 这些附加的方法和属性来在服务器程序里有效，但没有导出给客户端。 在生成的.cpp文件中，填写实现类的主体，就得到了可以工作的CORBA对象： AccountImpl::AccountImpl(const char *object_name): _sk_Account(object_name) { } CORBA::float AccountImpl::balance() { return _bal; }; void Initialize(CORBA::float startbal) // not available to clients { _bal = startbal; } 当实现CORBA对象的方法时，记住客户请求可能到达不同的线程。因此，你得预防线程冲突。 注意：在你的CORBA服务器中可以编写与BOA交互的代码。例如，使用BOA，可以临时令服务器对象隐藏或失活，过一回再重新激活他们。详见VisiBroker Programmer Guid。 缺省地，CORBA应用程序是多线程的。这意味着在实现CORBA对象时必须预防线程冲突。通过链接到orb_b.dll和orb_br.dll也可以创建单线程的CORBA服务器，但这样做时就失去了IDE对CORBA的支持或在服务器程序中使用VCL的好处。 除非你指定，否则BOA用缓冲池缓冲客户线程，就是说客户可以使用任何可用的线程。然而也可以确保每个线程始终使用相同的线程，即用逐会话线程策略启动BOA。当所有客户使用同一线程时，可以在线程变量中保存持久客户的信息。详见VisiBroker Programmer Guid。 Visibroker库包含可以帮助你避免线程冲突的类。它们在vthread.h中定义，安装在VisiBroker的include目录里。VisiBroker线程支持类包括一个互斥锁（mutex）名为VisMutex，一个条件变量（VISCondistion）及读写锁定，读写锁定工作方式类似于VCL的多重读取独占写同步装置（VISRWLock）。使用这些类的好处是他们可被移植到Visibroker支持的任意平台上。 例如，你可以在类实现加入VisMutex域以保护实例数据： class A{ VISMutex _mtx; ... } 然后，当你需要同步实例数据存取时，可以在存取实例数据的任何方法中锁上互斥锁： void A::AccessSharedMemory(...) { VISMutex_var lock(_mtx); //请一把锁，锁要已经被释放退出。 //其它代码，存取实例数据。 } 注意，互斥锁会在AccessShareMemory执行完毕后释放，即使在方法体内抛出了意外。 如果你在服务器程序中使用VCL，BCB包括许多类帮助你预防线程冲突。见联机帮助的Writing multi-threaded applications部分。 1-6 必要时，修改CORBA界面并将修改加进类实现中。 如果在用CORBA Object Wizard生成实现类之后修改IDL文件中的界面，CBuilder让你可以自动更新服务器工程反映这些改变并保证已经写实现类代码不会丢失。 在你修改IDL文件之后，选择Edit|CORBA Refresh。这条命令重编译IDL文件以自动生成服务器和客户文件反映新的界面定义和当前的编译选项。如果IDL文件编译成功，使用Project Updates对话框，你可以预览并编辑C++BUILDER将做的修改。 注意：如果CBuider找不到与现存实现类相应的界面，会问你是否改变了界面名字。如果答是，会提示你说明界面是怎么改名的，这样实现的类可以正确匹配界面。 当你使用CORBA Refresh命令时，新的方法被加进你的实现类中，申明被更新以反映对现有方法和属性的改变。然而，某些修改不会引起更新。特别是，如果你删除一个方法或属性，它的代码是不删除的。相反，被删除的方法保留在类中，但对CORBA客户不再可用了。相似的，如果你改名一个方法或属性，会被当作一个删除和一个增加，老的方法或属性将保留，并为新方法、属性生成代码。 IDL文件注册 尽管注册不是必需的，但如果你只使用静态绑定客户调用到服务器对象，建议注册界面。有两个工作可用来注册界面： 界面仓库(Interface Repository）。注册到界面仓库，客户可以在使用动态请求界面时(Dynamic Invocation Interface）编程获取界面信息。注册到界面库也便于其它开发者在写客户应用时查看你的界面。 对象激活守护进程(Object Activation Daemon）。注册至OAD，便利可以直到客户需要之时，服务器才加载或对象才实例化。这样可节省服务器上的资源。