Понятие о stub- и skeleton-объектах
Итак, мы убедились,что создание 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-объектов.
<< Назад |
Содержание |
Вперед >>