oess_1.2.0. Неизвестные расширения

Введение

Механизмы простых расширяемых типов (см. oess_1.2.0. Расширяемые типы) и наследования расширением (см. oess_1.2.0. Наследование расширением (subclassing_by_extension)) решают проблему "второй версии протокола" за счет организации в сериализованном представлении специальных областей-расширений. Эти области являются непрозрачными для "первой версии протокола" и игнорируются вспомогательным кодом, который был сгенерирован для первой версии.

В чистом виде механизмы расширяемых типов и наследования расширением решают проблему корректного разбора старым вспомогательным кодом нового сериализованного представления. Например, пусть в первой версии протокола описан тип заказа покупки:

{type	amount_data_t {extensible} ... }
{type	merchant_data_t {extensible} ... }
{type	goods_t
	{extensible}
	{subclassing_by_extension}
	...
}

{type	order_t
	{extensible}

	|| Описание суммы, валюты, типа платежной системы и т.д.
	{attr	m_amount_data {of amount_data_t}}

	|| Описание этого заказа в магазине.
	{attr	m_merchant_data {of merchant_data_t}}

	|| Список входящих в заказ товаров.
	{attr	m_goods {stl-list} {of {extension_of} goods_t}}
}

Объекты типа order_t передаются между магазином и платежной системой обслуживающего этот магазин банка.

Данный протокол может расширяться обеими сторонами. Так, магазин может вводит новых потомков для типа goods_t, т.к. описание покупаемого автомобиля может существенно отличаться от описания холодильника. Банк может расширять типы amount_data_t и merchant_data_t для новых типов магазинов и платежных систем, поддерживая при этом совместимость с магазанами, работающими со старыми спецификациями типов amount_data_t и merchant_data_t. Поскольку в таких расширениях задействованы всего две взаимодействующие стороны (магазин и его банк), то механизмы раширяемых типов и расширяющего наследования прекрасно справятся с подобными задачами.

Но что, если между магазином и банком появляются еще несколько слоев? Например, банк, который выпустил (эммитировал) платежную карту, с помощью которой осуществляется покупка. Этот банк проводит аутентификацию плательщика и подтверждает возможность проведения платежа. Или автоматическая система, которая пытается обнаружить случаи мошеничества на основании статистических методов и списков номеров дискредитированых платежных карт. Или автоматическая система анализа заказов магазина, которая на основании order_t::m_goods пытается спрогнозировать изменение покупательского спроса.

Во всех этих случаях появляется третья сторона, которая выполняет роль ретранслятора между магазином и его банком. При этом ретранслятору не обязательно знать последнюю версию спецификации протокола для выполнения своих прикладных действий. Но, если ретранслятор не знает про расширения типов и при десериализации извлекает только известные ему составляющие типов, то как ретранслятор передаст точные исходные образы объектов при повторной сериализации?

Примитивным решением являлось бы сохранение точной двоичной копии входящих данных, десериализация в известные ретранслятору типы и их обработка, после чего повторная сериализация не выполняется, а ретранслируется сохраненная ранее копия. Такое решение требует, чтобы прикладная логика ретранслятора имела возможность получить доступ к нераспакованному, двоичному представлению входных данных. Что может быть вообще не возможно, если прикладная часть ретранслятора получает уже десериализованные объекты от транспортной части, а транспортная часть занимается десериализацией/сериализацией даже не имея представления о прикладном назначении обрабатываемого трафика. Такая ситуация, например, происходит при работе через такие высокоуровневые протоколы, как SOAP или CORBA.

Второе решение заключается в том, чтобы при десериализации не "выбрасывать" неизвестные расширения объектов, а специальным образом сохранять их в десериализованном объекте. А при повторной сериализации помещать их в выходное двоичное представление. Именно такое решение реализовано в ObjESSty версии 1.2.0.

Поддержка неизвестных расширений в ObjESSty

Вспомогательный код ObjESSty версии 1.2.0 строится так, что при десериализации все неизвестные расширения (относящиеся как к простым расширяемым типам, так и к расширеным через наследование типам) сохраняются в специальных атрибутах базового класса oess_1::stdsn::serializable_t. При сериализации объекта, у которого в oess_1::stdsn::serializable_t оказались сохранены неизвестные расширения, все неизвестные расширения сериализуются. В результате полученное при повторной сериализации двоичное представление получается идентичным исходному двоичному представлению.

Например, пусть есть две версии спецификации прикладного протокола: v1 и v2. И две взаимодействующие стороны (A, B), между которыми оказался ретранслятор R. При этом A и B используют спецификацию v2, а R -- v1. Благодоря поддержке неизвестных расширений R получает от A запросы в версии v2, но работает только с частью запросов, которая описывается версией v1. Но после повторной десериализации B получает от R ретранслированные запросы в версии v2! Таким образом, A и B вообще не имеют представления о том, что их трафик кем-то ретранслируется.

