Web-сервисы Java: Освоение и моделирование WSDL 1.1

Как WSDL 1.1 определяет Web-сервисы, и как создать модели на языке Java для верификации и преобразования WSDL-документов

Спустя несколько лет после утверждения языка Web Services Description Language (WSDL) 2.0 в качестве стандарта World Wide Web Consortium (W3C) версия WSDL 1.1 все еще остается наиболее широко используемой формой описания Web-сервисов. Несмотря на свою популярность, WSDL 1.1 страдает рядом проблем, включая несколько используемых схем и разные способы обработки WSDL-документов стеками Web-сервисов. В этой статье мы покажем, как структурированы описания сервисов WSDL 1.1. Еще мы рассмотрим базовую структуру Java™-инструмента для верификации WSDL-документов и их преобразования в форму, соответствующую "практическим рекомендациям".

Денис Сосноски, консультант, Sosnoski Software Solutions, Inc.

Денис Сосноски (Dennis Sosnoski) - основатель и ведущий специалист консалтинговой компании по технологиям Java - Sosnoski Software Solutions, Inc., специализирующейся в обучении и консультировании по проблемам XML и Web-сервисов. Он имеет более чем 30-летний опыт работы в профессиональном проектировании ПО, специализируясь на серверных XML и Java-технологиях. Денис является основным разработчиком интегрированной системы с открытым программным кодом JiBX XML Data Binding, построенной на базе технологии классов Java и связанной системы Web-сервисов JibxSoap, также как и системы Web-сервисов Apache Axis2. Он также был одним их экспертов при разработке спецификаций JAX-WS 2.0.



08.02.2011

Об этом цикле статей

Web-сервисы ― важная функция технологии Java™ в корпоративных вычислениях. В этом цикле статей консультант по XML и Web-сервисам Денис Сосновский рассказывает об основных структурах и технологиях, ценных для Java-разработчиков, использующих Web-сервисы. Следите за статьями цикла, чтобы быть в курсе последних разработок в данной области и знать, как применить их в своих собственных проектах.

Web-сервисы для корпоративных приложений в значительной степени зависят от использования определений сервисов. Определения сервисов описывают основное соглашение между поставщиком услуг и любым потенциальным потребителем, детализируя типы функций, предоставляемых сервисом, и сообщения в рамках каждой функции. Поставщики и потребители свободны в выборе способа реализации своих объектов обмена в той мере, в какой фактические сообщения, которые они посылают, соответствуют определению сервиса. Использование определения сервиса с описанием способа обмена XML-сообщениями ― это то, что отличает Web-сервисы от более ранних технологий распределенного программирования.

Предлагались различные методы определения Web-сервисов, но наиболее широко используемым подходом остается WSDL 1.1. WSDL 1.1 имеет некоторые недостатки, в том числе чрезмерно сложную структуру, которая затрудняет его чтение для непосвященных. Он страдает также от отсутствия авторитетного формального определения, что привело к последовательным «уточнениям», которые заполняют некоторые пробелы в исходном документе спецификации. В результате стеки Web-сервисов стараются обрабатывать документы WSDL 1.1 как можно более гибко. Эта гибкость может усилить путаницу в понимании WSDL 1.1, так как разработчики видят широкий спектр WSDL-структур без каких-либо указаний на то, какой подход предпочтительнее.

В этой статье мы покажем, как разобрать документы WSDL 1.1, и рассмотрим первые части модели Java для проверки WSDL-документов и их преобразования в стандартную форму.

Разбор WSDL 1.1

Используемые пространства имен

В этой статье используются:

  • префикс wsdl для представления пространства имен WSDL 1.1 http://schemas.xmlsoap.org/wsdl/;
  • префикс soap для пространства имен http://schemas.xmlsoap.org/wsdl/soap/, используемого расширением SOAP 1.1 для WSDL 1.1;
  • префикс xs для пространства имен http://www.w3.org/2001/XMLSchema, используемого для определений XML-схемы.

