Понятие о 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-объектов.

<< Назад | Содержание | Вперед >>