Создание многозвенных приложений в Delphi 4.0

Александр Цимбал

В настоящее время, пожалуй, уже нет необходимости убеждать разработчиков, работающих с системами управления базами данных, в наличии серьезных преимуществ многозвенных систем по сравнению с традиционным подходом построения клиент-серверных приложений. Основной проблемой на пути широкого применения этой технологии является ее относительная недоступность (дороговизна; отсутствие или, по крайней мере, недостаток необходимой информации; недостаточная интегрированность средств разработки). Важным обстоятельством является и недостаточная "стандартность" предлагаемых средств, что приводит к повышенному риску при разработке серьезных коммерческих проектов. Например, наиболее широко известная на сегодняшний день технология создания распределенных приложений - 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;



Interface Ltd.
Подготовили: Александр Цимбал E-mail:tzimbal@interface.ru
29.07.98