Архитектура подсистемы взаимодействия с сервером XSS
Взаимодействие клиентских приложений с хранилищем XML данных происходит через адаптер. На модуль адаптера возложена функция перевода XML данных в объекты и сохранение объектов в XML. Для решения озвученных выше проблем в адаптере необходимо реализовать поддержку ссылок (отложенного чтения), а также создать модуль, отвечающий за автоматическую расстановку ссылок.Рис. 26. Схема взаимодействия клиентских приложений с XSS.
Ниже будут рассмотрены основные модули, принимающие участие в работе системы «XSS – клиент», в т.ч. будет рассмотрена схема работы адаптера.
Ядро
Ядро является основным компонентом системы. Его инициализация происходит при запуске приложения. Одна из функций ядра – поддержания списка типов, с которыми ведется работа, в актуальном состоянии. При запуске ядра производится анализ зарегистрированных и загруженных в домен приложения сборок, составляется и сохраняется список типов (кэш) с учетом наследования. Также в процессе работы приложения производится контролируемая ядром подгрузка и выгрузка дополнительных сборок. Обновления кэша типов при этом происходит автоматически.Рис. 1. Диаграмма класса ядра в части взаимодействия с типами.
Ключевые методы для работы с типами:
· RegisterAssembly. Загружает сборку, регистрирует содержащиеся в ней типы.
· UnRegisterAssembly. Выгружает сборку из домена приложения, удаляет принадлежащие ей типы из кэша.
· FindType. Возвращает типа из кэша по имени.
· GetInheritTypes. Возвращает список наследников данного типа.
· GetPK. Получение информации о поле/свойстве типа, содержащего идентификатор (id).
Драйвер работы с XSS.
Драйвер работы с XSS (XSS драйвер) – это модуль, обеспечивающий работу с хранилищем: сохранение и загрузку данных. То есть на XSS драйвер возложены обязанности сериализации и десериализации объектов. В были введены правила отображения объектов в XML представлении:· Текущий объект становится корневым элементом (тэгом)
· Простое свойство объекта становится атрибутом тэга. При этом атрибуты могут отсутствовать, что означает, что свойство будет проинициализировано значением по умолчанию
· Ссылочное свойство становится вложенным элементом относительно корневого
При этом все элементы имеют обязательный атрибут id, идентифицирующий экземпляр объекта.
В рамках был реализован компонент драйвера XSS:
Рис. 2. Диаграммы класса XSS драйвера.
Ключевой особенностью драйвера является возможность работы в двух режимах: онлайн и оффлайн. В первом режиме обмен данными с хранилищем идет в режиме реального времени, то есть сразу при необходимости загрузки, сохранения или удаления объекта. В режиме оффлайн работа с хранилищем ведется с помощью кэша драйвера. Данные, которые необходимо сохранить или удалить, сперва помещаются в кэш, а с хранилищем изменения синхронизируются единовременно при вызове соответствующей команды. Этот режим работы драйвера может пригодиться в случае отсутствия постоянного доступа к хранилищу.
Кэш объектов представляет собой список записей. Каждая запись списка – обертка над объектом, который непосредственно необходимо кэшировать. «Оберточная» запись предоставляет некоторую дополнительную информацию, для того чтобы программисту не пришлось вычислять ее самому.
Рис. 3. Запись кэша драйвера.
Свойства записи в кэше:
· Object – ссылка на сам объект
· Iid – содержит идентификатор объекта
· IidParent – идентификатор родительского объекта
· HashCode – первоначальный хэшкод объекта, добавленного в кэш; также на нем основана проверка, не допускающая повторного добавления объекта в кэш
· CurrentHashCode – текущий хэшкод объекта в кэше
· IsModified – флаг, указывающий на то, что объект был изменен (в случае есть Hashcode отличается от CurrentHashcode)
· IsDeleted – флаг, которым помечаются объекты для удаления (при вызове метода Delete() драйвера в режиме работы rmLazyCommit)
Поведение XSS драйвера при загрузке/сохранении объектов в зависимости от значения свойства WriteMode определяется следующим образом:
· WriteMode = rmDirectCommit (онлайн режим)
- GetObject – получает объект из XSS, помещает его в кэш
- SaveObject – сохраняет объект в кэш, помечает запись IsModified = false
- DeleteObject – удаляет объект из XSS и из кэша
- Commit – сохраняет в XSS объекты, помеченные IsModified, удаляет объекты, помеченные IsDeleted
- Reset – очищает кэш
- GetObject – получает объект из XSS, помещает его в кэш
- SaveObject – добавляет объект в кэш, если его там не было
- DeleteObject – если объект был в кэше, помечает его как IsDeleted, иначе добавляет в кэш, а потом помечает IsDeleted
- Commit – сохраняет в XSS объекты, помеченные IsModified, удаляет объекты, помеченные IsDeleted
- Reset – очищает кэш
· GetObject. Загрузка объекта с заданным типом и идентификатором.
· GetObjects. Загрузка всех объектов заданного типа.
· SaveObject. Сохранение объекта.
· SaveObjects. Сохранение массива объектов.
· DeleteObject. Удаление заданного экземпляра объекта из хранилища.
· DeleteObjects. Удаление списка объектов из хранилища.
· Commit. Синхронизация содержимое кэша с хранилищем: объекты для сохранения сохраняются, объекты, предназначенные для удаления, удаляются.
· Reset. Очистка кэша драйвера.
Операционная машина
Операционная машина – компонент, предоставляющий генерацию и выполнение дополнительных операций над типам, определенными в приложении. Примером операций над типом могут являться сложение, получение строкового представления и т.д.Операционную машину можно разделить на несколько частей:
· Генератор операционной машины
· Кэш сборок операций
· Модуль, отвечающий за доступ к операциям
Генератор операционной машины – часть операционной машины, отвечающая за генерацию новых операций на основе данных о типах, содержащихся в загружаемых ядром сборках. Генератор используется при построении кэша сборок операций. Генерация операций ведется при регистрации сборки в ядре.
Кэш сборок операций – это хранилище операций, физически содержащее скомпилированные библиотеки, содержащие код операций над типами. Кэш поддерживается в актуальном состоянии, то есть при регистрации сборки операции над ее типами генерируются, только если сборка с операциями отсутствует, либо если она устарела относительно регистрируемой сборки (например, в нее были добавлены новые типы). Основные функции кэша:
· Контроль соответствия регистрируемой сборки
· Определение набора операций над типами сборки
· Возможность добавления новых операций
Также в рамках операционной машины определено множество операций над кэшем сборок операций.
Разрешение ссылок на уровне представления
Типовое решение отложенной загрузки заключается в прерывании процесса загрузки, при этом необходимо оставлять соответствующую метку в структуре объектов. Это позволяет загрузить необходимые данные только тогда, когда они действительно понадобятся.Для разрешения ссылок на уровне представления необходима реализация нескольких функций:
· внесение изменений в XSS драйвер
o при разборе XML-тэга, в котором присутствует атрибут link, драйвер не должен загружать и создавать соответствующий объект;
o драйвер должен сохранять информацию о том, какие объекты не были загружены;
· внесение изменений в структуру объектов с тем, чтобы они поддерживали отложенную загрузку
Варианты реализации отложенной загрузки
Существует четыре основных шаблона при реализации отложенной загрузки [10]:1) Инициализация по требованию
Признаком того, что ссылочное поле незагружено, является ссылка на null. Основная идея данного подхода заключается в том, что при каждой попытке доступа к полю выполняется проверка, не содержит ли оно значение null.
public Department Dept { get { if (_dept == null) { _dept = LoadData(); } return _dept; } } |
Ключевые особенности инициализации по требованию:
- работает только для полей, обращение к которым происходит через свойство даже в пределах самого класса (самоинкапсулированные поля);
- требует связи бизнес-объектов с источником данных.
Виртуальный прокси-объект имитирует объект, являющийся значением поля, либо сам бизнес-объект, однако в действительности ничего в себе не содержит. В этом случае загрузка реального объекта будет выполнена только тогда, когда будет вызван один из методов виртуального прокси-объекта. Преимуществом виртуального прокси-объекта является то, что он "выглядит" точно так же, как реальный объект, который должен находиться в данном поле. Во втором случае оба объекта (бизнес и виртуальный) должны имплементировать один и тот же интерфейс для инкапсуляции типа. Соответственно прокси-объект создает «обертку» для полей бизнес-объекта: содержит в себе логику создания и загрузки объектов полей (она может быть реализовано в виде инициализации по требованию).
Рис. 4 Схема взаимодействия типов при реализации виртуального прокси-объекта.
Положительная сторона данного подхода заключается в том, что не нужно вмешивать в бизнес-объекты. Отрицательный момент состоит в увеличении числа классов и интерфейсов, а также в увеличении количества экземпляров объектов в памяти во время работы программы.
3) Фиктивный объект
Фиктивный объект – это реальный объект с неполным состоянием. Когда он загружается из источника данных, то содержит только свой идентификатор. При первой же попытке доступа к одному из его полей объект загружает значения всех остальных полей. Для большей наглядности фиктивный объект можно представить себе в виде объекта, все поля которого одновременно инициализируются по требованию, или объекта, который является собственным виртуальным прокси-объектом. Возможно, все данные не обязательно загружать за один раз; для большего удобства их можно разбить на группы полей, которые часто используются вместе.
4) Диспетчер значений
Диспетчер значения – это объект, который играет роль оболочки для какого-нибудь другого объекта. Чтобы получить значение базового объекта, необходимо обратиться за ним к диспетчеру значения. При первом обращении диспетчер значения извлекает необходимую информацию из источника данных.
private ValueHolder holder; public Department Dept { get { return (Department)holder.GetValue(); } } |
Этот способ имеет ряд недостатков:
· класс должен знать о наличии диспетчера значения (необходимо вносить изменения в бизнес-объект);
· теряются преимущества строгой типизации;
Чтобы избежать проблем идентификации, необходимо гарантировать, что диспетчер значения никогда не будет передаваться методам за пределами его класса-владельца.
Контейнеризация типов
В рамках данной работы для реализации отложенной загрузки предлагается использовать комбинированный способ: контейнеризацию типов. Суть этого подхода заключается в том, что для всех бизнес-типов создаются типы-контейнеры – «обертки». Они являются промежуточным слоем между обращением к свойствам объекта и получением их значений.Контейнеризация реализована следующим образом: для каждого бизнес-типа создает тип-контейнер, который включает в себя поле упаковываемого типа, а также содержит все свойства и методы, которые были определены в исходном типе. Преобразование осуществляется по следующим правилам:
· бизнес-тип тип-контейнер
· базовый тип базовый тип-контейнер (или супертип)
· поле исходного типа свойство
· свойство исходного типа свойство
· метод исходного типа метод
Операция контейнеризации реализована как одна из операций в наборе OperationMachine. Таким образом, в приложении будет две иерархии типов – исходных и контейнеров, а также супертип. Также на уровне ядра (Kernel) реализована таблица соответствий «тип – тип-контейнер». Она понадобится при загрузке данных для подмены исходного типа на тип-контейнер. Это необходимо для работы XSS драйвера – при загрузке данных он должен создавать объекты соответствующих типов-контейнеров. Фактически, клиентские приложения будут работать только с экземплярами объектов-контейнеров.
При генерации типов-контейнеров на основе исходного типа «копированию» подвергаются все публичные поля, свойства и методы (в том числе статические, виртуальные и переопределенные с сохранением атрибутов). Таким образом, соблюдаются принципы инкапсуляции и наследования в иерархии типов-контейнеров. Также переносятся все атрибуты типа, полей, свойств и методов. Поля и свойства, помеченные атрибутом LK, упаковываются особым образом – с реализацией инициализации значения по требованию. В них проверяется, загружено ли соответствующее свойство объекта исходного типа (см. инициализацию по требованию); если нет, то происходит его загрузка. Перечисленные правила генерации гарантируют, что поведение объекта-контейнера не отличается от поведения объекта исходного типа.
Еще одна функция, реализуемая с помощью типов-контейнеров – это сбор статистики использования свойств.
Комментариев нет:
Отправить комментарий