Итак, мы убедились,что создание CORBA-приложений с помощью Delphi, на первый взгляд, мало отличается от создания COM-приложений. Для обеспечения взаимодействия клиента и сервера, функционирующих в разных адресных пространствах или на разных компьютерах (в том числе и в разных операционных системах), как и в случае других технологий объектно-ориентированных распределенных вычислений, используются объекты, расположенные в адресных пространствах клиента и сервера и обменивающиеся данными между собой. В терминологии CORBA они называются stub и skeleton. Stub - это представитель сервера в адресном пространстве клиента (иногда для его обозначения используют и термин proxy). Skeleton - это представитель клиента в адресном пространстве сервера (рис. 13).
Рис. 13. Взаимодействие CORBA-клиента и CORBA-объекта
Клиентское приложение взаимодействует со stub-объектом, вызывая его методы (названия которых совпадают с названиями методов серверного объекта). В действительности stub-объект обращается к клиентской части Object Request Broker (ORB), обращающейся, в свою очередь, к специализированному сервису middleware - Smart Agent (он может функционировать на каком-либо из компьютеров сети), представляющему собой не что иное, как directory service - службу, обеспечивающую поиск доступного сервера, содержащего реализацию запрашиваемого клиентом объекта.
Когда сервер найден, в его адресном пространстве создается запрошенный серверный объект, содержащий, в свою очередь, skeleton-объект, которому ORB передает запрос клиента с помощью Basic Object Adaptor (BOA). Используя эту службу, skeleton регистрирует созданный серверный CORBA-объект с помощью Smart Agent, а также сообщает о доступности, факте создания и о готовности объекта принимать запросы клиента.
Как и в случае COM, stub-объект и skeleton-объект взаимодействуют между собой с помощью маршалинга, представляющего собой обмен данными (передаваемые данные упаковываются в так называемый marshalling packet и распаковываются после передачи в другое адресное пространство) и передачу указателей на интерфейсы и аргументы функций между этими объектами.
Оба эти объекта (stub и skeleton) в случае использования Delphi 4 создаются автоматически при определении интерфейса CORBA-объектов в редакторе библиотеки типов.
Проиллюстрируем сказанное выше на примере созданнного нами ранее CORBA-сервера. При создании CORBA-объекта (в нашем случае модуля данных) автоматически были сгенерированы два модуля. Первый из них - serv_TLB.pas (так называемый stub-and-skeleton unit) - представляет собой автоматически сгенерированный код классов stub- и skeleton-объектов:
unit serv_TLB; // ************************************************************************ // // WARNING // // ------- // // The types declared in this file were generated from data read from a // // Type Library. If this type library is explicitly or indirectly (via // // another type library referring to this type library) re-imported, or the // // 'Refresh' command of the Type Library Editor activated while editing the // // Type Library, the contents of this file will be regenerated and all // // manual modifications will be lost. // // ************************************************************************ // // PASTLWTR : $Revision: 1.11.1.75 $ // File generated on 11.01.99 16:12:40 from Type Library described below. // ************************************************************************ // // Type Lib: E:\Program Files\Borland\Delphi4\Projects\CORBA\serv.tlb // IID\LCID: {42ED5200-A96E-11D2-B185-000000000000}\0 // Helpfile: // HelpString: Project1 Library // Version: 1.0 // ************************************************************************ // interface uses Windows, ActiveX, Classes, Graphics, OleCtrls, StdVCL, SysUtils, CORBAObj, OrbPas, CorbaStd; // *********************************************************************// // GUIDS declared in the TypeLibrary. Following prefixes are used: // // Type Libraries : LIBID_xxxx // // CoClasses : CLASS_xxxx // // DISPInterfaces : DIID_xxxx // // Non-DISP interfaces: IID_xxxx // // *********************************************************************// const LIBID_serv: TGUID = '{42ED5200-A96E-11D2-B185-000000000000}'; IID_Icrb1: TGUID = '{42ED5201-A96E-11D2-B185-000000000000}'; CLASS_crb1: TGUID = '{42ED5203-A96E-11D2-B185-000000000000}'; type // *********************************************************************// // Forward declaration of interfaces defined in Type Library // // *********************************************************************// Icrb1 = interface; Icrb1Disp = dispinterface; // *********************************************************************// // Declaration of CoClasses defined in Type Library // // (NOTE: Here we map each CoClass to its Default Interface) // // *********************************************************************// crb1 = Icrb1; // *********************************************************************// // Interface: Icrb1 // Flags: (4416) Dual OleAutomation Dispatchable // GUID: {42ED5201-A96E-11D2-B185-000000000000} // *********************************************************************// Icrb1 = interface(IDataBroker) ['{42ED5201-A96E-11D2-B185-000000000000}'] function Get_Table1: IProvider; safecall; property Table1: IProvider read Get_Table1; end; // *********************************************************************// // DispIntf: Icrb1Disp // Flags: (4416) Dual OleAutomation Dispatchable // GUID: {42ED5201-A96E-11D2-B185-000000000000} // *********************************************************************// Icrb1Disp = dispinterface ['{42ED5201-A96E-11D2-B185-000000000000}'] property Table1: IProvider readonly dispid 1; function GetProviderNames: OleVariant; dispid 22929905; end; Tcrb1Stub = class(TDataBrokerStub, Icrb1) public function Get_Table1: IProvider; safecall; end; Tcrb1Skeleton = class(TDataBrokerSkeleton) private FIntf: Icrb1; public constructor Create(const InstanceName: string; const Impl: IUnknown); override; procedure GetImplementation(out Impl: IUnknown); override; stdcall; published procedure Get_Table1(const InBuf: IMarshalInBuffer; Cookie: Pointer); end; Cocrb1 = class class function Create: Icrb1; class function CreateRemote(const MachineName: string): Icrb1; end; Tcrb1CorbaFactory = class class function CreateInstance(const InstanceName: string): Icrb1; end; implementation uses ComObj; { Tcrb1Stub } function Tcrb1Stub.Get_Table1: IProvider; var OutBuf: IMarshalOutBuffer; InBuf: IMarshalInBuffer; begin FStub.CreateRequest('Get_Table1', True, OutBuf); FStub.Invoke(OutBuf, InBuf); Result := UnmarshalObject(InBuf, IProvider) as IProvider; end; { Tcrb1Skeleton } constructor Tcrb1Skeleton.Create(const InstanceName: string; const Impl: IUnknown); begin inherited; inherited InitSkeleton('crb1', InstanceName, 'IDL:serv/Icrb1:1.0', tmMultiThreaded, True); FIntf := Impl as Icrb1; end; procedure Tcrb1Skeleton.GetImplementation(out Impl: IUnknown); begin Impl := FIntf; end; procedure Tcrb1Skeleton.Get_Table1(const InBuf: IMarshalInBuffer; Cookie: Pointer); var OutBuf: IMarshalOutBuffer; Retval: IProvider; begin Retval := FIntf.Get_Table1; FSkeleton.GetReplyBuffer(Cookie, OutBuf); MarshalObject(OutBuf, IProvider, Retval); end; class function Cocrb1.Create: Icrb1; begin Result := CreateComObject(CLASS_crb1) as Icrb1; end; class function Cocrb1.CreateRemote(const MachineName: string): Icrb1; begin Result := CreateRemoteComObject(MachineName, CLASS_crb1) as Icrb1; end; class function Tcrb1CorbaFactory.CreateInstance(const InstanceName: string): Icrb1; begin Result := CorbaFactoryCreateStub('IDL:serv/crb1Factory:1.0', 'crb1', InstanceName, '', Icrb1) as Icrb1; end; initialization CorbaStubManager.RegisterStub(Icrb1, Tcrb1Stub); CorbaInterfaceIDManager.RegisterInterface(Icrb1, 'IDL:serv/Icrb1:1.0'); CorbaSkeletonManager.RegisterSkeleton(Icrb1, Tcrb1Skeleton); end.
В этом модуле определены skeleton-объекты для всех интерфейсов, поддерживаемых данным сервером. Все они являются наследниками класса TCorbaSkeleton и в действительности реализуют маршалинг - обмен данными между skeleton-объектом и соответствующим stub-объектом клиента. Редактировать этот модуль не рекомендуется.
Второй автоматически сгенерированный модуль (в нашем случае serv2.pas) содержит реализацию методов сервера. Этот файл подлежит редактированию (в нем мы создавали обработчики событий, связанных с созданием и уничтожением экземпляра модуля данных):
unit serv2; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ComObj, VCLCom, StdVcl, BdeProv, DataBkr, CorbaRdm, CorbaObj, serv_TLB, Db, DBTables; type Tcrb1 = class(TCorbaDataModule, Icrb1) Table1: TTable; Table2: TTable; DataSource1: TDataSource; procedure crb1Create(Sender: TObject); procedure crb1Destroy(Sender: TObject); private { Private declarations } public { Public declarations } protected function Get_Table1: IProvider; safecall; end; var crb1: Tcrb1; implementation {$R *.DFM} uses serv1, CorbInit, CorbaVcl ; function Tcrb1.Get_Table1: IProvider; begin Result := Table1.Provider; end; procedure Tcrb1.crb1Create(Sender: TObject); begin Form1.UpdateCount(1); end; procedure Tcrb1.crb1Destroy(Sender: TObject); begin Form1.UpdateCount(-1); end; initialization TCorbaVclComponentFactory.Create('crb1Factory', 'crb1', 'IDL:serv/crb1Factory:1.0', Icrb1, Tcrb1, iMultiInstance, tmSingleThread); end.
Как и в случае редактирования библиотеки типов COM-сервера, при создании новых свойств и методов CORBA-сервера "заготовки" методов генерируются в этом модуле автоматически при выполнении операции его обновления (кнопка Refresh панели инструментов библиотеки типов). Этот же модуль отвечает и за создание для каждого из доступных клиентам интерфейсов объекта TCorbaFactory, создающего или находящего подходящий экземпляр класса реализации описанных методов и передающего его интерфейс соответствующему классу skeleton-объектов.
<< Назад | Содержание | Вперед >>