Редакция 1.1 WSDL, опубликованная в начале 2001 года, технически заменена рекомендациями W3C WSDL 2.0, опубликованными в 2007 году. WSDL 2.0 предлагает более четкую структуру, чем WSDL 1.1, наряду с большей гибкостью. Но WSDL 2.0 страдает от проблемы курицы и яйца: WSDL 2.0 не используется широко, потому что не поддерживается широко, а так как он широко не используется, у разработчиков стеков Web-сервисов мало стимулов его поддерживать. Несмотря на все его недостатки, для большинства целей WSDL 1.1 достаточно хорош.

Оригинальная спецификация WSDL 1.1 была неточной в отношении количества используемых функций. Так как в центре внимания WSDL была работа с определениями служб SOAP, он включал также поддержку функций SOAP (таких как кодирование rpc), которые позднее оказались нежелательными. Организация Web Services Interoperability Organization (WS-I) решила эти проблемы в Базовом профиле (BP), который содержит практические рекомендации по Web-сервисам с использованием SOAP и WSDL. BP 1.0 был утвержден в 2004 году, а в 2006 году вышла редакция BP 1.1. В этой статье рассматривается WSDL 1.1 на базе рекомендаций BP WS-I и не затрагиваются фактически устаревшие функции, такие как кодирование rpc для SOAP.

Предполагается, что структура XML-документов задается определениями XML-схемы. В первоначальную спецификацию WSDL 1.1 входит описание схемы, но эта схема в нескольких отношениях не соответствует текстовым описаниям. Позже это было исправлено в модифицированной версии схемы, но документ WSDL 1.1 не был отредактирован с учетом этого изменения. Затем группа BP WS-I решила внести еще больше изменений в схему WSDL и создала то, что преподносится как практические рекомендации к этой скользкой схеме. Документы, написанные для одной версии схемы, как правило, не совместимы с другими версиями (несмотря на то, что используется одно и то же пространство имен), но к счастью, большинство инструментов Web-сервисов в основном игнорирует схему и принимает все, что выглядит разумным. (См. ссылки на многие схемы WSDL в разделе Ресурсы).

Даже версия BP WS-I схемы WSDL 1.1 не очень помогает гарантировать соответствие спецификации документов WSDL 1.1. Схема не отражает всех ограничений BP WS-I, особенно в отношении порядка следования компонентов. Кроме того, XML-схема не способна обработать многие типы легко устанавливаемых ограничений в документах (такие как альтернативные атрибуты или необходимые дополнительные элементы из отдельной схемы). Поэтому проверка соответствия документа WSDL 1.1 спецификации WSDL 1.1 (с поправками, внесенными BP WS-I) включает в себя гораздо больше, чем просто выполнение валидации XML-схемы. Мы еще вернемся к данной теме в этой статье. Но сначала рассмотрим структуру описаний сервиса WSDL 1.1.

Компоненты описания

В документах WSDL 1.1 используется фиксированный корневой элемент с удобным названием <wsdl:definitions>. В пределах этого корневого элемента в пространстве имен WSDL 1.1 определены один «пассивный» дочерний элемент (просто ссылка на отдельные документы WSDL 1.1) и пять «активных» дочерних элементов (которые собственно и составляют описание сервиса):

  • <wsdl:import> ссылается на отдельный документ WSDL 1.1 с описаниями, подлежащими включению в этот документ;
  • <wsdl:types> определяет типы XML или элементы, используемые для обмена сообщениями;
  • <wsdl:message> определяет фактическое сообщение с точки зрения типов или элементов XML;
  • <wsdl:portType> определяет абстрактный набор операций, осуществленных сервисом;
  • <wsdl:binding> определяет фактическую реализацию <wsdl:portType> с помощью конкретных протоколов и форматов;
  • <wsdl:service> определяет сервис в целом, как правило, включая один или несколько элементов <wsdl:port> с информацией доступа для элементов <wsdl:binding>.

Существует также элемент <wsdl:document>, который может использоваться для целей документирования, как первый дочерний элемент <wsdl:definitions>, а также первый дочерний элемент любого из вышеуказанных элементов.

