пятница, 27 августа 2010 г.

Часть 5: Реализация механизмов поддержки и порождения ссылок уровня хранения на базе системы хранения XML данных XSS

Архитектура подсистемы взаимодействия с сервером XSS

Взаимодействие клиентских приложений с хранилищем XML данных происходит через адаптер. На модуль адаптера возложена функция перевода XML данных в объекты и сохранение объектов в XML. Для решения озвученных выше проблем в адаптере необходимо реализовать поддержку ссылок (отложенного чтения), а также создать модуль, отвечающий за автоматическую расстановку ссылок.
clip_image001
Рис. 26. Схема взаимодействия клиентских приложений с XSS.
Ниже будут рассмотрены основные модули, принимающие участие в работе системы «XSS – клиент», в т.ч. будет рассмотрена схема работы адаптера.

Ядро

Ядро является основным компонентом системы. Его инициализация происходит при запуске приложения. Одна из функций ядра – поддержания списка типов, с которыми ведется работа, в актуальном состоянии. При запуске ядра производится анализ зарегистрированных и загруженных в домен приложения сборок, составляется и сохраняется список типов (кэш) с учетом наследования. Также в процессе работы приложения производится контролируемая ядром подгрузка и выгрузка дополнительных сборок. Обновления кэша типов при этом происходит автоматически.
clip_image003
Рис. 1. Диаграмма класса ядра в части взаимодействия с типами.
Ключевые методы для работы с типами:
· RegisterAssembly. Загружает сборку, регистрирует содержащиеся в ней типы.
· UnRegisterAssembly. Выгружает сборку из домена приложения, удаляет принадлежащие ей типы из кэша.
· FindType. Возвращает типа из кэша по имени.
· GetInheritTypes. Возвращает список наследников данного типа.
· GetPK. Получение информации о поле/свойстве типа, содержащего идентификатор (id).

Драйвер работы с XSS.

Драйвер работы с XSS (XSS драйвер) – это модуль, обеспечивающий работу с хранилищем: сохранение и загрузку данных. То есть на XSS драйвер возложены обязанности сериализации и десериализации объектов. В были введены правила отображения объектов в XML представлении:
· Текущий объект становится корневым элементом (тэгом)
· Простое свойство объекта становится атрибутом тэга. При этом атрибуты могут отсутствовать, что означает, что свойство будет проинициализировано значением по умолчанию
· Ссылочное свойство становится вложенным элементом относительно корневого
При этом все элементы имеют обязательный атрибут id, идентифицирующий экземпляр объекта.
В рамках был реализован компонент драйвера XSS:
clip_image005
Рис. 2. Диаграммы класса XSS драйвера.
Ключевой особенностью драйвера является возможность работы в двух режимах: онлайн и оффлайн. В первом режиме обмен данными с хранилищем идет в режиме реального времени, то есть сразу при необходимости загрузки, сохранения или удаления объекта. В режиме оффлайн работа с хранилищем ведется с помощью кэша драйвера. Данные, которые необходимо сохранить или удалить, сперва помещаются в кэш, а с хранилищем изменения синхронизируются единовременно при вызове соответствующей команды. Этот режим работы драйвера может пригодиться в случае отсутствия постоянного доступа к хранилищу.
Кэш объектов представляет собой список записей. Каждая запись списка – обертка над объектом, который непосредственно необходимо кэшировать. «Оберточная» запись предоставляет некоторую дополнительную информацию, для того чтобы программисту не пришлось вычислять ее самому.
clip_image007
Рис. 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 – очищает кэш
· WriteMode = rmLazyCommit (оффлайн режим)
    • GetObject – получает объект из XSS, помещает его в кэш
    • SaveObject – добавляет объект в кэш, если его там не было
    • DeleteObject – если объект был в кэше, помечает его как IsDeleted, иначе добавляет в кэш, а потом помечает IsDeleted
    • Commit – сохраняет в XSS объекты, помеченные IsModified, удаляет объекты, помеченные IsDeleted
    • Reset – очищает кэш
Методы работы с данными, реализованные в XSS драйвере:
· 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;
}
}
Листинг 1. Пример реализации инициализации по требованию на языке C#.
Ключевые особенности инициализации по требованию:
  • работает только для полей, обращение к которым происходит через свойство даже в пределах самого класса (самоинкапсулированные поля);
  • требует связи бизнес-объектов с источником данных.
