潘爱民 北京大学计算机科学技术研究所 http://www.icst.pku.edu.cn/CompCourse 组件技术——最后一讲 潘爱民 北京大学计算机科学技术研究所 http://www.icst.pku.edu.cn/CompCourse
内容 框架和类库 CORBA overview 复习
framework 领域工程 组件库 提供定制功能,允许开发人员对于框架主体部分进行修改 不同层次上的framework 单个系统 ——〉一类系统 针对应用的抽象 组件库 提供定制功能,允许开发人员对于框架主体部分进行修改 不同层次上的framework 基于二进制代码的framework,例如MMC 基于源代码的framework,例如MFC
基于二进制的framework 接口: 通信模型 为应用中的组件提供二进制接口 粒度 用户组件与框架进行通信 用户组件之间如何通信? 以对象形式封装 以功能为单位 粒度 大而全的接口 小型接口,允许动态发现新的接口 通信模型 用户组件与框架进行通信 用户组件之间如何通信? 通过框架传递信息 通过框架建立直通模型
基于源代码的framework 接口: 通信模型 一般为抽象类,用户提供虚函数的实现,并注册到主框架中 用户定制的余地比较大 用户组件与框架进行通信 用户组件之间容易建立起直通途径
从派生类传播类型到基类的一种模式 意图: 解决方案 基类有时需要根据子类的类型执行一些功能,而基类又不可能直接得到子类的静态类型 这对于generic programming非常重要,因为编译器要靠静态类型来实例化模板(函数或者类) 解决方案 用虚函数不能解决问题 —— runtime多态性 在子类中插入一个函数,由该函数调用模板函数或者模板类,或者该函数调用基类中的模板成员函数 仅对基于源代码的framework适用
Framework举例 为报社提供一套framework SnapIn仓库 IFrameSite SnapIn DLL SnapIn DLL ISnapInfo SnapIn对象 SnapIn对象 SnapIn对象 SnapIn仓库 FrameSite FrameSite FrameSite Snap-In管理器 UI管理器 Database管理器 Security管理器
可重用类库的设计(一) 在所有的系统设计中,可重用类库的设计是难度比较大的,要做到: 经验非常重要 类库的基础 参考成功的类库 使用:灵活性和易用性 功能:广泛性和效率 经验非常重要 实现同样的功能会有许多不同的道路,如何选择?效果怎么样? 类库的基础 是否使用其他的类库?是否使用特殊的平台和编译环境? 参考成功的类库 起点要高
可重用类库的设计(二) 接口的设计 内存管理 这是类库的关键,会影响到类库的使用 大而全的接口并不理想 接口的语义一定要清晰 接口的类型:C/C++ 大而全的接口并不理想 接口的语义一定要清晰 facade模式 内存管理 保证内存分配和释放的一致性 使用要方便 [out]参数的资源由谁来申请?谁知道size? 是否使用自定义的内存分配器,例如针对小对象的分配器
可重用类库的设计(三) 使用各种模式 模板类库的特殊性 模式是经验,成功的典范 policy模式允许使用者定制类 结构型模式有助于建立起更加合理的结构模型,而不至于层次错综复杂 行为型模式有助于各个类之间有更好的协作模型 创建型模式可以提供各种合理的创建机制 模板类库的特殊性 利用模板类型实现compile-time的预处理 熟悉编译器的特性 控制模板生成代码
可重用类库的设计(四) 行为前置和延后 用宏来封装代码 代码风格 类库的优化 在基类中提供缺省的实现 纯虚函数 —— 强制子类提供实现 利用functor或者函数指针 要求(必须)子类调用父类的实现 用宏来封装代码 代码风格 类库的优化 优化需要用到内部知识,是否暴露这些知识 允许使用者用policy进行配置,用不同的实现配置类 类似于policy的思想,在细节点上用开关进行控制
可重用类库的设计(五) 类库的调试 类库的测试 类库的发行 类库内部调试,使用assert支持 比应用系统的测试更加严格 是否提供源代码? 文档 编译设置
可重用类库的设计(六) 举例:MFC/ATL MFC ATL 同时也具有源代码框架的结构 传统意义上的C++类库,对Win32进行了封装 以便于使用为主要目标,优化较少 用到了许多patterns,吻合Windows应用模型 涉及到许多类库设计技术 与Wizard结合产生基本代码 ATL 用到了generic programming中许多新的技术 模板技术 优化比较突出
CORBA Overview ORB(Object Request Broker ) Interface Definition Language (IDL) CORBA Communications Model: IIOP CORBA Object Model: IOR CORBA Clients and Servers client stubs and server skeletons CORBAservices and CORBAfacilities
CORBA结构
CORBA Specification overview OMG IDL Interface: ORB Interface、DII/DSI、Interface Repository POA Interoperability GIOP、IIOP、IOR Mapping COM、Automation C mapping、C++ mapping、SmallTalk Service Specification(单独的文档) naming service、event service、transaction service、security service、trade service、……
OMG IDL 类C风格的语言 定义接口: interface 基本数据类型 常数、常量表达式 构造数据类型 void、boolean、char、wchar、octet、 (unsigned) short、(unsigned) long、(unsigned ) long long、float、double、long double、string、any 常数、常量表达式 构造数据类型 typedef、enum、struct、union、数组、sequence
OMG IDL(续) Interface,例如: 接口方法中参数的方向属性 异常:用户自定义异常和系统异常 oneway操作 interface Account { void deposit( in unsigned long amount ); void withdraw( in unsigned long amount ); long balance(); }; 接口方法中参数的方向属性 异常:用户自定义异常和系统异常 oneway操作 module、include 允许多重继承,与实现(implementation)无关
C++ mapping 基本数据类型与C++的对应关系 枚举类型 复合类型 接口类型 特殊标识符的处理 module ——〉namespace IDL类型——〉CORBA::中的数据类型 字符串处理不用new、delete,而用专门的函数 枚举类型 复合类型 除了类型本身外,另生成一个_var类型(类似auto_ptr) 接口类型 _ptr类型、_var类型
客户端C++ mapping 客户通过stub调用对象的方法 客户如何得到对象引用 客户如何调用对象方法
客户端C++ mapping 客户通过ORB接口获得初始的对象引用 首先初始化ORB并获得ORB接口 然后利用字符串形式的引用获得内部的对象引用 int main( int argc, char *argv[] ) { // ORB initialization CORBA::ORB_var orb = CORBA::ORB_init( argc, argv, "local-orb" ); CORBA::Object_var obj = orb->string_to_object( argv[1] ); Account_var client = Account::_narrow( obj ); client->deposit( 700 ); client->withdraw( 250 ); cout << "Balance is " << client->balance() << endl; return 0; }
服务器端C++ mapping 对象与OA进行通信
服务器端C++ mapping(续二) Servant和CORBA对象 客户请求 ORB POA Manager POA Servant
服务器端C++ mapping(续三) int main (int argc, char *argv[]) { CORBA::ORB_var orb = CORBA::ORB_init (argc, argv, ”local-orb”); CORBA::Object_var poaobj = orb->resolve_initial_references ("RootPOA"); PortableServer::POA_var poa = PortableServer::POA::_narrow (poaobj); PortableServer::POAManager_var mgr = poa->the_POAManager(); Account_impl * account = new Account_impl; PortableServer::ObjectId_var oid = poa->activate_object (account); …… // Write reference to file mgr->activate (); orb->run(); poa->destroy (TRUE, TRUE); delete micocash; return 0; }
POA: Portable Object Adapter POA负责从CORBA对象到servant之间的对应 CORBA对象与servant对象的生命周期相互分开 POA对于CORBA服务器在性能、资源利用、可伸缩性等方面具有重要的意义 POA策略,控制: 对象生命周期:永久对象、暂态对象 对象标识符:在POA范围内唯一 POA Active Object Map:从ObjectID到servant的映射 请求处理策略:servant manager 线程模型 ……
POA: Portable Object Adapter(续) 所有的工作都从RootPOA开始 所有的POA构成一个树状层次结构 利用RootPOA的create_POA创建新的POA 在创建POA时指定策略,CORBA::PolicyList POA提供的功能 创建对象 注册servant,激活对象 提供ObjectID、ObjectReference之间的转换 servant manager编程 POA manager:控制来自客户的请求 多个POA可以共享同一个POA manager ORB事件处理
服务器端C++ mapping(续四) 利用idl生成的框架类编写servant类 编写main函数 从ORB初始化开始 考虑POA的策略,简单的程序可以使用默认的RootPOA 创建servant并注册 处理对象引用 进入主循环 阻塞方式:orb->run() 非阻塞方式: orb->work_pending() orb->perform_work()
用C++开发CORBA应用 基本的C++知识,包括链表结构、智能指针的知识 理解CORBA的client/server应用模型 包含头文件 链接库文件 掌握IDL编译器 掌握ORB接口 如何传递对象引用
ORB接口 初始:CORBA::ORB_Init,通过命令行传递参数 对象引用到字符串的转换操作 获得初始接口 对象引用:CORBA::Object 获得初始接口 resolve_initial_reference
DII:Dynamic Invocation Interface 客户程序可以不通过IDL编译器生成的stub而调用对象的方法,步骤: 客户首先得到对象引用:CORBA::Object_ptr 创建一个请求对象CORBA::Request_ptr 把调用参数放到链表中:CORBA::NVList_ptr,指明类型和数值,也指明返回值 调用CORBA::Request_ptr->Invoke方法 服务器并不知道客户程序是通过stub还是DII调用其方法的 客户程序在编译时刻可以没有idl类型知识 比较:COM Automation中IDispatch接口的用法
用C++开发CORBA应用:客户 用IDL生成stub代码以及类型定义头文件 在客户的main函数中,初始化orb,获得orb对象 获得对象引用,并调用_narrow向下转换 调用对象的方法 如果使用DII,则需要用到请求对象,并处理参数 如果不用_var类型的话,释放orb和对象引用
DSI:Dynamic Skeleton Interface 服务器程序可以动态实现对象,而不利用IDL编译器生成的skeleton代码 服务器实现一个对象,如下所示 class GenericServant : virtual public PortableServer::DynamicImplementation { virtual void invoke (CORBA::ServerRequest_ptr req); virtual CORBA::RepositoryId _primary_interface ( const PortableServer::ObjectId &, PortableServer::POA_ptr); }; main函数的处理不变 重点在于invoke函数,利用CORBA::ServerRequest对象获得所有的类型信息 对于客户透明 比较:COM Automation中IDispatch接口的实现
用C++开发CORBA应用:服务器 用IDL生成skeleton代码以及类型定义头文件 在客户的main函数中,初始化orb,获得orb对象 根据服务器应用的需要,处理POA 创建servant对象 如果使用DSI,那么invoke方法是servant对象的关键 如何把对象引用传递出去?字符串?名字服务? 调用orb->run()或者orb->perform_work()
CORBA IIOP和IOR General Inter-ORB Protocol:CORBA定义了GIOP作为其互用性框架结构,包括数据传输、数据表示、消息格式 Internet Inter-ORB Protocol:IIOP是GIOP的具体实现,建立在TCP/IP基础上 IOR:Interoperable object reference RepositoryID Data for Protocol 1 Data for Protocol 2 … Endpoint Info Object Key Object ID
IOR例子 Repo Id: IDL:GenericServer:1.0 IIOP Profile Version: 1.0 Address: inet:162.105.178.100:12122 Location: corbaloc::162.105.73.196:12122//25607/991958392/%5f0 Key: 2f 32 35 36 30 37 2f 39 39 31 39 35 38 33 39 32 /25607/991958392 2f 5f 30 /_0 Multiple Components Profile Components: Native Codesets: normal: ISO 8859-1:1987; Latin Alphabet No. 1 wide: ISO/IEC 10646-1:1993; UTF-16, UCS Transformation Format 16-bit form Key: 00 .
CORBA结构
CORBA services:naming service 建立起“名称-对象引用”的映射关系 在内部以层次结构的形式组织名字映射,类似于文件系统的结构。 所有的类型和接口定义位于CosNaming域中 NamingContext接口负责所有有关命名服务相关的功能,例如: 创建新的context、删除context bind操作、unbind操作 resolve操作 怎样获得初始namingcontext? 通过orb->resolve_initial_reference ORB厂商应该提供命名服务工具
naming service示例 CORBA::Object_var nsobj = orb->resolve_initial_references ("NameService"); CosNaming::NamingContext_var nc = CosNaming::NamingContext::_narrow (nsobj); CosNaming::Name name; name.length (1); name[0].id = CORBA::string_dup ("myAccount"); name[0].kind = CORBA::string_dup (""); // 服务器方 Account_ptr acc = new Account_impl (); nc->bind (name, acc); // 客户方 CORBA::Object_var obj; obj = nc->resolve (name); Account_var client = Account::_narrow( obj );
CORBA services:其他服务 Event service Trade Service Time Service 提供松耦合事件模型的机制 Trade Service 提供了更加灵活的对象查找服务 Time Service 提供了与时间有关的服务,统一了时间的表达方式 Concurrency service Security Service Transaction Service ……
ORB产品 考察ORB:支持平台、支持语言、性能 商业ORB 自由ORB Orbix,IONA公司,完全支持CORBA 2.3规范 Visibroker,Inprise公司,4.0版完全支持2.3规范 Netscape communicator浏览器嵌入Visibroker 自由ORB ORBit,遵循CORBA 2.2规范,支持C语言,性能较高 mico,GNU,OpenSource TAO,美国华盛顿大学分布式对象计算研究小组 omniORB,AT&T剑桥实验室
CORBA与COM的比较 标准的层次不同 CORBA跨平台优势 COM在Windows平台上的优势 COM的效率有优势 跨语言的策略不同 CORBA与Java结合的优势 CORBA跨平台优势 COM在Windows平台上的优势 COM的效率有优势 跨语言的策略不同 COM组件丰富、CORBA开发简单 CORBA不适合开发UI组件 CORBA与COM互补策略