Для полного описания сервиса, как правило, требуется, по крайней мере, один элемент каждого из этих типов, за исключением <wsdl:import>, но не обязательно, чтобы все они находились в одном и том же документе. Для сборки полного описания WSDL из нескольких документов можно использовать <wsdl:import>, что позволяет подразделять описания для нужд организации. Например, первые три элемента описания (<wsdl:types>, <wsdl:message> и <wsdl:portType>) вместе составляют полное описание интерфейса сервиса (возможно, определенного группой архитектуры), так что их имеет смысл держать отдельно от ориентированных на реализацию элементов <wsdl:binding> и <wsdl:service>. Все крупные стеки Web-сервисов поддерживают разделение описаний на несколько WSDL-документов.

В листингах 1 и 2 показан пример описания сервиса WSDL, разбитого на два WSDL-документа, так что компоненты описания интерфейса содержится в файле BookServerInterface.wsdl, а компоненты реализации ― в файле BookServerImpl.wsdl. В листинге 1 показан BookServerInterface.wsdl.

Листинг 1. BookServerInterface.wsdl
<wsdl:definitions ... xmlns:tns="http://sosnoski.com/ws/library/BookServerInterface"
    targetNamespace="http://sosnoski.com/ws/library/BookServerInterface">
  <wsdl:document>Book service interface definition.</wsdl:document>
  <wsdl:types>
    <xs:schema ...
        targetNamespace="http://sosnoski.com/ws/library/BookServerInterface">
      <xs:import namespace="http://sosnoski.com/ws/library/types"
          schemaLocation="book-types.xsd"/>
      ...
    </xs:schema>
  </wsdl:types>
  <wsdl:message name="getBookMessage">
    <wsdl:part name="part" element="tns:getBook"/>
  </wsdl:message>
  <wsdl:message name="getBookResponseMessage">
    <wsdl:part name="part" element="tns:getBookResponse"/>
  </wsdl:message>
  ...
  <wsdl:message name="addBookMessage">
    <wsdl:part name="part" element="tns:addBook"/>
  </wsdl:message>
  <wsdl:message name="addBookResponseMessage">
    <wsdl:part name="part" element="tns:addBookResponse"/>
  </wsdl:message>
  <wsdl:message name="addDuplicateFault">
    <wsdl:part name="fault" element="tns:addDuplicate"/>
  </wsdl:message>
  <wsdl:portType name="BookServerPortType">
    <wsdl:documentation>
      Book service implementation. This creates an initial library of books when the
      class is loaded, then supports method calls to access the library information
      (including adding new books).
    </wsdl:documentation>
    <wsdl:operation name="getBook">
      <wsdl:documentation>
        Get the book with a particular ISBN.
      </wsdl:documentation>
      <wsdl:input message="tns:getBookMessage"/>
      <wsdl:output message="tns:getBookResponseMessage"/>
    </wsdl:operation>
    ...
    <wsdl:operation name="addBook">
      <wsdl:documentation>Add a new book.</wsdl:documentation>
      <wsdl:input message="tns:addBookMessage"/>
      <wsdl:output message="tns:addBookResponseMessage"/>
      <wsdl:fault message="tns:addDuplicateFault" name="addDuplicateFault"/>
    </wsdl:operation>
  </wsdl:portType>
</wsdl:definitions>

В листинге 2 показан BookServerImpl.wsdl. Элемент <wsdl:import> в начале импортирует описание интерфейса BookServerInterface.wsdl.

Листинг 2. BookServerImpl.wsdl
<wsdl:definitions ... xmlns:ins="http://sosnoski.com/ws/library/BookServerInterface"
    xmlns:tns="http://sosnoski.com/ws/library/BookServer"
    targetNamespace="http://sosnoski.com/ws/library/BookServer">
  <wsdl:document>
    Definition of actual book service implementation.
  </wsdl:document>
  <wsdl:import namespace="http://sosnoski.com/ws/library/BookServerInterface"
      location="BookServerInterface.wsdl"/>
  <wsdl:binding name="BookServerBinding" type="ins:BookServerPortType">
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
    <wsdl:operation name="getBook">
      <soap:operation soapAction="urn:getBook"/>
      <wsdl:input>
        <soap:body/>
      </wsdl:input>
      <wsdl:output>
        <soap:body/>
      </wsdl:output>
    </wsdl:operation>
    ...
    <wsdl:operation name="addBook">
      <soap:operation soapAction="urn:addBook"/>
      <wsdl:input>
        <soap:body/>
      </wsdl:input>
      <wsdl:output>
        <soap:body/>
      </wsdl:output>
      <wsdl:fault name="addDuplicateFault">
        <soap:fault name="addDuplicateFault"/>
      </wsdl:fault>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="BookServer">
    <wsdl:port name="BookServerPort" binding="tns:BookServerBinding">
      <soap:address location="http://localhost:8080/cxf/BookServer"/>
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>