2) Виртуальный прокси-объект
Виртуальный прокси-объект имитирует объект, являющийся значением поля, либо сам бизнес-объект, однако в действительности ничего в себе не содержит. В этом случае загрузка реального объекта будет выполнена только тогда, когда будет вызван один из методов виртуального прокси-объекта. Преимуществом виртуального прокси-объекта является то, что он "выглядит" точно так же, как реальный объект, который должен находиться в данном поле. Во втором случае оба объекта (бизнес и виртуальный) должны имплементировать один и тот же интерфейс для инкапсуляции типа. Соответственно прокси-объект создает «обертку» для полей бизнес-объекта: содержит в себе логику создания и загрузки объектов полей (она может быть реализовано в виде инициализации по требованию).
clip_image008
Рис. 4 Схема взаимодействия типов при реализации виртуального прокси-объекта.
Положительная сторона данного подхода заключается в том, что не нужно вмешивать в бизнес-объекты. Отрицательный момент состоит в увеличении числа классов и интерфейсов, а также в увеличении количества экземпляров объектов в памяти во время работы программы.
3) Фиктивный объект
Фиктивный объект – это реальный объект с неполным состоянием. Когда он загружается из источника данных, то содержит только свой идентификатор. При первой же попытке доступа к одному из его полей объект загружает значения всех остальных полей. Для большей наглядности фиктивный объект можно представить себе в виде объекта, все поля которого одновременно инициализируются по требованию, или объекта, который является собственным виртуальным прокси-объектом. Возможно, все данные не обязательно загружать за один раз; для большего удобства их можно разбить на группы полей, которые часто используются вместе.
4) Диспетчер значений
Диспетчер значения – это объект, который играет роль оболочки для какого-нибудь другого объекта. Чтобы получить значение базового объекта, необходимо обратиться за ним к диспетчеру значения. При первом обращении диспетчер значения извлекает необходимую информацию из источника данных.
private ValueHolder holder;
public Department Dept
{
get
{
return (Department)holder.GetValue();
}
}
Листинг 2. Пример реализации диспетчера значений на языке C#.
Этот способ имеет ряд недостатков:
· класс должен знать о наличии диспетчера значения (необходимо вносить изменения в бизнес-объект);
· теряются преимущества строгой типизации;
Чтобы избежать проблем идентификации, необходимо гарантировать, что диспетчер значения никогда не будет передаваться методам за пределами его класса-владельца.

Контейнеризация типов

В рамках данной работы для реализации отложенной загрузки предлагается использовать комбинированный способ: контейнеризацию типов. Суть этого подхода заключается в том, что для всех бизнес-типов создаются типы-контейнеры – «обертки». Они являются промежуточным слоем между обращением к свойствам объекта и получением их значений.
Контейнеризация реализована следующим образом: для каждого бизнес-типа создает тип-контейнер, который включает в себя поле упаковываемого типа, а также содержит все свойства и методы, которые были определены в исходном типе. Преобразование осуществляется по следующим правилам:
· бизнес-тип clip_image010 тип-контейнер
· базовый тип clip_image010[1] базовый тип-контейнер (или супертип)
· поле исходного типа clip_image010[2] свойство
· свойство исходного типа clip_image010[3] свойство
· метод исходного типа clip_image010[4] метод
Операция контейнеризации реализована как одна из операций в наборе OperationMachine. Таким образом, в приложении будет две иерархии типов – исходных и контейнеров, а также супертип. Также на уровне ядра (Kernel) реализована таблица соответствий «тип – тип-контейнер». Она понадобится при загрузке данных для подмены исходного типа на тип-контейнер. Это необходимо для работы XSS драйвера – при загрузке данных он должен создавать объекты соответствующих типов-контейнеров. Фактически, клиентские приложения будут работать только с экземплярами объектов-контейнеров.
При генерации типов-контейнеров на основе исходного типа «копированию» подвергаются все публичные поля, свойства и методы (в том числе статические, виртуальные и переопределенные с сохранением атрибутов). Таким образом, соблюдаются принципы инкапсуляции и наследования в иерархии типов-контейнеров. Также переносятся все атрибуты типа, полей, свойств и методов. Поля и свойства, помеченные атрибутом LK, упаковываются особым образом – с реализацией инициализации значения по требованию. В них проверяется, загружено ли соответствующее свойство объекта исходного типа (см. инициализацию по требованию); если нет, то происходит его загрузка. Перечисленные правила генерации гарантируют, что поведение объекта-контейнера не отличается от поведения объекта исходного типа.
Еще одна функция, реализуемая с помощью типов-контейнеров – это сбор статистики использования свойств.

Комментариев нет:

Отправить комментарий