Идентификация и расширение языков
Встраивание расширяемости в языки обычно приводит к системам, которые являются более свободно связанными. Расширяемость позволяет отправителям изменять экземпляры без необходимости следовать централизованным нормативам. Таким образом, первое правило, касающееся расширяемости - это:
1. Правило "Расширяемость допускается": языки ДОЛЖНЫ быть предназначены для расширяемости |
Основное требование, предъявляемое к расширяемости, это возможность определять язык, состоящий из элементов и атрибутов. Пространства имен XML обеспечивают технологию связывания URI (Uniform Resource Identifier, универсальный идентификатор ресурса) c именем XML-элемента или атрибута, то есть задают язык этого имени. В результате, также удается избежать конфликта имен.
Спецификация "W3C XML Schema" предоставляет конструкцию, называемую групповой символ (wildcard), <xs:any>, позволяющую проверять, где допускаются элементы из определенного пространства имен. Групповой символ означает, что элементы в указанном пространстве имен допустимы в реальных документах там, где находится групповой символ. Благодаря этому можно четко определять расширения схем. Получатели расширенных документов могут устанавливать и, в зависимости от модели обработки расширений, без риска игнорировать расширения, которые они не понимают.
<xs:any> использует атрибут namespace, чтобы проверять, из каких пространств имен поступают элементы расширения. Основные значения этого атрибута - ##any, которое означает, что схему можно расширить, используя элемент из любого возможного пространства имен; ##other, который допускает только элементы расширения из пространств имен, отличных от текущего; ##targetnamespace, допускает только элементы расширения из текущего пространства имен.
<xs:any> использует атрибут processContents, чтобы контролировать, как XML-парсер проверяет на допустимость расширенные элементы. Возможные методы: "lax" ("нестрогий") - допускающий проверку, "strict" ("строгий") - требующий проверки, "skip" ("пропустить") - позволяющий ее пропустить. В этой статье используется нестрогая проверка, поскольку это наиболее гибкий и типичный выбор для спецификаций Web-сервисов.
Основная цель модели "Необходимо пропускать" - разрешить внесение в документы изменений, обеспечивающих обратную и прямую совместимость. Как минимум, это не подразумевает ни добавления имен пространств имен, ни изменения имен элементов. Добавление имен элементов в целевое пространство имен (target namespace) может быть выполнено с пространством имен ##any или комбинацией пространства имен ##other и целевого пространства имен словаря.
Приведем ряд примеров, демонстрирующих это правило. Предположим, что заказ на поставку (purchase order) пересылается с одной машины на другую. В результате обработки этого заказа появляется сообщение "отгружено" ("shipped"). Однако, это сообщение могло быть отправлено спустя какое-то время после получения заказ на поставку. Было бы желательно, чтобы программное обеспечение, осуществляющее отправку, могло бы подождать ответ произвольное время (синхронный обмен сообщениями). Предпочтительная модель для получателя - иметь возможность самостоятельно отправить сообщение "отгружено", не заставляя отправителя ждать. Получатель "перезванивает" первоначальному отправителю - отсюда происходит термин "обратный вызов" ("callback"). Отправитель предоставляет получателю адрес в виде адреса обратного вызова. Он указывает адрес, который получатель должен использовать для отправки отправителю любых последующих сообщений. В случае Web-сервисов этот обратный вызов обычно отравляется в виде блока SOAP Header.
Предпочтительным вариантом было бы применение расширяемого стиля ##any. Ниже приведен тип CallbackType (тип обратного вызова), который использует эту модель:
Пример 1. Схема, в которой для расширяемости используется ##any
<s:complexType name="CallbackType"> <s:sequence> <s:element name="callbackLocation" type="s:anyURI" minOccurs="1" maxOccurs="1"/> <s:any processContents="lax" minOccurs="0" maxOccurs="unbounded" namespace="##any"/> </s:sequence> <s:anyAttribute/> </s:complexType>
Однако, функционированию этой модели препятствуют описанные ниже детерминистических ограничения XML-схемы W3C. Проблема возникает при добавлении необязательного элемента в последующую версию обратного вызова. В качестве примера можно привести время простоя. Время простоя обратного вызова - это ценная информация для получателя. Либо получатели могут продолжить обработку, если они не понимают время простоя. Приведенная ниже схема - это приблизительно то, что желательно создать для использования групповых символов, но она неправомерна из-за детерминистических ограничений:
Пример 2. Неправомерная схема
<s:complexType name="CallbackType"> <s:sequence> <s:element name="callbackLocation" type="s:anyURI" minOccurs="1" maxOccurs="1"/> <s:element name="expires" type="s:dateTime" minOccurs="0" maxOccurs="1"/> <s:any processContents="lax" minOccurs="0" maxOccurs="unbounded" namespace="##any"/> </s:sequence> <s:anyAttribute/> </s:complexType>
Поскольку эта модель не работает, требуется создание модели схемы, которая является грубым эквивалентом, необходимым для достижения первоначальной цели. Чтобы разрешить новые расширения в том же самом пространстве имен, разработчик схемы должен создать тип расширения, который разрешает расширения в том же пространстве имен. Чтобы разрешить надлежащее управление версиями определений языка XML, необходимы еще два правила: первое - для пространства имен:
2. Правило "Любое пространство имен": уровень расширяемости ДОЛЖЕН предусматривать расширения в любом пространстве имен. Для приложений XML-схемы точка расширяемости ДОЛЖНА быть элементом, который разрешает расширение в целевом пространстве имен, и групповым символом, который разрешает расширения в любом другом пространстве имен. |
3. Правило "Полная расширяемость": все XML-элементы ДОЛЖНЫ разрешать расширяемость элемента после определения элемента и допускать любые атрибуты. |
Ниже приведен пример типа Callbacktype, который выполняет эти требования:
Пример 3. Тип Callback с расширяемостью
<s:complexType name="CallbackType"> <s:sequence> <s:element name="callbackLocation" type="s:anyURI" minOccurs="1" maxOccurs="1"/> <s:element name="Extension" type="wscb:ExtensionType" minOccurs="0" maxOccurs="1"/> <s:any processContents="lax" minOccurs="0" maxOccurs="unbounded" namespace="##other"/> </s:sequence> <s:anyAttribute/> </s:complexType> <s:complexType name="ExtensionType"> <s:sequence> <s:any processContents="lax" minOccurs="1" maxOccurs="unbounded" namespace="##targetnamespace"/> </s:sequence> <s:anyAttribute/> </s:complexType>
Поскольку каждое расширение в целевом пространстве имен находится внутри элемента Extension, каждое последующее пространство имен будет увеличивать вложенность на один уровень. Хотя такой уровень вложенности на расширение нежелателен, это то, что сегодня можно сделать при задании строгой проверки на допустимость по XML-схеме W3C. Кажется, что наличие многочисленных вложенных элементов оправдано, если в язык могут быть внесены многократные совместимые исправления. Этот прием позволяет выполнить проверку допустимости расширений в целевом пространстве имен при сохранении проверки допустимости самого целевого пространства имен.
В общем расширение может быть определено с помощью новой спецификации, в которой указывается нормативная ссылка на более раннюю спецификацию, а затем определяется новый элемент. На самом деле основной смысл в проектировании пространств имен XML - разрешить децентрализованные расширения. Неизбежным следствием является необходимость получения разрешения для расширений в том же пространстве имен. У пространства имен есть владелец, поэтому изменение смысла чего-либо не владельцами пространства имен может привести к нежелательным результатам.