Помимо определений элементов (и атрибутов) в пространстве имен WSDL 1.1, WSDL 1.1 определяет также дополнительные элементы. Они предназначены для заполнения конкретных ячеек в описаниях сервисов WSDL 1.1 для передачи дополнительной информации, необходимой для конкретного типа сервисов. Единственные дополнительные элементы WSDL 1.1, которые все еще широко используются, это привязки для SOAP 1.1 (они представлены в в листинге 2, в элементах <wsdl:binding> и <wsdl:service>), которые были определены в первоначальной спецификации WSDL 1.1, и для SOAP 1.2, определенные отдельной спецификацией в 2006 году.

Детали компонентов

Элемент <wsdl:types> содержит все определения XML, используемые для сообщений, в виде одного или нескольких элементов <xs:schema>. (WSDL допускает альтернативы XML-схеме для этих определений, но большинство стеков поддерживает только XML-схемы). Если нужно, элементы <xs:schema> могут использовать <xs:import> или <xs:include> для включения других схем, внешних по отношению к WSDL (а также ссылаться на отдельные схемы внутри одного и того же WSDL).

Поскольку один элемент <wsdl:types> может содержать любое количество определений схемы, нет никаких причин использовать в документе WSDL более одного элемента <wsdl:types>. В листинге 1 элемент <wsdl:types> находится в верхней части BookServerInterface.wsdl.

Помимо <wsdl:import> и <wsdl:types>, всем компонентам верхнего уровня документа WSDL присвоены отдельные имена с использованием обязательного атрибута name. При использовании атрибута targetNamespace в корневом элементе <wsdl:definitions> документа (что обычно лучше всего), имена этих компонентов определены в этом целевом пространстве имен. Это означает, что при определении имени достаточно присвоить простую, или «локальную», часть имени, но ссылки на этот компонент должны уточнять имя с помощью префикса пространства имен или с помощью пространства имен по умолчанию. На рисунке 1 показаны наиболее важные связи между компонентами WSDL, причем сплошные линии соответствуют ссылкам по полному имени, а пунктирные ― по имени, используемому для идентификации без уточнения пространства имен.

Рисунок 1. Связи между компонентами WSDL
Связи между компонентами WSDL

Сообщения, представленные элементами <wsdl:message>, расположены в ядре описаний сервисов WSDL. Элементы <wsdl:message> - это описания XML-данных, передаваемых между клиентом и поставщиком услуг. Каждый элемент <wsdl:message> содержит ноль или более (обычно один) дочерних элементов <wsdl:part>. Для каждого элемента part требуется собственный атрибут name(уникальный в пределах <wsdl:message>) и один из атрибутов element или type, ссылающийся на определение схемы XML-данных. Несколько элементов <wsdl:message> показаны в листинге 1, после элемента <wsdl:types> в BookServerInterface.wsdl.

Элементы <wsdl:portType> определяют абстрактный интерфейс сервиса в части сообщений, передаваемых сервису и принимаемых от него. Элементы <wsdl:portType> содержат любое количество дочерних элементов <wsdl:operation>. Каждому дочернему элементу <wsdl:operation> требуется собственный атрибут name (BP WS-I требует, чтобы он был уникальным в пределах <wsdl:portType>), и в нем содержится один или несколько дочерних элементов, описывающих сообщения, используемые операцией. Дочерние элементы бывают трех типов, соответствующих разным способам использования:

  • <wsdl:input> : данные, отправляемые клиентом поставщику услуг в качестве входных данных для операции;
  • <wsdl:output> : данные, возвращаемые клиенту поставщиком услуг как результат операции;
  • <wsdl:fault> : данные, возвращаемые клиенту поставщиком услуг при возникновении ошибки в процессе обработки.