Для простых расширяемых типов поддержка неизвестных расширений тривиальна. А для наследования расширением в базовом классе oess_1::stdsn::serializable_t сохраняется не только образ сериализованных неизвестных атрибутов и типов, но и информация о том, какой реально тип был сериализован (т.н. subclassing extension path). Например, если при сериализации на стороне A был сериализован тип passenger_car_t, производный от car_t, производный от goods_t (т.е. subclassing extension path == {passenger_car_t, car_t, goods_t}), а сторона R знает только про тип goods_t, то на стороне R будет десериализована только составляющая типа goods_t оригинального объекта. Но subclassing extension path оригинального объекта будет сохранен как часть неизвестного расширения. Поэтому, при повторной сериализации на стороне R subclassing extension path будет восстановлен в своем исходном значении (т.е. {passenger_car_t, car_t, goods_t}). И сторона B десериализует именно объект типа passenger_car_t.

Поскольку неизвестные расширения сохраняются в базовом типе oess_1::stdsn::serializable_t, то производным от него прикладным типам не нужно заботится о том, чтобы копировать неизвестные раширения в конструкторах и операторах копирования. Для этого достаточно задействовать оператор копирования из базового типа. Например:

class goods_t
  : public oess_1::stdsn::serializable_t
  {
    typedef oess_1::stdsn::serializable_t base_type_t;
    OESS_SERIALIZER( goods_t )
  public :
    goods_t()
      {}
    goods_t( const goods_t & o )
      : base_type_t( o )
      { ... }

    goods_t &
    operator=( const goods_t & o )
      {
        base_type_t::operator=( o );
        ...
        return *this;
      }
    ...
  };

Если же сохранение неизвестных расширений и их последующая сериализация не желательно, то можно воспользоваться методом oess_1::stdsn::serializable_t::oess_drop_unknown_extensions(), который "выбрасывае" все неизвестные расширения из объекта. При этом теряются как расширения простых расширяемых типов, так и расширения типов в механизме наследования расширением (включая и subclassing extension path).

Применение неизвестных расширений

Поддержка неизвестных расширений в ObjESSty стала результатом попытки упрощения реализации ретрансляторов, которые должны обрабатывать прикладной трафик, сериализованный средствами ObjESSty. Поэтому главным назначением механизма неизвестных расширений является именно ретрансляция.

Благодоря механизму неизвестных расширений можно реализовывать промежуточные прикладные системы, которые обрабатыват "то, не знаю что". Например, пусть есть система, состоящая из магазина, который продает услуги производителя; банка-эквайера, который обслуживает магазин; банка-эммитента, который выпускае платежную карту. В центре этой системы находится платежный шлюз, который:

При этом платежный шлюз для обращения к производителю оперирует объектом order_t::m_goods, для обращения к банку-эмитенту -- объектами order_t::m_amount_data и order_t::m_merchant_data, а для обращения к банку-эквайеру -- объектом order_t::m_amount_data.

Примечательно, что платежный шлюз может знать только об одном типе из семейства типов goods_t -- базовом типе goods_t. О конкретных типах товаров должны знать только магазин и производитель. Более того, разные магазины и производители могут оперировать разными ветвями иерархии goods_t. Например, торгующие автомобильями магазины могут использовать свои унаследованные от goods_t типы (car_t, passenger_car_t, truck_t, racing_car_t и т.д.). А фото-магазины свои: photo_cam_t, digital_photo_cam_t, film_photo_cam_t, film35mm_photo_cam_t. А магазины, торгующие иформационными услугами -- свои (magazine_t, video_on_demand_t, ...). Все эти типы могут создаваться намного позже того, как будет реализован и запущен в реальную эксплуатацию платежный шлюз. При этом конкретные типы, описывающие реальный товар, могут быть достаточно сложными. Например, тип passenger_car_t может включать в себя описание десятков параметров: начиная от цвета кузова, типа и цвета обивки салона, заканчивая подробным описанием каждого из колес (включая запасное), а так же пакет дополнительных услуг (страхование, установку противоугонной системы, время и место доставки автомобиля заказчику, ...).

Более того, после запуска в экплуатацию платежного шлюза могут быть расширены даже такие типы, как merchant_data_t и amount_data_t. Например, в какой-нибудь очень независимой стране может использоваться локализованная версия платежной системы Visa. Карты этой системы требуют такой же аутентификации, как и остальные карты системы Visa. Различия могут заключаться в том, что при платеже этой картой внутри данной независимой страны магазин должен сообщить эквайеру дополнительную информацию, которая передается в новой версии типа amount_data_t. Платежный шлюз оказывается работоспособным и в этом случае, т.к. он, как и банк-эммитент, работает только с необходимой им частью объекта amount_data_t. А магазин и банк-эквайер -- с полной версией amount_data_t.


Документация по ObjESSty. Последние изменения: Fri Oct 13 18:35:37 2006. Создано системой  doxygen 1.4.7
Hosted by uCoz