Сервер приложений и web-сервисы |
В процессе функционирования клиент оперирует с бизнес-объектами. Клиент может запросить экземпляр бизнес-объекта для просмотра и обработки, передать его серверу приложений для сохранения в БД отредактированного экземпляра или запросить у сервера приложений другую функциональность, предусмотренную разработчиком бизнес-объекта.
Для доступа к функциональности приложения через web-сервисы используются объектные интерфейсы. Объектный интерфейс определяет тип бизнес-объекта: состав данных (свойства интерфейса) и функциональность (методы интерфейса). Чтобы объектный интерфейс стал доступен клиентам, он должен быть опубликован. Т.е. на интернет-сервере необходимо подключить код соответствующих web-сервисов.
Публикуемые объектные интерфейсы и vip-интерфейсы должны иметь квалификатор web. В этом случае необходимый код будет сгенерирован утилитой atlIdl.
Для каждого компонента приложения, содержащего интерфейсы с квалификатором web, будет сгенерирован web-сервис.
Поскольку приложения-клиенты работают автономно от сервера приложений, последний не знает от какого клиента с каким бизнес-объектом придет следующий запрос. Поэтому клиенты вместе с запросом передает ссылку на обрабатываемый экземпляр бизнес-объекта. Такая ссылка хранится в структуре, называемой хендл на vip-интерфейс (в текущем разделе - просто хендл). Хендл создается сервером приложений в момент запроса клиентом экземпляра бизнес-объекта. Для этого публикуемый интерфейс должен реализовать интерфейс IGetHandle.
Экземпляр бизнес-объекта, передаваемый клиенту, создается конструктором. Имя конструктора и список параметров определяется при создании хендла.
В момент запроса клиентом экземпляра бизнес-объекта ядро приложения выполняет следующие действия:
вызывает метод CreateHandle интерфейса IGetHandle для создания хендла;
вызывает конструктор, указанный в хендле, с параметрами, полученными от клиента;
сохраняет ссылку на созданный экземпляр бизнес-объекта в хендле.
Для модификации данных бизнес-объекта публикуемый vip-интерфейс должен реализовать интерфейс ISetData.
Для корректной работы алгоритма модификации данных для свойств введены понятия левого и правого соединения (ссылок): leftJoin, rightJoin и noJoin. Указание типа соединения влияет на порядок вызовов метода DoSetData для свойств текущего объекта.
Если объект ссылается на подобъект, то это считается правой ссылкой. Пример - ссылки на каталоги.
Если подобъект ссылается на объект, то это считается левой ссылкой. Пример - спецификация ссылается на счет.
По умолчанию ссылка считается правой.
При попытке модификации данных со стороны клиента на сервере будут выполнены следующие действия:
при наличии атрибута primary у одного из индексов будет произведено позиционирование по этому индексу;
для объекта будут установлены все необходимые свойства, являющиеся простыми типами данных;
для правых ссылок будут вызваны методы DoSetData, в которые будет передаваться ссылка на конкретного владельца текущего подобъекта (т.е. ссылка на уровень выше);
будет вызвана функция DoSetData для корневого объекта с NullRef в качестве параметра;
для левых ссылок будут вызваны методы DoSetData, в которые будет передаваться ссылка на конкретного владельца текущего подобъекта (т.е. ссылка на уровень выше).
Еще одним важным нюансом является отслеживание пассивных блокировок. Т.е. необходимо убедиться, что с момента получения данных клиентом до возврата их серверу приложений для модификации их другой пользователь не изменил указанные данные. В стандартном режиме работы эта функциональность обеспечивается платформо-зависимым образом, который в данном случае не может быть использован.
В связи с этим прикладному разработчику нужно добавить в выборку данных поля ATL_LASTDATE, ATL_LASTTIME и ATL_LASTUSER, через которые и будут отслеживаться пассивные блокировки. Далее, прикладному разработчику необходимо написать соответствующий прикладной код для отслеживания данной ситуации. Для этого в метод ISetData.DoSetData (или иной, модифицирующий данные) нужно будет добавить код, подобный нижеприведенному:
if ( <таблица>.ATL_LASTDATE <> <старый>ATL_LASTDATE or <таблица>.ATL_LASTTIME <> <старый>ATL_LASTTIME or <таблица>.ATL_LASTUSER <> <старый>ATL_LASTUSER ) { result := tsConflict; exit; }