WSDL 1.1 определяет несколько шаблонов взаимодействия между клиентом и поставщиком услуг, представленных различными последовательностями дочерних элементов <wsdl:input> и <wsdl:output>, но не все модели достаточно хорошо определены, чтобы их можно было реализовать. BP WS-I допускает всего две модели: операций типа запрос-ответ, где за <wsdl:input> следует <wsdl:output>, и односторонние операции, содержащие только <wsdl:input>. В случае операций типа запрос-ответ (на сегодняшний день наиболее распространенный тип) за элементами <wsdl:input> и <wsdl:output> может следовать любое количество элементов <wsdl:fault>.

Каждый элемент <wsdl:input>, <wsdl:output> или <wsdl:fault> ссылается на описание сообщения посредством обязательного атрибута message. Это ссылка с уточнением пространства имен, поэтому она обычно требует добавления префикса. Примеры можно увидеть в листинге 1: например, когда элемент <wsdl:input message="tns:getBookMessage"/> используется в описании операции getBook. (Префикс tns служит определением корневого элемента <wsdl:definitions> с тем же пространством имен URI, что и у атрибута targetNamespace.)

Во многих отношениях <wsdl:portType> можно считать логическим эквивалентом интерфейса Java, так что элементы <wsdl:operation> эквивалентны методам, элементы <wsdl:input> - параметрам методов, элементы <wsdl:output> - результатам методов, а элементы <wsdl:fault> - проверяемым исключениям. Эти соответствия используются при генерировании кода Java из WSDL, как и в большинстве инструментов, создающих WSDL из существующего кода Java.

Сравнение SOAP 1.1 и 1.2

SOAP 1.1 широко используется для Web-сервисов с момента опубликования спецификации в 2000 году. Версия SOAP 1.2 разработана при более широкой поддержке отрасли через W3C и опубликована в качестве официального стандарта W3C в 2007 году. SOAP 1.2 лучше документирована и чище, чем SOAP 1.1, причем некоторые уродливые аспекты 1.1 хирургически удалены. Несмотря на эту очищенную структуру, для большинства Web-сервисов практических различий между ними немного. Вероятно, наиболее существенная особенность SOAP 1.2 заключается в том, что это единственный официально поддерживаемый способ использования расширенной поддержки SOAP-вложений XML-binary Optimized Packaging (XOP) и SOAP Message Transmission Optimization Mechanism (MTOM). В цикле Web-сервисы Java я до сих пор использовал SOAP 1.1, потому что некоторые старые стеки не поддерживают SOAP 1.2, но для разработки новых Web-сервисов 1.2, вероятно, является лучшим выбором.

Элементы <wsdl:binding> представляют собой экземпляр абстрактного интерфейса, определенного <wsdl:portType>, который виден в листинге 2 в начале BookServerImpl.wsdl. Атрибут type содержит полное имя типа порта, реализованного в привязке.

Дочерние элементы <wsdl:binding> содержат подробную информацию о способе реализации типа порта. Дочерние элементы из пространства имен WSDL соответствуют элементам <wsdl:portType> и должны использовать то же значение nameа не ссылки с уточнением пространства имен, как в случае <wsdl:portType>. На рисунке 1 эта связь показана пунктирными линиями на уровне <wsdl:operation>. Та же связь по имени относится и к дочерним элементам <wsdl:input>/<wsdl:output>/<wsdl:fault> элементов <wsdl:operation>. Несмотря на такое повторное использование одних и тех же имен элементов, содержание этих элементов значительно различается, когда это дочерние элементы <wsdl:binding>, а не элемента <wsdl:portType>.

