Глава 31
Технология CORBA
Спецификация CORBA (Common Object Request Broker Architecture) разработана группой компаний OMG (Object Management Group) и описывает основанный на объектах способ создания распределенных приложений, то есть как осуществляется взаимодействие объектов клиента и сервера. После инсталляции специального программного обеспечения сервер приложений и клиенты могут взаимодействовать, используя объекты и интерфейсы CORBA.
Программное ядро CORBA разработано для всех основных аппаратных и программных платформ. Объекты приложений CORBA, используя в качестве посредника ПО CORBA, взаимодействуют с другими объектами CORBA, используя для этого интерфейсы. Для поиска и организации взаимодействия объектов CORBA предназначено специальное ПО — объектный брокер запросов (Object Request Broker — ORB) и сетевой агент Smart Agent. В результате клиентские и серверные приложения CORBA, работающие на платформе Wintel, одинаково легко взаимодействуют с аналогичными приложениями CORBA, откомпилированными для операционных систем UNIX или AS400.
Таким образом, использование архитектуры CORBA позволяет разработчикам многоуровневых приложений в Delphi создавать действительно гетерогенные системы, используя преимущества каждой платформы.
Основу архитектуры CORBA составляет объектный брокер запросов VisiBroker for C++ ORB V 3.3.2, который управляет взаимодействием клиентов и серверов в распределенной сетевой среде. Взаимодействие со средой и приложениями Delphi обеспечивает дополнительная библиотека orbpas50.dll.
Кроме ORB, набор программных средств, обеспечивающих работоспособность приложений CORBA, разрабатываемых в Delphi, включает:
Basic Object Adaptor (BOA) — служба, обеспечивающая регистрацию приложения-сервера в распределенной сетевой среде.
Smart Agent — распределенная служба, обеспечивающая предоставление информации о запущенных в сетевой среде серверах CORBA.
Object Activation Daemon (OAD) — специальное ПО, обеспечивающее автоматический запуск сервера CORBA (если он еще не запущен) при обращении к нему клиента CORBA.
В этой главе рассматриваются следующие вопросы.
Архитектура CORBA
Клиентские приложения и приложения-серверы инкапсулируют объекты CORBA. Спецификация CORBA определяет, каким образом должны взаимодействовать объекты CORBA в распределенной гетерогенной среде.
Архитектура CORBA представляет собой совокупность программных средств, которые обеспечивают передачу объектных запросов в рамках распределенной гетерогенной среды в соответствии со спецификацией CORBA. Архитектура включает следующие элементы.
Брокер запросов VisiBroker for C++ ORB (Version 3.3.2) — главная составная часть архитектуры и предназначен для обработки запросов и организации взаимодействия между объектами клиентов и серверов.
Служба Implementation Repository сохраняет информацию о зарегистрированных объектах, их идентификаторах и т. д. Кроме этого, в нем хранятся дополнительные сведения о распределенной среде: местоположение ресурсов, отладочная информация, уровень безопасности и т. д. VisiBroker использует эту службу для обращения к объектам.
Basic Object Adaptor обеспечивает активизацию объектов сервера при их вызове клиентом. Это ПО регистрирует объект в сетевой распределенной службе Smart Agent.
Служба Smart Agent обеспечивает взаимодействие частей архитектуры в сетевой среде. Для работы приложения CORBA достаточно одного запущенного экземпляра этой службы на любом компьютере среды. Он объединяет между собой отдельные брокеры запросов.
Object Activation Daemon выполняет автоматический запуск сервера при первом запросе от клиента.
Взаимодействие служб CORBA
Рассмотрим порядок взаимодействия частей архитектуры CORBA при работе распределенного приложения.
ORB должен быть установлен и функционировать на каждом компьютере, содержащем приложения CORBA. Он обеспечивает управление запросами от клиента серверу.
Клиентское приложение, используя внутренний механизм выполнения запросов (см. ниже), обращается к экземпляру VisiBroker, функционирующему на компьютере клиента.
После получения запроса объектный брокер запроса опрашивает сетевое окружение с целью найти работающий экземпляр службы Smart Agent. Для этого ORB рассылает по сети сообщение. Рабочий сеанс устанавливается с той службой Smart Agent, которая откликнется первой. Соединение брокера запросов с сетевой службой работает на основе протокола UDP. Для этого им требуется иметь одинаково настроенные номера сетевых портов.
Служба Smart Agent имеет доступ к базе данных службы Implementation Repository, в которой зарегистрированы все работающие серверы, запущенные вручную, и все серверы, запускаемые автоматически с помощью Object Activation Daemon. В соответствии с полученной информацией. Smart Agent передает запрос нужному экземпляру брокера.
Найденный ORB обеспечивает передачу запроса серверу. Результат запроса возвращается клиенту аналогичным образом.
Регистрация сервера CORBA
Теперь рассмотрим процесс регистрации серверов CORBA и их объектов.
Сервер может запускаться вручную или автоматически при помощи OAD. При ручном запуске сервер обращается к службе BOA, функционирующей на компьютере сервера. BOA передает информацию о местоположении и параметрах сервера для хранения службе Implementation Repository. А к Implementation Repository обращается Smart Agent при адресации клиентского запроса.
Регистрация сервера для ручного запуска осуществляется из среды Delphi или при помощи утилиты idl2ir.
Для автоматического запуска сервер должен быть зарегистрирован при помощи утилиты oadutil. При автоматическом запуске служба Smart Agent обращается к OAD, который и запускает приложение-сервер.
Внутренний механизм передачи запросов
Теперь перейдем к устройству частей многоуровневого приложения в архитектуре CORBA.
В сервере приложений взаимодействие с клиентами обеспечивает специальный объект, называемый скелетом (skeleton). Скелет работает в адресном пространстве сервера. Он получает запросы от клиента, преобразует их и передает серверу. Ответы сервера он форматирует и при посредстве рассмотренных выше служб отправляет клиенту, а точнее, заглушке.
Заглушка (stub) — особый объект CORBA, который функционирует в адресном пространстве клиента. Этот объект перехватывает запросы клиента, преобразует их в транспортный формат и отправляет серверу при помощи служб CORBA, а точнее, скелету. Он же получает ответы сервера.
Рассмотрим ситуацию, когда клиент отправляет запрос серверу.
Клиент передает параметры вызова в стек и осуществляет вызов соответствующей функции через указатель интерфейса. Если вызов направлен в другое адресное пространство, то он перехватывается заглушкой, упаковывается (параметры вызова считываются из стека), помещается в специальный буфер обмена и через службы передается скелету сервера.
Скелет обращается к буферу, получает из него и распаковывает пакет, помещает параметры вызова в стек и передает вызов серверу. Тем самым скелет реконструирует вызов клиента в адресном пространстве сервера.
В результате, сервер и клиент как бы работают в одном адресном пространстве. Причем клиенту "кажется", что сервер работает в его процессе, а сервер "пребывает в уверенности", что клиент функционирует в его адресном пространстве.
Объекты CORBA в Delphi
Для управления распределенной средой архитектуры CORBA в Delphi реализован набор классов, инкапсулирующих функции взаимодействия приложений в рамках CORBA.
Для установления соединения приложения с сервером CORBA предназначен компонент TCorbaConnection. Сервер CORBA в Delphi разрабатывается на основе модуля данных TCorbaDataModule. Пример создания сервера и клиента CORBA с использованием модуля данных CORBA и компонента TCorbaConnection представлен ниже.
Для работы с брокером запросов и службой Basic Object Adaptor предназначены классы torb и твоа. Экземпляры этих классов создаются автоматически при выполнении приложения и доступны через глобальные переменные ORB и BOA.
Компонент TCorbaConnection
Компонент TCorbaConnection обеспечивает соединение клиентского приложения с сервером CORBA (табл. 31.1).
Сервер определяется свойством Repositoryid, в котором задается имя сервера, связанное с идентификатором сервера. При обращении к приложению, разработанном в Delphi, в свойстве через слэш задается имя сервера и имя модуля данных CORBA.
Если требуется подключиться к строго определенному серверу, в свойстве HostName задается имя или адрес компьютера сервера.
Соединение с сервером открывается и закрывается при помощи свойства Connection.
Таблица 31.1. Свойства и методы компонента TCorbaConnection
Объявление |
Тип |
Описание |
Свойства |
||
property AppServer: Variant; |
Ro |
Возвращает интерфейс для обращения к серверу |
property Cancelable: Boolean;
|
Pb |
Управляет отменой установки соединения, которое приостановлено по какой-либо причине |
property Connected: Boolean; |
Pb |
Открывает или закрывает соединение |
property HostName: string; |
Pb |
Описывает адрес сервера |
property ObjectName: string; |
Pb |
Содержит имя сервера |
type TRepositoryId = type string; property Repositoryld: TRepositoryId; |
Pb
|
Содержит внутренний идентификатор CORBA для интерфейса модуля данных сервера |
Методы |
||
function GetServer: lAppServer; override; |
Pu |
Возввращает интерфейс lAppServer |
Методы-обработчики событий |
||
type TCancelEvent = procedure (Sender: TObject; var Cancel: Boolean; var DialogMeasage: string) of object; property OnCancel: TCancelEvent; |
Pb |
Вызывается при разрыве устанавливаемого соединения. Параметр Cancel в значении True разрывает соединение немедленно. Параметр DialogMessage содержит текстовое сообщение, отображаемое при разрыве соединения |
Класс TORB
Функции брокера объектных запросов в Delphi реализованы в классе torb. При запуске приложения экземпляр класса создается автоматически и доступен через глобальную переменную ORB.
В Delphi разработчику нет необходимости самому заниматься запуском и выгрузкой объектов. Все эти процессы осуществляются автоматически. Но при надобности можно воспользоваться методами, представленными в табл. 31.2.
Таблица 31.2. Свойства и методы компонента torb
Объявление |
Описание |
Свойства |
||
function Bind (const RepositoryID: string; const ObjectName: string = ''; const HostName: string = ''): lObject; overloads; |
Делает объект доступным для клиентов. Для идентификации объекта можно использовать внутренний идентификатор CORBA RepositoryID или глобальный идентификатор InterfaceID. |
|
function Bind (const InterfaceID: TGUID; const ObjectName: string = ''; const HostName: string = ''): lObject; overload; |
Возвращает ссылку на интерфейс объекта
|
|
function FindTypeCode (const RepositoryID: string): ITypeCode; |
Возвращает ссылку на интерфейс ITypeCode для объекта RepositoryID
|
|
class procedure Initialize (const ConimandLine: TCommandLine); overload; |
Задает параметры инициализации серверного приложения
|
|
function MakeAlias (const RepositoryID, TypeName: string; Value, Test: TAny): TAny; |
Создает псевдоним объекта |
|
function MakeAliasTypeCode (const RepositoryID, Name: string; const TC: ITypeCode): ITypeCode; |
Возвращает ссылку на интерфейс ITypeCode для объекта с псевдонимом
|
|
function MakeArray (Kind: TCKind; const Elements: array of TAny): TAny; function MakeArray (TypeCode: ITypeCode; const Elements: array of TAny): TAny; |
Возвращает массив параметров TAny. Вид параметра определяется типом TCKind или ITypeCode
|
|
function MakeObjectRefTypeCode (const RepositoryID, Name: string): ITypeCode; |
Возвращает ссылку на интерфейс ITypeCode ДЛЯ объекта RepositoryID
|
|
function MakeSequence (Kind: TCKind; const Elements: array of TAny): TAny; function MakeSequence (TypeCode: ITypeCode; const Elements: array of TAny): TAny; |
Возвращает массив параметров TAny. Вид параметра определяется типом TCKind или ITypeCode Создает интерфейс ITypeCode для последовательности TAny |
|
function MakeStructure (TypeCode: ITypeCode; const Elements: array of TAny): TAny; |
Pu Возвращает структуру TAny, созданную из массива параметров Elements
|
|
type TStructMember = record Name: string; TC: ITypeCode; end; TStructMembers = array of TStructMember; |
Pu Создает интерфейс ITypeCode для структуры Members
|
|
function MakeStructureTypeCode(RepositoryID, Name: string; Members: TStructMembers): ITypeCode; |
|
|
function ObjectToString (const Obj: lObject): string; |
Возвращает строковое представление объекта |
|
procedure Shutdown; |
Разрывает соединение с сервером |
|
function StringToObject(const ObjectString: string): lObject; |
Преобразует текстовое представление объ екта в объект и возвращает его интерфейс |
Класс ТВОА
Функции службы Basic Object Adaptor в Delphi реализованы в классе твоа (табл. 31.3). При запуске приложения экземпляр класса создается автоматически и доступен через глобальную переменную BOA.
Таблица 31.3. Свойства и методы компонентаTBоа
Объявление |
Описание |
Свойства |
||
procedure Deactivate (const Obj: lObject); |
Делает объект Obj недоступным для клиентов |
|
type TCorbaPrincipal = array of Byte; |
Возвращает информацию о клиентских приложениях, обращавшихся к объекту Obj |
|
function GetPrincipal (const Obj: lObject): TCorbaPrincipals- |
|
|
procedure ImplIsReady; |
Подготавливает сервер к приему сообщений от клиентов |
|
type TArgv = array of string; TCoimnandLine = TArgv; class procedure Initialize (const CoinmandLine: TCommandLine) ; |
Задает параметры инициализации серверного приложения
|
|
procedure ObjIsReady (const Obj: lObject); |
Сообщает BOA о готовности объекта Obj к работе с клиентами |
Простой сервер
Так же, как и в других серверах приложении, основой сервера CORBA является модуль данных. Вы можете создать модуль данных CORBA (класс TCorbaDataModuie), выбрав соответствующий значок на странице Multitier Репозитория объектов. Перед созданием модуля необходимо задать его параметры в появившемся диалоговом окне (рис. 31.1).
Однострочный редактор Class Name позволяет задать имя модуля данных.
Список Instancing содержит возможные режимы создания экземпляров модуля данных:
Рис. 31.1. Диалоговое окно установки параметров модуля данных CORBA
Список Threading Model определяет способ использования интерфейса клиентом:
При использовании режима Shared instance сервер не может напрямую использовать интерфейс IProvider. Для успешной работы необходимо создать собственный интерфейс.
После создания модуля данных на нем можно размещать компоненты доступа к данным и компоненты-провайдеры. В качестве простейшего примера обратимся к таблице Country из демонстрационной базы данных DBDEMOS. Для этого используем стандартный компонент TTable и компонент TDataSetProvider, который обеспечивает передачу данных между сервером и клиентом.
Листинг 31.1 Исходный код модуля данных SimpleCORBA
unit RDM;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ComObj, VCLCom, StdVcl, DataBkr, CorbaRdm, CorbaObj, SimpleCORBAServ_TLB, Db, DBTables;
type
TSimpleCORBA = class(TCorbaDataModule, ISimpleCORBA)
Country: TTable;
CountryProvider: TDataSetProvider;
private
{ Private declarations } public
{ Public declarations } end;
var
SimpleCORBA: TSimpleCORBA;
implementation
{$R *.DFM}
uses Corblnit, CorbaVcl;
initialization
TCorbaVclComponentFactory.
Create('SimpleCORBAFactory', 'SimpleCORBA', 'IDL:SimpleCORBAServ/SimpleCORBAFactory:1.0', ISimpleCORBA,
TSimpleCORBA, iMultiInstance, tmSingleThread);
end.
Как видно из листинга 31.1, исходный код модуля данных CORBA не отли чается от обычного удаленного модуля данных. В секции инициализации модуля располагается конструктор фабрики класса, которая обеспечивае' создание экземпляра модуля данных при загрузке сервера.
А вот в библиотеке типов имеются существенные дополнения.
Листинг 31.2 Файл SimpleCORBAServ_TLB.PAS(без комментариев)
unit SimpleCORBAServ_TLB,
{$TYPEDADDRESS OFF) interface
uses Windows, ActiveX, Classes, Graphics, OleServer, OleCtrls, StdVCL, SysUtils, CORBAObj, OrbPas, CorbaStd, MIDAS;
const
// TypeLibrary Major and minor versions SimpleCORBAServMajorVersion = 1;
SimpleCORBAServMinorVersion = 0;
LIBID_SimpleCORBAServ: TGUID = '(4C023840-D2AF-11D3-9D68-40ДВ04С10000)';
IID_ISimpleCORBA: TGUID = '{4C023841-D2AF-11D3-9D68-40AB04C10000)' ;
CLASS_SimpleCORBA: TGUID = '{4C023843-D2AF-11D3-9D68-40AB04C10000}';
type
ISimpleCORBA = interface;
ISimpleCORBADisp = dispinterface;
SimpleCORBA = ISimpleCORBA;
//
// Interface: ISimpleCORBA
// Flags: (4416) Dual'OleAutomation Dispatchable
// GUID: {4C023841-D2AF-11D3-9D68-40AB04C10000} //**********************************//
ISimpleCORBA = interface(lAppServer)
['{4C023841-D2AF-11D3-9D68-40AB04C10000}'] end;
// Displntf: ISimpleCORBADisp
// Flags: (4416) Dual OleAutomation Dispatchable
// GUID: {4C023841-D2AF-11D3-9D68-40AB04C10000}
//
ISimpleCORBADisp = dispinterface
['(4C023841-D2AF-11D3-9D68-40AB04C10000)']
function AS_ApplyUpdates(const ProviderName: WideString; Delta:
OleVariant; MaxErrors: Integer; out ErrorCount: Integer; var OwnerData:
OleVariant): OleVariant; dispid 20000000;
function AS GetRecords(const ProviderName: WideString; Count:
Integer; out Recsbut: Integer; Options: Integer; const CommandText:
WideString; var Param5: OleVariant; var OwnerData: OleVariant):
OleVariant; dispid 20000001;
function AS DataRequest(con3t ProviderName: WideString; Data:
OleVariant): OleVariant; dispid 20000002;
function AS_GetProviderNames: OleVariant; dispid 20000003;
function AS_GetParams(const ProviderName: WideString; var OwnerData:
OleVariant): OleVariant; dispid 20000004;
function AS_RowRequest(const ProviderName: WideString; Row:
OleVariant; RequestType: Integer; var OwnerData: OleVariant): OleVariant;
dispid 20000005;
procedure AS_Execute(const ProviderName: WideString; const CommandText: WideString; var Pa rams: OleVariant; var OwnerData:
OleVariant); dispid 20000006;
end;
TSimpleCORBAStub = class(TAppServerStub, ISimpleCORBA)
public
end;
TfiimpleCORBASkeleton = class(TAppServerSkeleton) private
FIntf: ISimpleCORBA;
public
constructor Create(const InstanceName: string; const Impi: lUnknown);
override;
procedure Getlmplementation(out Impi: lUnknown); override; stdcall;
published end;
CoSimpleCORBA = class
class function Create: ISimpleCORBA;
class function CreateRemote(const MachineName: string): ISimpleCORBA;
end;
//
// OLE Server Proxy class declaration
// Server Object : TSimpleCORBA
// Help String : SimpleCORBA Object
// Default Interface: ISimpleCORBA
// Def. Intf. DISP? : No
// Event Interface:
// TypeFlags : (2) CanCreate
//
($IFDEF LIVE_SERVER_AT_DESIGN_TIME} TSimpleCORBAProperties= class;
($ENDIF)
TSimpleCORBA = class(TOleServer) private
FIntf: ISimpleCORBA;
{$IFDEF LIVE_SERVER_AT_DESIGN_TIME}
FProps: TSimpleCORBAProperties;
function GetServerProperties: TSimpleCORBAProperties;
{$ENDIF}
function GetDefaultInterface: ISimpleCORBA;
protected
procedure InitServerData; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Connect; override;
procedure ConnectTo(svrlntf: ISimpleCORBA);
procedure Disconnect; override;
function AS_ApplyUpdates(const ProviderName: WideString; Delta:
OleVariant; MaxErrors: Integer; out ErrorCount: Integer; var OwnerData:
OleVariant): OleVariant;
function AS GetRecords(const ProviderName: WideString; Count:
Integer; out RecsOut: Integer; Options: Integer; const CommandText:
WideString; var Params: OleVariant; var OwnerData: OleVariant):
OleVariant;
function AS_DataRequest(const ProviderName: WideString; Data:
OleVariant): OleVariant;
function AS_GetProviderNames: OleVariant;
function AS Get Pa rams(const ProviderName: WideString; var OwnerData:
OleVariant): OleVariant;
function AS_RowRequest(const ProviderName: WideString; Row:
OleVariant; RequestType: Integer; var OwnerData: OleVariant): OleVariant;
procedure AS Execute(const ProviderName: WideString; const CommandText: WideString; var Params: OleVariant; var OwnerData:
OleVariant);
property Defaultlnterface: ISimpleCORBA read GetDefaultInterface;
published ($IFDEF LIVE_SERVER_AT_DESIGN_TIME}
property Server: TSimpleCORBAProperties read GetServerProperties;
{$ENDIF} end;
($IFDEF LIVE_SERVER_AT_DESIGN_TIME)
//
// OLE Server Properties Proxy Class
// Server Object : TSimpleCORBA
// (This object is used by the IDE's Property Inspector to allow editing
// of the properties of this server)
//
TSimpleCORBAProperties = class(TPersistent) private
FServer: TSimpleCORBA;
function GetDefaultInterface: ISimpleCORBA;
constructor Create(AServer: TSimpleCORBA);
protected public
property Defaultlnterface: ISimpleCORBA read GetDefaultInterface;
published end;
{$ENDIF}
TSimpleCORBACorbaFactory = class
class function Createlnstance(const InstanceName: string):
ISimpleCORBA;
end;
procedure Register;
implementation uses ComObj;
{ TSimpleCORBAStub'}
{ TSimpleCORBASkeleton }
constructor TSimpleCORBASkeleton.Create(const InstanceName:string; const Impl: IDnknown);
begin
inherited;
inherited InitSkeleton('SimpleCORBA', InstanceName,
'IDL:SimpleCORBAServ/ISimpleCORBA:1.0', tmMultiThreaded,True);
FIntf := Irnpl as ISimpleCORBA;
end;
procedure TSimpleCORBASkeleton.GetImplementation(out Irnpl:IUnknown);
begin
Impl := FIntf;
end;
class function CoSimpleCORBA.Create: ISimpleCORBA;
begin
Result := CreateComObject(CLASS_SimpleCORBA) as ISimpleCORBA;
end;
class function CoSimpleCORBA.CreateRemote(const MachineName:string):
ISimpleCORBA;
begin
Result := CreateRemoteComObject(MachineName, CLASS_SimpleCORBA) as ISimpleCORBA;
end;
procedure TSimpleCORBA.InitServerData;
const
CServerData: TServerData = (
ClassID: '{4C023843-D2AF-11D3-9D68-40AB04C10QOO}' ;
IntfIID: '(4C023841-D2AF-11D3-9D68-40AB04C10000}' ;
Event I ID: ";
LicenseKey: nil;
Version: 500);
begin
ServerData := @CServerData;
end;
procedure TSimpleCORBA.Connect;
var
punk: lUnknown;
begin
if FIntf = nil then
begin
punk := GetServer;
Fintf:= punk as ISimpleCORBA;
end;
end;
procedure TSimpleCORBA.ConnectTo(svrlntf: ISimpleCORBA);
begin
Disconnect;
FIntf := svrlntf;
end;
procedure TSimpleCORBA.Disconnect;
begin
if Fintf <> nil then
begin
PIntf := nil;
end;
end;
function TSimpleCORBA.GetDefaultInterface: ISimpleCORBA;
begin
if FIntf = nil then Connect;
Assert(FIntf <> nil, 'Defaultlnterface is NULL. Component is not connected to Server. You must call ''Connect'' or ''ConnectTo'' before this operation') ;
Result := FIntf;
end;
constructor TSimpleCORBA.Create(AOwner: TComponent) ;
begin
inherited Create(AOwner) ;
{$IFDEF LIVE_SERVER_AT_DESIGN_TIME}
FProps := TSimpleCORBAProperties.Create;Self);
{$ENDIF} end;
destructor TSimpleCORBA.Destroy;
begin
{$IFDEF LIVE_SERVER_AT_DESIGN_TIME} FProps.Free;
{$ENDIF}
inherited Destroy;
end;
($IFDEF LIVE_SERVER_AT_DESIGN_TIME)
function TSimpleCORBA.GetServerProperties: TSimpleCORBAProperties;
begin
Result := FProps;
end;
{$ENDIF}
function TSimpleCORBA.AS_ApplyUpdates(const ProviderName: WideString;
Delta: OleVariant;
MaxErrors: Integer; out ErrorCount: Integer;
var OwnerData: OleVariant):
OleVariant;
begin
Result := DefaultInterface.AS ApplyUpdates(ProviderName, Delta, MaxErrors, ErrorCount, OwnerData);
end;
function TSimpleCORBA.AS_GetRecords(const ProviderName: WideString;
Count: Integer; out RecsOut: Integer; Options: Integer; const CommandText: WideString; var Params: OleVariant; var OwnerData:
OleVariant): OleVariant;
begin
Result := Defaultlnterface.AS_GetRecords(ProviderName, Count, RecsOut, Options, ConroandText, Params, OwnerData) ;
end;
function TSimpleCORBA.AS_DataRequest(const ProviderName: WideString;
Data: OleVariant): OleVariant;
begin
Result := Defaultlnterface.AS_DataRequest(ProviderName, Data);
end;
function TSimpleCORBA.AS_GetProviderNames: OleVariant;
begin
Result := DefaultInterface.AS_GetProviderNames;
end;
function TSimpleCOKBA.AS GetParams(const ProviderName: WideString; var OwnerData: OleVariant): OleVariant;
begin
Result := DefaultInterface.AS GetParams(ProviderName, OwnerData);
end;
function TSimpleCORBA.AS RowRequest(const ProviderName: WideString; Row:
OleVariant; RequestType: Integer; var OwnerData: OleVariant): OleVariant;
begin
Result := DefaultInterface.AS RowRequest(ProviderName, Row, RequestType, OwnerData);
end;
procedure TSimpleCORBA.AS_Execute(const ProviderName: WideString; const CommandText: WideString; var Params: OleVariant; var OwnerData:
OleVariant) ;
begin
DefaultInterface.AS Execute(ProviderName, CommandText, Params, OwnerData) ;
end;
{$IFDEF LIVE_SERVER_AT_DESIGN_TIME} constructor TSimpleCORBAProperties.Create(AServer: TSimpleCORBA) ;
begin
inherited Create;
FServer := AServer;
end;
function TSimpleCORBAProperties.GetDefaultInterface: ISimpleCORBA;
begin
Result := FServer.Defaultlnterface;
end;
($ENDIF)
class function TSimpleCORBACorbaFactory.CreateInstance(const InstanceName: string): ISimpleCORBA;
begin
Result :=
CorbaFactoryCreateStub('IDL:
SimpleCORBAServ/SimpleCORBAFactory:1.0',
'SimpleCORBA', InstanceName, '', ISimpleCORBA) as ISiitipleCORBA;
end;
procedure Register;
begin
RegisterComponents('Servers',[TSimpleCORBA]) ;
end;
initialization
CorbaStubManager.RegisterStub(ISimpleCORBA, TSimpleCORBAStub) ;
CorbaInterfaceIDManager.Registerlnterface
(ISimpleCORBA, 'IDL:SimpleCORBAServ/ISimpleCORBA:1.0');
CorbaSkeletonManager.
RegisterSkeletontISimpleCORBA, TSimpleCORBASkeleton) ;
end.
Помимо уже известных объявлений модуля данных и необходимых интерфейсов и диспинтерфейсов, здесь присутствуют объявления заглушки и скелета для соединения CORBA. Также здесь имеется объявление и описание фабрики классов.
Заглушка и скелет связаны через интерфейс ISimpleCORBA и обеспечивают использование единственного набора данных сервера.
В методах Get_parts заглушки и скелета для работы со специальным буфером обмена используются интерфейсы iMarshalinBuffer (чтение аргументов из буфера) и IMarshalOutBuffer (запись аргументов в буфер).
В секции initialization регистрируются объявленные выше объекты заглушки TSimpleCORBAStub, скелета TSimpleCORBASkeleton И Интерфейс ISimpleCORBA.
Представленный в листинге 31.2 исходный код создан полностью автоматически и обеспечивает полноценное представление модуля данных сервера в соответствии со спецификацией CORBA. Полученный исходный код на языке Object Pascal можно экпортировать для создания бинарного кода этого сервера в других средах разработки или в других операционных системах. Это может понадобиться для создания гетерогенной распределенной среды на основе CORBA.
Экспорт возможен на языке CORBA IDL (стандарт OMG) или на языке Microsoft IDL и осуществляется из формы библитеки типа для модуля данных CORBA.
Формат CORBA IDL (Interface Definition Language — Язык Описания Интерфейсов) предоставляет разработчикам единый способ описания объектов CORBA. Тем самым обеспечивается совместимость различных частей многоуровневых приложений, работающих под управлением различных операционных систем на различных аппаратных платформах.
Представление сервера на любом из этих языков чрезвычайно просто (листинг 31.3) и это имеет свое объяснение. Для обеспечения совместимости с любыми платформами язык IDL описывает только самые общие параметры объекта, которые обеспечивают инициализацию процесса создания кода объекта в той или иной программной среде.
Листинг 31.3 Файл Simple CORBAServ.IDL (язык CORBA IDL )
#include "Midas.idl"
module SimpleCORBAServ {
interface ISimpleCORBA;
interface ISimpleCORBA: Midas::lAppServer. {
};
interface SimpleCORBAFactory {
ISimpleCORBA Createlnstance(in string InstanceName);
};
На этом создание простейшего сервера CORBA завершено. Для его успешного функционирования необходимо выполнить еще ряд действий, которые будут рассмотрены сразу после создания клиента CORBA.
Простой клиент
При создании клиентского приложения на основе архитектуры CORBA будем руководствоваться теми же соображениями, что и в предыдущей главе — нам важно показать методику установления связи между клиентом и сервером. Поэтому пример клиента обладает минимальным набором функций.
Расширенный набор функций для реального клиента рассмотрен в главе 27. Он основан на использовании свойств и методов компонента TclientDataSet. Поэтому рассмотренный там вариант построения клиентского приложения может быть применен и в данном случае.
Основу соединения с сервером составляет компонент TCorbaConnection, который расположен на странице MIDAS Палитры компонентов. Параметры соединения задаются всего одним свойством — RepositoryID.
Так как в архитектуре CORBA регистрация серверов в том виде, как это делается в DCOM, отсутствует, то списка доступных для подключения серверов для этого свойства в Инспекторе объектов не существует.
В свойстве RepositoryiD необходимо через слэш задать имя сервера и имя модуля данных. В нашем случае это будет выглядеть так:
SimpleCORBAServ/SimpleCORBA
Существует еще одно представление имени сервера в нотации IDL, которое также можно использовать:
IDL:SimpleCORBAServ/ISimpleCeRBA:1.0
После этого необходимо перенести на форму компонент TclientDataSet и
Через свойство RemoteServer связать его С Компонентом TCorbaConnection.
Затем из списка доступных интерфейсов в свойстве providerName выберем CountryProvider.
Выбор значений описываемых свойств из выпадающих списков в Инспекторе объектов возможен только при запущенной службе Smart Agent и работающем сервере CORBA.
Затем добавим в форму компоненты TDataSource и TDBGrid и свяжем их с набором данных клиента обычным образом.
При обращении к серверу, работающему в сети, в свойстве HostName необходимо указать имя сетевого компьютера. Это можно сделать в Инспекторе объектов или в процессе выполнения клиентского приложения, введя имя компьютера в поле Host Name.
Листинг 31.4 Секция implementation модуля клиента CORBA
implementation {$R *.DFM}
procedure TClientFom. FomShow( Sender: TObject);
begin
CORBAConnection.Connected := True;
CountryClient.Open;
end;
procedure TClientForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin CountryClient.Close;
CORBAConnection.Connected := False;
end;
end.
Открытие и закрытие соединения и набора данных осуществляется при открытии и закрытии формы.
После запуска сервера, этот процесс рассматривается ниже, достаточно открыть набор данных клиента, чтобы соединение заработало. При этом компонент TCorbaConnection функционирует так же, как и другие компоненты соединения — при открытии набора данных клиента соединение автоматически устанавливается, при закрытии связь прерывается.
Запуск сервера
Взаимодействие частей многоуровневого приложения на основе CORBA осуществляется Брокером объектных запросов — ORB.
Все утилиты, обеспечивающие функционирование сервера и клиента приложения CORBA доступны в меню Borland Delphi 5\VisiBroker кнопки Start операционной системы.
Для того чтобы сервер и клиент смогли установить соединение, на любом из компьютеров (желательно на сервере) должна быть запущена программа VisiBroker Smart Agent. Она осуществляет маршрутизацию запросов в сети.
Простейший и самый надежный способ запуска сервера приложений CORBA — запустить его вручную. Тогда вы можете быть уверены, что клиентская часть приложения включена и функционирует.
При ручном запуске сервер должен быть зарегистрирован в распределенной среде CORBA при помощи утилиты irep. Командная строка должна содержать имя регистрируемого сервера и имя файла описания сервера в формате IDL. В нашем случае параметры утилиты irep выглядят следующим образом:
irep SimpleCORBAServ SimpleCORBAServ.idI
В случае успешной регистрации имя сервера отображается в консоли утилиты VisiBroker Smart Finder (см. ниже).
Для автоматического запуска сервера по первому вызову клиента на компьютере сервера постоянно должна работать утилита Object Activation Daemon (OAD). Однако для этого интерфейсы сервера должны быть зарегистрированы для совместной работы с OAD. Сначала при помощи библиотеки типов требуется экспортировать интерфейсы в формате IDL. Затем при помощи утилиты OADUTIL.EXE. Она хранится в каталоге Borland\Vbroker\Bin.
Для регистрации интерфейса в командной строке утилиты используется ключ reg и обязательные параметры, назначение которых представлено в табл. 31.4.
Таблица 31.4. Параметры утилиты oadutil.exe
Параметр |
Описание |
-i <имя интерфейса> |
Задает имя интерфейса в формате IDL |
-г <идентификатор> |
Задает интерфейс по его идентификатору |
-о <имя объекта> |
Определяет имя объекта, связанного с интерфейсом |
-срр <имя файла> |
Задает имя исполняемого файла сервера |
Для уничтожения регистрации используется та же утилита, но с ключом unreg.
Если соединение между сервером и клиентом не устанавливается, то для проверки правильности настройки среды CORBA можно использовать утилиту VisiBroker Smart Finder. После запуска она проводит проверку доступных в сети объектов CORBA и выдает собранную информацию.
Например, эту утилиту можно использовать для уточнения имен функционирующих в сети серверов приложений CORBA и наличии регистрации серверов в OAD. Если утилита находит в сети работающий сервер, то правильно настроенное клиентское приложение (см. выше) должно соединяться с сервером и успешно работать.
При задании имени сервера и модуля данных в свойстве RepositoryID компонента TCorbaConnection клиента несовпадение регистра символов считается ошибкой.
При наличии в сети работающего сервера клиентское приложение может посылать запросы и получать результат и во время разработки в среде Delphi.
Резюме
Технология CORBA открывает перед программистами, использующими Delphi, мир других операционных систем. Она позволяет создавать распределенные приложения, части которых разработаны в различных средах на различных платформах.
Взаимодействие клиентских приложений и приложений-серверов в Delphi в архитектуре CORBA обеспечивает брокер объектных запросов Visibroker.