В настоящее время, пожалуй, уже нет необходимости убеждать разработчиков,
работающих с системами управления базами данных, в наличии серьезных преимуществ
многозвенных систем по сравнению с традиционным подходом построения клиент-серверных
приложений. Основной проблемой на пути широкого применения этой технологии
является ее относительная недоступность (дороговизна; отсутствие или, по
крайней мере, недостаток необходимой информации; недостаточная интегрированность
средств разработки). Важным обстоятельством является и недостаточная "стандартность"
предлагаемых средств, что приводит к повышенному риску при разработке серьезных
коммерческих проектов. Например, наиболее широко известная на сегодняшний
день технология создания распределенных приложений - DCOM - нормально функционирует
только в среде Windows NT.
Пожалуй, наиболее интересным на сегодняшний день предложением в области
средств создания многозвенных систем является решение фирмы Inprise. Ее
позиции на данном сегменте рынка являются просто уникальными.
Судите сами: Inprise - это результат слияния Borland и Visigenic. Borland
имеет колоссальный опыт создания средств разработки, ориентированных на
создание клиентских приложений. Такие шедевры программирования, как Delphi,
C++ Builder и Java Builder, говорят сами за себя. Кроме того, Borland -
автор самого совершенного в мире Windows универсального стандарта работы
с реляционными базами данных - BDE. Важно и то, что Borland имеет хорошие
партнерские отношения с ORACLE (которая лицензировала Borland'овскую Java-технологию)
и вполне приемлемые (в настоящее время) - с Microsoft. Microsoft объективно
заинтересована в той вольной или невольной поддержке, которую оказывает
ее Borland, предлагая COM-совместимые средства разработки (Delphi, MIDAS
и др.)
С другой стороны, Inprise - это еще и Visigenic, т.е. ведущий производитель
программных средств в области реализации CORBA - конкурента DCOM как стандарта
создания распределенных приложений. Объективно в настоящее возможности
CORBA превосходят возможности DCOM. Достаточно сказать, что CORBA работает
в различных операционных средах, в том числе в различных видах Unix.
Наличие этих чрезвычайно благоприятных условий обусловило появление
Delphi 4 и тех дополнительных средств, которые обеспечивают как создание
элементов многозвенных систем - клиентов и серверов приложений, так и их
взаимодействие. Inprise называет эту технологию MIDAS.
В настоящей статье мы, не вдаваясь в излишние технические подробности,
рассмотрим основные возможности, предоставляемые Delphi и MIDAS.
Что же входит в MIDAS (как технологию, а не отдельный программный продукт)?
Во-первых, это компонентная модель Borland; во-вторых - совместимость
объектов Delphi со спецификацией COM; в-третьих - конкретные компоненты
(TDCOMConnection, TSocketConnection, TOLEnterprise, TCORBAConnection, TProvider
и др.), а также сервера автомации OLE (TRemoteDataModule и TMTSDataModule),
которые создаются в Delphi с помощью экспертов. Кроме того, это сервер
CORBA TCORBADataModule, брокер Business Object Broker (как часть OLEnterprise)
для DCOM, ORB VisiBroker в случае использования CORBA, сервисы взаимодействия
объектов просто по протоколу TCP/IP - ScktSrvr и ScktSrvc (для NT), утилита
SQL Explorer (необходимая для поддержки и распространения существующих
на сервере БД ограничений в случае использования BDE) и многое другое.
Сначала - об общей идее построения трехзвенной системы с помощью MIDAS.
Распределенное приложение включает в себя три части - функции, реализованные
на сервере БД, сервер приложения как реализация основной части бизнес-логики
и так называемые "тонкие" клиенты. Задача тонкого клиента - реализация
интерфейса конечного пользователя. В частности, такой клиент, создаваемые
в рамках базовой Borland'овской технологии работы с данными, не подозревает
о существовании BDE. Взаимодействие с BDE - задача сервера приложений.
Классификация способов взаимодействие клиентов и серверов приложений
может быть построена по различным критериям. Наиболее важными для нас являются
два: это, во-первых, выбор стандарта взаимодействия "объектов-клиентов"
и "объектов-серверов", и, во-вторых, способ поиска конкретным клиентским
приложением доступного сервера приложения.
По первому критерию многозвенные системы можно разделить на две группы:
первая из них использует модель DCOM и OLE-автомацию (это реализация взаимодействия
либо с помощью самого DCOM, либо с помощью дополнительных средств, разработанных
Inprise - ScktSrvr/c, OLEnterpise). Такого рода приложения могут использовать
и MTS (Microsoft Transaction Server), о чем будем говорить несколько позднее.
Вторая группа приложений - это системы, в которых взаимодействуют не OLE,
а CORBA-объекты.
По второму критерию приложения делятся на те, которые обращаются к
конкретному серверу на конкретном компьютере, и те, которые доверяют поиск
нужного сервера специальному программному обеспечению. В случае использования
DCOM это или Object Broker, или (для OLEnterprise) Business Object Broker;
при работе с CORBA это брокер (ORB - Object Request Broker) VisiBroker.
1. Сервера приложений.
Общей характеристикой всех видов серверов приложений, создаваемых с помощью MIDAS, является то, что они содержат "удаленный модуль данных" (remote data module). Все виды модулей данных (их 3) реализуют интерфейс IDataBroker. Этот интерфейс реализует только один метод - GetProviderNames. С помощью обращения к этому методу удаленные клиенты получают список "провайдеров" (provider component). Провайдеры обеспечивают получение данных от сервера БД, "упаковывают" их передачи удаленному клиенту и осуществляют передачу. Кроме того, они ответственны за получение измененных данных от приложения-клиента и отправку их серверу БД с возможностью обработки возникающих ошибок. Знать об интерфейсе IDataBroker полезно еще и потому, что добавление к нему методов и свойств - самый естественный способ реализации дополнительной функциональности сервера приложений.
Рассмотрим кратко каждый из модулей данных.
1.1 TRemoteDataModule.
Этот способ, в общем, хорошо знаком тем, кто создавал трехзвенные системы
с помощью Delphi 3. Тем не менее, в Delphi 4 произошли некоторые изменения.
TRemoteDataModule Вы можете построить как в виде DLL, так и в виде
EXE-модуля. В любом случае модуль данных реализует OLE-сервер автомации
с так называемым двойственным (dual) интерфейсом; в любом случае необходима
регистрация сервера в системном реестре.
Тем не менее, между DLL- и EXE-реализацией существует большая разница.
Для EXE-модуля нужно правильно выбрать способ, определяющий количество
запускаемых серверов и, соответственно, количество создаваемых OLE-объектов
(т.е. самих удаленных модулей данных). Для DLL стоит проблема определения
правил взаимодействия объектов и потоков (обсуждение вариантов выбора выходит
за рамки статьи).
1.2 TMTSDataModule.
Подобно TRemoteDataModule, TMTSDataModule реализует сервер автомации
c двойственным интерфейсом. Взаимодействие с этим сервером, как и в предыдущем
случае, можно осуществить через DCOM, OLEnterprise и Socket Service (т.е.
в том или ином виде через DCOM). Но на этом сходство и кончается.
В отличие от TRemoteDataModule, этот сервер должен быть реализован
как ActiveX-сервер, т.е. в виде DLL. Самое же важное - это то, что он не
должен быть зарегистрирован в системном реестре. Вместо этого DLL должна
быть установлена вместе с MTS - для того, чтобы все COM-вызовы шли только
через Transaction Server.
Настройки модуля TMTSDataModule включают в себя опции взаимодействия
объекта с потоками и опции управления транзакциями.
Какие преимущества имеет использование MTS?
- MTS позволяет использовать освободившееся соединение с сервером БД
для обслуживания другого клиента, экономя тем самым и время, и ресурсы);
- MTS, естественно, существенным образом расширяет возможности управления
транзакциями (транзакции для разных баз данных(двухфазные транзакции) -
правда, полностью реализовано только для Oracle 7 и MS SQL; функциональность,
вообще не связанная с БД);
- расширенное управление созданием и уничтожением сервера автомации.
Как всегда, нет роз без шипов. Использование всех этих свойств приводит к невозможности полноценной работы со стандартным интерфейсом IProvider. Этот интерфейс ответственен за взаимодействие конкретных наборов данных на сервере приложений (точнее, в удаленном модуле данных) - TTable, Tquery, TStoredProc - с компонентом TClientDataSet приложения-клиента. Одним из требований его функционирования является сохранение (в некоторых случаях) состояния модуля данных (точнее, компонента TProvider). Это нужно, например, в случае передачи данных клиенту не целиком, а пакетами требуемого размера. Впрочем, существуют способы обойти это ограничение - от сохранения состояния TProvider до создания собственного интерфейса.
1.3 TCORBADataModule.
Работа с CORBA-серверами, естественно, существенно отличается от работы
с объектами COM. В частности, основным способом взаимодействия OLE-объектов
является динамический (т.е. во время работы программы) вызова метода Invoke
интерфейса IDispatch (разумеется, это не значит, что не существует других
способов), в то время как для CORBA более характерно использования так
называемой ранней привязки (binding) - на стадии компиляции программы (опять
же, динамический вызов методов интерфейсов вполне возможен). Регистрация
объектов, поиск нужного сервера и т. д - все выполняется по-другому. Впрочем,
описание функционирования CORBA-объектов выходит за рамки статьи.
Самое поразительное, что можно сказать про использование CORBA - это
даже не то, что Delphi позволяет работать с этой технологией простым и
естественным образом - в конце концов, программисты Borland'а к этому нас
уже приучили - а то, что Вы не увидите принципиальных различий между построениями
CORBA- и DCOM-приложений! В частности, в одной из моделей создания CORBA-сервера
- для каждого клиента свой экземпляр объекта удаленного сервера данных
- использование интерфейса IProvider не имеет никаких особенностей по сравнению
с тем, как это было в Delphi 3.
2. Формирование пакетов данных.
Рассмотрение возможностей, предоставляемых провайдерами (компонентами
TPtovider) потребовало бы написание отдельной статьи. Здесь же мы остановимся
только на том, что нового появилось в Delphi 4 в связи с поддержкой встроенных
таблиц и соотношения master-detail.
Как известно, интерфейс IProvider обеспечивает различные режимы передачи
данных от сервера приложений к клиенту. В частности, программа-клиент могла
определить размер пакета (т.е. количество записей) и то, когда нужно получать
эти данные.
В Delphi 4 введена расширенная встроенная поддержка отношения master-detail.
В связи с этим возникает вопрос: пусть компонент TProvider на сервере приложений
связан, например, с компонентом TTable, у которого есть detail-компонент
- что произойдет с дочерними записями при передаче набора записей из master-компонента?
Теперь TProvider имеет набор опций. Подробно описывать их для ознакомительной
статьи нет нужды - названия говорят сами за себя: poFetchBlobsOnDemand,
poFetchDetailsOnDemand, poCascadeDeletes, poCascadeUpdates!
Чрезвычайно интересным является также новое событие компонента TProvider
- OnGetDataSetProperties. Событие вызывается перед отправкой пакета клиентскому
приложению. Обработчик события может поместить в пакет произвольную информацию,
а клиент - извлечь ее с помощью вызова метода getOptionalParam. Небольшой
пример:
procedure TMyDataModule.Provider1GetDataSetProperties
(Sender : TObject;
DataSet : TDataSet; out
Properties : OleVariant);
begin
Properties := VarArrayCreate
([0,1], varVariant);
Properties[0] := VarArrayOf
(['CurrentTime', Now, True]);
Properties[1] := VarArrayOf
(['RecNum', DataSet.RecordCount, False]);
end;
Извлечь эту информацию можно так:
//…
dt : TdateTime;
//…
dt := NeededDataSet.GetOptionalParam('CurrentTime');
3. Управление сервером приложений.
В большинстве случаев взаимодействие сервера приложения
и клиента (через интерфейс IProvider) осуществляется автоматически или
с использованием стандартных методов - например, SetParams. Тем не менее,
часто возникает ситуация, когда приложение-клиент пытается заставить сервер
выполнить какие-то нестандартные действия, возможно, даже не связанные
с манипулированием данными.
Как и в Delphi 3, возможны два подхода: управление удаленным модулем
данных (т.е. работа с интерфейсом IDataBroker) и вызов методов интерфейса
IProvider для конкретного компонента TProvider.
Работа с IProvider очень проста: приложение-клиент вызывает метод DataRequest,
что приводит в вызову обработчика события onDataRequest для нужного компонента
TProvider на сервере приложений:
V : OleVariant;
//…
V := ClientDataSet1.Provider.DataRequest(Edit1.Text);
Обработчик события на сервере может выглядеть, например, так:
TForm1.Provider1DataRequest (Sender : TObject; Input : OleVariant)
: OleVariant;
Var
s : String;
begin
s := Input;
//…
end;
Для управления удаленным модулем данных в целом нужно добавить новые методы к интерфейсу IDataBroker, после чего к ним можно обратиться, используя компонент соединения с сервером приложений (например, TCORBAConnection):
MyCorbaConnection.AppServer.MyMethod (1);
Можно так же использовать раннее связывание (на уровне компиляции программы), т.е. непосредственное обращение к таблице виртуальных функций соответствующего интерфейса (только при соединении через DCOM и CORBA):
with MyDCOMConnection.AppServer as IMyAppServer do
MyMethod(1);
или
with Iunknown(MyCORBAConnection.AppServer) as IMyAppServer do
MyMethod(1);
При соединении через TCP/IP или OLEnterprise обращение непосредственно к таьлице виртуальных методов невозможно, но можно добиться некоторого выигрыша по времени (по сравнению с динамическим вызовом метода) с помощью диспинтерфейса:
var
di : IMyAppServerDisp;
begin
di := MyTCPConnection.AppServer;
di.MyMethod (1);
end;