Расширения, определяемые WSDL, вступают в игру в <wsdl:binding>. Дочерний элемент <soap binding> используется в определении сервиса SOAP (единственный тип сервиса, допускаемый BP WS-I, хотя WSDL 1.1 допускает еще и привязки HTTP). Этот элемент <soap:binding> использует обязательный атрибут transport для определения вида транспорта, используемого привязкой. (HTTP, как видно из значения http://schemas.xmlsoap.org/soap/http в листинге 2, - это единственный выбор, разрешенный ВР WS-I.) Необязательный атрибут style позволяет выбирать между стилями rpc и document для представления XML-данных (наиболее распространенное значение document соответствует сообщениям с использованием элементов определения схемы, а не типа).

Внутри каждого дочернего элемента <wsdl:operation> элемента <wsdl:binding> элемент <soap:operation> может использоваться для указания значения SOAPAction с целью идентификации запросов вызова этой операции (и потенциально также для переопределения выбора стиля document или rpc, указанного элементом <soap:binding>, хотя BP WS-I запрещает такое использование). Каждый дочерний элемент <wsdl:input>/<wsdl:output>/<wsdl:fault> содержит другой дополнительный элемент, который в листинге 2 всегда является элементом <soap:body> (указывающим, что данные сообщения передаются в теле сообщения SOAP – данные и даже ошибки можно передавать также в заголовках SOAP, хотя я не рекомендую это) для <wsdl:input> или <wsdl:output>, или его эквивалент <soap:fault>, используемый с <wsdl:fault>.

Последним компонентом описания сервиса WSDL является элемент <wsdl:service>, который состоит из группы элементов <wsdl:port>. Каждый элемент <wsdl:port> связывает адрес доступа с <wsdl:binding>. Адрес доступа содержится во вложенном дополнительном элементе <soap:address>.


Работа с WSDL

Не удивительно, что при всех вариациях схем и правил для документов WSDL 1.1 многие документы не соответствуют практическим рекомендациям BP WS-I. Поддержка всеми стеками Web-сервисов многих отклонений от практических рекомендаций помогла увековечить использование устаревших или неправильных конструкций, что привело к распространению дурной практики по всей отрасли. И я определенно не застрахован от этой инфекции – просматривая WSDL-документы, которые я приводил в качестве примеров кода для этого цикла, я к своему удивлению обнаружил, что ни один из них не является полностью корректным.

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

Для работы с WSDL-документами на языке Java построено множество различных моделей, в том числе широко используемый язык описания Web-сервисов для Java Toolkit (WSDL4J), который представляет собой эталонную реализацию JSR 110 (см. раздел ресурсы). Ни одна из этих моделей не соответствует тому, что я собирался сделать, ввиду двоякой постановки задачи: во-первых, чтение WSDL-документов в любой полуразумной форме и сообщение об ошибках и отклонениях от практических рекомендаций, и во-вторых, написание безошибочных WSDL-документов, переформатированных в форму, соответствующую практическим рекомендациям. WSDL4J, например, не сохраняет порядок вводимых элементов, так чтобы можно было сообщать о проблемах порядка их следования, и не обрабатывает определения схемы, так что его нельзя напрямую использовать для проверки ссылок из элементов <wsdl:part>. Так что мне пришлось выбирать между более реалистичной постановкой задачи и написанием своей собственной модели. Естественно, я решил написать собственную модель.

Модель WSDL

Валидация и верификация

В этой статье я использую термин верификация для обозначения проверки правильности WSDL-документа, потому что альтернативный термин валидация, обычно используемый для XML-документов, означает проверку документов на соответствие определению схемы.

Ранее я уже частично реализовал модель WSDL для использования с привязкой данных JiBX в рамках проекта JiBX/WS. Эта модель предназначена только для вывода и включает относительно небольшое число классов, которые в некоторых случаях объединяют данные из вложенных элементов структуры WSDL XML (<wsdl:message> в сочетании с одним дочерним элементом <wsdl:part>, <wsdl:input>, <wsdl:output> и <wsdl:fault> внутри <wsdl:binding> в сочетании с элементом <soap:body> или <soap:fault> и т.д.). Эта компактная структура классов облегчила построение подмножества WSDL-документов, поддерживаемых структурой, но когда я стал рассматривать возможность создания инструмента верификации и реструктуризации на базе этой модели, я понял, что модель для поддержки ввода, возможно, плохо структурированного WSDL должна быть ближе к XML-представлению.

Еще один вариант ― генерирование кода из схемы BP WS-I для WSDL 1.1. Увидев это, я понял, что простое использование созданных классов напрямую приведет к путанице, так как схема включает избыточные типы, а также некоторые неудобные конструкции, которые используются для представления различных моделей обмена сообщениями (некоторые из которых затем были запрещены текстом BP WS-I).

Так что в конечном итоге я составил классы вручную, хотя результат оказался почти таким же, как если бы я начал с кода, сгенерированного из схемы, и просто сократил ненужное дублирование и сложность. Привязка данных JiBX поддерживает несколько связей с одними и теми же классами, так что мне удалось создать привязку ввода для обработки всего спектра вариантов, допускаемых любой версией WSDL 1.1, хотя настройка привязки выхода для вывода WSDL была только в форме, соответствующей практическим рекомендациям.

В листинге 3 показана часть класса Definitions, соответствующая корневому элементу <wsdl:definitions>.

Листинг 3. Класс Definitions (частично)
public class Definitions extends ElementBase
{
    /** Перечисление дочерних элементов в ожидаемом порядке. */
    static enum AddState {
        invalid, imports, types, message, portType, binding, service };

    /** Список разрешенных  имен атрибутов. */
    public static final StringArray s_allowedAttributes =
        new StringArray(new String[] { "name", "targetNamespace" });

    /** Валидация используемого контекста. */
    private ValidationContext<ElementBase,Definitions> m_validationContext;

    /** Текущее состояние (используется для проверки порядка, в котором добавляются*/ 
    /** дочерние элементы). */
    private AddState m_state;

    /** Имя этого определения. */
    private String m_name;

    /** Целевое пространство имен WSDL. */
    private String m_targetNamespace;

    /** Список всех импортируемых дочерних элементов. */
    private List<Import> m_imports = new ArrayList<Import>();

    /** Список всех типов дочерних элементов. */
    private List<Types> m_types = new ArrayList<Types>();

    /** Список всех сообщений дочерних элементов. */
    private List<Message> m_messages = new ArrayList<Message>();

    /** Список всех дочерних элементов portType. */
    private List<PortType> m_portTypes = new ArrayList<PortType>();

    /** Список всех привязок дочерних элементов. */
    private List<Binding> m_bindings = new ArrayList<Binding>();

    /** Список всех сервисов дочерних элементов. */
    private List<Service> m_services = new ArrayList<Service>();

    /** Отображение квалифицированного имени на сообщение в этом определении. */
    private Map<QName,Message> m_nameMessageMap =
        new HashMap<QName,Message>();

    /** Отображение квалифицированного имени на тип порта в этом определении. */
    private Map<QName,PortType> m_namePortTypeMap =
        new HashMap<QName,PortType>();

    /** Отображение квалифицированного имени на сообщение в этом определении. */
    private Map<QName,Binding> m_nameBindingMap =
        new HashMap<QName,Binding>();

    /** Отображение квалифицированного имени на сервис в этом определении. */
    private Map<QName,Service> m_nameServiceMap =
        new HashMap<QName,Service>();
    ...
    /**
     * Проверка переходных состояний между различными типами дочерних элементов. 
     * Если элементы не находятся в ожидаемом порядке,
     * для отчета отмечается первый элемент вне ожидаемого порядка.
     * @param state new add state
     * @param comp element component
     */
    private void checkAdd(AddState state, ElementBase comp) {
        if (m_state != state) {
            if (m_state == null || (m_state != AddState.invalid &&
                state.ordinal() > m_state.ordinal())) {

                // переход к другому типу дочерних элементов
                m_state = state;

            } else if (state.ordinal() < m_state.ordinal()) {

                // отчет о дочерних элементах вне ожидаемого порядка
                m_validationContext.addWarning
                    ("Child element of wsdl:definitions out of order", comp);
                m_state = AddState.invalid;
            }
        }
    }
    ...
    /**
     * Добавление немаршаллизированного дочернего элемента wsdl:message. 
     * Здесь же сообщение  индексируется по имени для доступа с целью валидации.
     *  
     * @param child
     */
    public void addMessage(Message child) {
        checkAdd(AddState.message, child);
        m_messages.add(child);
        addName(child.getName(), child, m_nameMessageMap);
    }
    ...

Организация данных дочерних элементов в листинге 3 показывает, каким образом модель поддерживает как общую форму ввода, так и форму вывода в соответствии с практическими рекомендациями. Вместо единого списка дочерних элементов всех типов, используются отдельные списки для каждого типа. Привязка ввода JiBX обрабатывает дочерние элементы как неупорядоченный набор, вызывая специфический для данного типа элементов set-метод всякий раз, когда дочерний элемент находится не на своем месте. Вместо того чтобы заменять какое-либо из предшествующий значений, set-метод добавляет экземпляр в типизированный список, как видно из set-метода addMessage(), который используется для дочерних элементов <wsdl:message>. Каждый set-метод запускает также проверку состояния для отлова неупорядоченных элементов.

В любом из элементов WSDL разрешены дополнительные атрибуты и элементы (как правило, все атрибуты или элементы, которые не используют пространство имен WSDL 1.1). Примером таких дополнительных элементов служат конфигурации WS-Policy, встроенные в WSDL-документы из предыдущих статей данного цикла, как и ссылки на фактические политики. Лучше всего, чтобы эти дополнительные элементы предшествовали любым дочерним элементам из пространства имен WSDL 1.1, и именно так они обрабатываются в привязке вывода. Привязка ввода обрабатывает дополнительные элементы и атрибуты с помощью кода базового класса из классов элементов WSDL, не показанного в листинге 3, и позволяет элементам следовать в любом порядке (генерируя предупреждение, если они следуют за элементом из пространства имен WSDL 1.1).

Модель обрабатывает известные элементы, используя отдельные привязки для каждого дополнительного пространства имен, каждая из которых имеет свой собственный набор классов. Я рассмотрю обработку этих дополнительных элементов более подробно в следующем выпуске Web-вервисов Java, где будет подробнее представлен и исходный код.

Верификация модели

Некоторая базовая верификация данных WSDL выполняется по мере добавления немаршаллизованных объектов, соответствующих элементам, в древовидную структуру WSDL-документа, как показано в коде addMessage()в конце листинга 3. Этот код использует метод checkAdd() для проверки порядка следования дочерних элементов и метод addName() для проверки того, что представлено допустимое имя (текст соответствует типу схемы NCName и значение уникально в пределах типа элемента), и отображения имени на объект. Но это только проверка самой основной информации для отдельного элемента; для проверки других свойств каждого элемента и взаимосвязей между элементами необходим дополнительный код верификации.

JiBX позволяет вызывать обработчики пользовательских расширений в рамках процесса маршаллинга и демаршаллинга. Для исполнения логики верификации модель WSDL использует один из таких дополнительных обработчиков, метод post-set. Метод post-set вызывается после завершения демаршаллинга связанного объекта, так что это часто хороший способ выполнения проверок типа верификации объектов. В случае проверки WSDL простейший подход – это выполнение всей верификации объектов из одного метода post-set для корневого элемента <wsdl:definitions>. Такой подход позволяет избежать проблем прямых ссылок на компоненты WSDL-документа, когда компоненты не следуют в ожидаемом порядке.


Другие дополнения

В этой статье изложены основы структуры и использования WSDL и введение в модель данных Java для WSDL, предназначенную для поддержки верификации WSDL-документов и их преобразования в форму, соответствующую практическим рекомендациям.

Следующая статья этого цикла продолжит эту тему, рассматривая проблемы, часто встречающиеся при написании утверждений WS-Policy и WS-SecurityPolicy. Кроме того, в ней будет более подробно рассмотрена модель WSDL и процесс верификации, в том числе расширение модели с включением утверждений WS-Policy/WS-SecurityPolicy, встроенных в WSDL.

Ресурсы

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы в первый раз заходите в developerWorks. Выберите данные в своем профиле (имя, страна/регион, компания) которые будут общедоступными и будут отображаться, когда вы публикуете какую-либо информацию. Вы можете изменить данные вашего ИБМ аккаунта в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Технология Java, SOA и web-сервисы
ArticleID=784531
ArticleTitle=Web-сервисы Java: Освоение и моделирование WSDL 1.1
publish-date=02082011