Web-сервисы Java, часть 3: Связывание данных в Axis2 (исходники)Источник: IBM developerWorks Россия Денис Сосноски
Несмотря на то, что обмен сообщениями XML лежит в основе Web-сервисов, большинство приложений Web-сервисов не имеют отношения к XML. Вместо этого приложения обмениваются бизнес-данными, специфичными для каждого конкретного приложения. В данном случае XML - это просто формат, используемый для представления бизнес-данных, поддерживающий интерфейс Web-сервиса. XML отлично подходит для этой цели, поскольку он предоставляет платформенно-независимое представление данных, которое может обрабатываться различными инструментами. Однако приложениям в конечном счете приходится преобразовывать XML в собственные внутренние структуры данных, используемые в приложении, и обратно. Связыванием данных называют методики преобразования между XML и структурами данных приложения. Можно написать собственный код связывания данных для приложения, но большинство разработчиков предпочитают работать со средами связывания данных, которые выполняют преобразование типовым образом, применимым к широкому спектру приложений. Одной из основных сильных сторон среды Web-сервисов Apache Axis2 является то, что она с самого начала разрабатывалась для работы со множеством сред связывания данных. Вы можете выбрать тот подход к связыванию данных, который более всего подходит к вашим потребностям, и использовать этот подход для обработки преобразований между XML и структурами данных, используя при этом среду Axis2 (и ее расширения) для управления работой собственно Web-сервиса. В этой статье показано, как использовать гибкость Axis2 в выборе подхода к связыванию данных, на примерах кода, реализующего один и тот же Web-сервис с помощью каждого из трех поддерживаемых подходов. Вы узнаете, почему один способ связывания данных может быть предпочтительнее других. Ссылки на Axis2Из предыдущей статьи этой серии вы узнали о модели документов AXIOM, которую Axis2 использует для обработки сообщений XML. AXIOM отличается от других моделей документов тем, что она поддерживает формирование модели не только целиком, но и по необходимости. При использовании среды связывания данных для преобразования XML в структуры данных приложения и наоборот связываемый с данными XML обычно является лишь частью модели документа AXIOM. Модель не расширяется до полной модели документа до тех пор, пока это не понадобится по какой-либо причине (например, для шифрования или подписывания документов с помощью WS-Security). Для того чтобы отделить приложение от работы с AXIOM напрямую, Axis2 поддерживает генерацию кода связи на основе описания сервиса на языке описания Web-сервисов (Web Services Description Language, WSDL). Сформированный код связи выполняет преобразование структур данных в XML и обратно, используя выбранную вами среду связывания данных, предоставляя вашему приложению прямой доступ к структурам данных. Кроме того, в Axis2 реализована ограниченная поддержка обратного процесса - формирования описания WSDL из существующего кода. Axis2 формирует код связи как для клиентов сервисов, так и для сервис-провайдеров. Клиентский код связи - своего рода класс-заглушка, расширяющий класс Код связи клиентаКод заглушки (stub) с клиентской стороны определяет методы доступа, которые будет использовать код вашего приложения для вызова операций сервиса. Сначала мы создаем экземпляр класса заглушки - либо с помощью конструктора по умолчанию (если конечная точка вашего сервиса всегда будет такой же, какая определена в WSDL, используемом для формирования заглушки), или с помощью конструктора, принимающего в качестве строкового параметра иную конечную точку. После того как мы создали экземпляр заглушки, мы можем по желанию использовать методы, определенные базовым классом В Листинге 1 показан пример создания заглушки с изменением конечной точки сервиса (по сравнению с указанной в WSDL) на порт клиентской системы (localhost) Tcpmon по умолчанию (8800). Tcpmon - это очень популярный инструмент мониторинга обмена сообщениями Web-сервиса, поэтому наличие такой возможности в клиентском коде часто бывает очень полезным. После создания экземпляра заглушки изменяется значение тайм-аута, назначаемое по умолчанию (также полезно при отладке кода провайдера, поскольку вы можете превысить стандартное значение в 20 секунд), и вызывается метод сервиса. Листинг 1. Пример кода заглушки с клиентской стороны
В Листинге 1 показан синхронный вызов метода сервиса, при котором клиентский поток блокируется вызовом сервиса и не возвращается до завершения вызова и получения результатов. В Axis2 также поддерживаются асинхронные вызовы с помощью интерфейса обратного вызова. В листинге 2 показан модифицированный код Листинга 1, куда добавлен обычный асинхронный вызов (обычный в том смысле, что код приложения просто ждет завершения операции, а не делает чего-либо полезного). Листинг 2. Асинхронный код клиентской заглушки
По соединению HTTP, использованному в Листинге 2, ответ обычно возвращается клиенту немедленно. Асинхронные вызовы наиболее полезны при работе по транспорту, который разделяет потоки запроса и ответа - например, Java™ Message Service (JMS) или Simple Mail Transfer Protocol (SMTP) -, поскольку между отправлением запроса и получением ответа могут быть значительные временные задержки. Конечно же, сервисы, работающие по HTTP, также могут иметь значительные задержки обработки. Для работы с сервисами HTTP с такими задержками можно использовать WS-Addressing, который позволяет отделить ответы от запросов, а для обработки этих ответов использовать асинхронные вызовы. Кроме класса заглушки (и класса обработки обратного вызова, если вы используете поддержку асинхронного обмена данными), формируется еще один интерфейс для вашего клиентского кода. Интерфейс определяет методы сервиса, соответствующие операциям, определяемым конструкцией WSDL Если вы просто хотите работать с XML на клиентской стороне, вам не нужно использовать сформированный клиентский класс stub совсем; вместо этого вы можете использовать класс Серверный код связиСерверный код связи для Axis2 представляет собой приемник сообщений, определенный в рамках конфигурации сервиса Axis2. В этом приемнике сообщений должен быть реализован интерфейс Если вы работаете напрямую с XML (в виде элементов AXIOM), вы можете использовать один из стандартных классов В Листинге 3 показан пример серверного скелета (формат изменен для удобства чтения), где метод Листинг 3. Пример серверного скелета
Издержки добавления кода непосредственно в этот класс заключаются в том, что при изменении интерфейса сервиса вам придется сгенерировать класс еще раз и снова внести изменения. Этого можно избежать с помощью отдельного класса, который дополняет сформированный скелет, позволяя переписывать методы скелета без изменения сформированного кода. Для этого вам нужно изменить сгенерированное описание сервиса services.xml. Это делается просто; достаточно заменить название класса скелета на название класса конкретной реализации. Во всех рассматриваемых ниже в этой статье примерах связывания данных используется подход с отдельным классом реализации. Эти примеры можно увидеть в файлах Ant build.xml в разделе файлы для загрузки, где показано, как автоматизировать замену. Инструменты Axis2Axis2 предоставляет разработчикам ряд инструментов, облегчающих работу со средой. Наиболее важными из них являются инструменты, которые позволяют формировать код связи Java (описанный в предыдущем разделе) из определения сервиса WSDL и формировать определение сервиса WSDL на основе существующего кода Java. В состав Axis2 входит инструмент WSDL2Java, служащий для формирования кода из описания сервиса WSDL. Вы можете использовать этот инструмент напрямую, запуская класс В инструменте WSDL2Java реализовано множество параметров командной строки, и их число постоянно растет. В документации по Axis2 содержится полный список этих параметров, поэтому здесь мы рассмотрим только наиболее важные из их числа:
Также в WSDL2Java реализовано несколько параметров, специфичных для отдельных сред связывания данных. Вы можете увидеть часть из этих параметров, когда перейдете к примерам связывания данных ниже в этой статье. В состав Axis2 также входит инструмент Java2WSDL, который вы можете использовать для создания определения сервиса WSDL на основе существующего кода сервиса. Однако полезность этого инструмента страдает от множества ограничений, в том числе невозможности работы с классами коллекций Java и отсутствия гибкости при структурированию XML, формируемого классами Java. Отчасти эти ограничения связаны с недостаточным интересом к этой области вследствие изменения способов разработки Web-сервисов. Вообще говоря, многие авторитеты в области Web-сервисов и SOA не одобряют разработку Web-сервисов на основе существующего кода. Они считают, что использование кода привязывает структуру сообщений XML к определенной реализации, тогда как принцип Web-сервисов заключается в том, что XML не должен зависеть от реализации. Конечно же, у этой точки зрения есть некоторые основания, но и у ее противников также достаточно аргументов. Один из них состоит в сложности написания определений сервисов WSDL и схем XML с нуля. И WSDL, и схема представляют собой достаточно сложные стандарты, и для эффективного применения существующих инструментов для работы с этими определениями требуется хорошее знание этих стандартов. Если их использует разработчик, не разбирающийся в стандартах, получающиеся описание WSDL и схемы часто бывают значительно более запутанными, чем сформированные из кода. Еще одна проблема носит чисто практический характер. Часто у разработчиков есть код, реализующий необходимые функции, который должен быть выполнен в виде Web-сервиса, и они желают использовать этот код без внесения значительных изменений. Поэтому формирование WSDL на основе кода, вероятно, будет в обозримом будущем оставаться проблемой. В качестве более мощной альтернативы Java2WSDL вы можете попробовать использовать разработанный мной инструмент Jibx2Wsdl. Jibx2Wsdl формирует полный комплект определений, связывающих WSDL, схемы и JiBX на основе одного или нескольких классов. Он поддерживает общие коллекции и перечисления Java 5, сохраняя при этом совместимость с более ранними версиями виртуальных машин Java (JVM), а также автоматически экспортирует Javadocs из исходных файлов Java в качестве документации для формируемых определений WSDL и схемы. В Jibx2Wsdl также реализован расширенный механизм настройки, позволяющий управлять тем, как будут определяться сервисы и представление XML из классов Java, что позволяет использовать типизированные данные даже в коллекциях Java версий ниже 5. Несмотря на то, что Jibx2Wsdl специально предназначен для упрощения развертывания существующих классов в качестве Web-сервисов с помощью среды связывания данных JiBX (также созданной мной), сформированные описания WSDL и схемы не зависят от среды связывания данных. Вы можете использовать их с другими средами связывания данных Java или даже с другими платформами - просто сформируйте все, что нужно, отбросьте связывание JiBX и работайте с остальным. Еще одной альтернативой для пользователей Java 5 или более поздних версий являются аннотации Java Architecture for XML Binding (JAXB) 2.0 и Java API for XML Web Services (JAX-WS), которые позволяют развернуть объекты данных и сервисные классы в качестве Web-сервисов. Эти аннотации не предоставляют такого же уровня возможностей настройки, какими отличается Jibx2Wsdl, но они позволяют вам встроить информацию о конфигурации непосредственно в исходный код, что некоторые разработчики находят привлекательным. В Axis2 реализована экспериментальная поддержка JAXB 2.0 и JAX-WS начиная с версии 1.2, и в дальнейших версиях она будет улучшаться. Кроме того, в будущих версиях Jibx2Wsdl также может быть реализована поддержка аннотаций JAXB 2.0 и JAX-WS в виде дополнительных настроек. (В следующей статье этой серии будут подробно рассматриваться JAXB 2.0 и JAX-WS, и мы еще раз вернемся к вопросу формирования WSDL из кода.) Сравнение различных подходов к связыванию данныхВ Axis2 (начиная с версии 1.2) реализована полная поддержка трех вариантов связывания данных и готовится поддержка еще нескольких. В этой статье сравниваются примеры кода, использующие три полностью поддерживаемые среды связывания данных и раскрываются некоторые сильные и слабые стороны использования каждой из этих сред с Axis2. Пример кода, показанный в Листинге 4 (также входит в состав примеров, которые можно загрузить в разделе файлы для загрузки), реализует сервис библиотеки, в которой хранится набор книг, организованных по тематикам. В библиотеке определены несколько операций, в том числе:
В листинге 4 представлен фрагмент WSDL для этого сервиса, в котором показаны только части, участвующие в работе Листинг 4. Сервис библиотеки WSDL
Фактический код реализации сервиса прост, он наполняет экземпляр библиотеки жестко зафиксированным списком книг. Клиентский код выполняет следующую последовательность запросов:
Детали реализации различны в разных примерах, поскольку в каждом из них используются объекты данных, соответствующие объектам, участвующим в конкретном связывании данных. Если не указано иное, весь представленный код совместим с версиями Axis2 1.1.1 и 1.2. Для версии Axis2 1.3, готовящейся к выходу на момент публикации этой статьи, требуется незначительное изменение кода вследствие изменения названий формируемых классов исключений, соответствующих сбоям в работе сервиса. Обе версии кода доступны для загрузки (см. раздел файлы для загрузки). В этой статье мы рассмотрим только клиентский код, хотя загрузить можно (см. раздел файлы для загрузки) и клиентский, и серверный код вместе с файлами компиляции Ant для всех примеров. После этого мы сравним клиентский код для трех сред связывания данных и рассмотрим сильные и слабые стороны каждого подхода. Axis2 Data BindingADB (Связывание данных Axis2) - это расширение Axis2 для связывания данных. В отличие от других сред связывания данных, код ADB можно использовать только вместе с Web-сервисами Axis2. Это обстоятельство значительно ограничивает использование ADB, однако оно также даёт определенные преимущества. Поскольку ADB интегрировано с Axis2, код может быть оптимизирован под требования Axis2. В качестве одного из примеров можно назвать то, что ADB построено на модели документа AXis Object Model (AXIOM), которая является основой Axis2 (как обсуждалось в предыдущей статье этой серии). Кроме того, в ADB реализованы некоторые полезные функции, отсутствующие на данный момент в других средах связывания данных, в том числе автоматическая обработка вложений. В WSDL2Java реализована полная поддержка формирования кода ADB, в том числе формирование классов модели данных, соответствующих компонентам схемы XML. Поддержка схем в ADB имеет некоторые ограничения. В текущей версии Axis2 1.2 к этим ограничениям относятся такие возможности работы со схемами, как компоновщики с В базовом варианте формирования кода ADB используется прямая модель, в которой каждому входящему и исходящему сообщению, используемым в каждой операции, назначается отдельный класс. В Листинге 5 показаны наиболее интересные фрагменты клиентского кода примера формирования кода ADB таким образом. В клиентском коде видно взаимодействие с классами, созданными ADB, к которым относятся классы
В Листинге 5 используется формирование кода с параметром WSDL2Java -u, предназначенным специально для ADB. При использовании этого параметра для каждого класса модели данных и сообщения формируется отдельный исходный файл Java; если вы не используете этот параметр, ADB сформирует код таким образом, что все эти классы будут статическими внутренними классами создаваемой заглушки. Работать с отдельными классами значительно легче, поэтому, если вы используете ADB, указывайте параметр -u. Прямая форма генерирования кода приводит к созданию большого числа классов для ввода и вывода каждой из операций (независимо от параметра -u, который просто организует одни и те же классы различным образом). В этих сформированных классах сообщений часто содержится мало (или даже вообще не содержится, как в случае класса Распакованный ADBЧасто Web-сервисы разрабатываются на основе существующих интерфейсов API в виде вызова методов, и в этом случае может быть очень полезно встроить существующий API в Web-сервис. Это легко сделать; операции, определенные для сервиса (формально для portType, если быть более точным), по существу эквивалентны вызову методов из определения интерфейса. Единственное значимое отличие состоит в том, что входные и выходные данные сервиса представляют собой сообщения XML, вместо входных и возвращаемых значений вызова. Поэтому для того, чтобы встроить существующий API в описание Web-сервиса, вам достаточно определить, как представлять параметры вызова и возвращаемые значения в виде структуры сообщений XML. К счастью, Microsoft® давно уже установила конвенцию в этой области, что позволяет нам не изобретать велосипед. Это соглашение называется wrapped document/literal (упакованным документально-литеральным) и является представлением, используемым .NET по умолчанию при объявлении вызова метода операцией Web-сервиса. По существу "упакованный" (wrapped) подход говорит о том, что каждое входящее сообщение является элементом XML, который состоит только из последовательности дочерних элементов, а каждое исходящее сообщение является элементом XML с одним дочерним элементом. В реализации Microsoft есть еще некоторые технические детали, которые на самом деле не особо важны, если вам не требуется полная совместимость с .NET, но мы их опустим; скажем только, что сообщения, использованные в примере с библиотекой (фрагмент представлен в Листинге 4) следует этому подходу. WSDL2Java поддерживает распаковывание таких документально-литеральных сервисов в рамках формирования кода ADB. При использовании распаковки с соответствующим определением сервиса WSDL, созданная клиентская заглушка (а также серверный скелет) получается значительно более простой и понятной. В Листинге 6 показан код клиентского приложения, эквивалентный приведенному в Листинге 5, но с переданным WSDL2Java параметром uw для создания распакованного интерфейса. Классы сообщений, на порядок увеличивающие сложность в Листинге 5, большей частью исключаются в Листинге 6 (за исключением класса Листинг 6. Распакованный клиентский код ADB
К моменту выхода версии, следующей за версией Axis2 1.2, проблемы распаковки ADB должны быть большей частью устранены. Однако ADB - это не единственная среда связывания данных для Axis2, поддерживающая распаковку. В JiBX также реализована поддержка распаковки, и со времени выхода Axis2 1.1.1 версия JiBX стабильна. Увидеть клиентский код JiBX вы сможете чуть ниже в этой статье, после того, как мы рассмотрим остальные варианты связывания данных Axis2.
XMLBeans - это общая среда обработки XML, в состав которой входит слой связывания данных. Она создавалась как проект BEA Systems, а впоследствии была передана организации Apache Foundation. XMLBeans была первой формой связывания данных, поддерживаемой Axis2, и продолжает быть наиболее популярным вариантом работы с Axis2, особенно со сложными определениями схем. В Листинге 7 показаны наиболее интересные фрагменты клиентского кода XMLBeans для нашего примера. Так же, как и в базовом коде ADB (без распаковки) для ввода и вывода каждой операции создается отдельный класс. Однако XMLBeans отличается от ADB тем, что в ней добавляется класс для документа, который содержит класс ввода или вывода (например, Listing 7. XMLBeans client code
Хотя в XMLBeans заявлена поддержка схемы XML на 100%, точность этого заявления зависит от трактовки. Верно, что почти для всех конструкций схемы XMLBeans формирует набор Java-классов, которые могут быть использованы для чтения и записи документов, соответствующих схеме. Однако, в отличие от других сред связывания данных, описанных в этой статье, XMLBeans по умолчанию практически ничего не делает для обеспечения соблюдения схемы. Например, если вы закомментируете строку, которая устанавливает название добавляемой книги в коде Листинге 7, XMLBeans будет успешно считывать и сохранять документы без обязательного элемента <title>, которые поэтому не будут соответствовать схеме. В листинге 8 показано это изменение кода вместе с XML, отправленным на сервер для добавления запроса и XML, который был получен с сервера при запросе книг. В случае ответа на запрос в документе содержится элемент <title>, но используется атрибут Листинг 8. Неверный документ и код XMLBeans
Это простой пример опущенного определения обязательного значения. Для более сложных схем интерфейс API, сформированный XMLBeans, может скрывать более серьезные ошибки. В ходе недавних дискуссий в списке рассылки XMLBeans рассматривался случай, в котором, чтобы сформировать правильный вывод, значения было необходимо добавлять в два различных списка в разном порядке. Поэтому для работы с XMLBeans разработчик должен знать и схему, и то, как сформированный код соответствует схеме, чтобы обеспечить формирование кодом приложения корректных документов XML. Одно из основных преимуществ сред связывания данных обычно состоит в том, что такие подробности схемы скрываются от разработчиков, и XMLBeans определенно проигрывает на этом фронте. Вы можете избежать проблем обработки и формирования не соответствующих правилам документов XML при работе с XMLBeans, вызывая метод JiBXJiBX (также разработанная мной) представляет собой среду связывания данных, которая главным образом фокусируется на работе с существующими Java-классами, а не с кодом, сформированным на основе схемы. При использовании JiBX сначала нужно создать описание связывания, определяющее, как объекты Java будут преобразовываться в XML и обратно, после чего скомпилировать это связывание с помощью инструмента, дополняющего файлы класса данных новыми методами (в виде байт-кода), реализующими преобразование. После этого интерактивная среда JiBX использует эти добавленные методы для преобразования данных в XML и обратно. Подход JiBX имеет свои сильные и слабые стороны. К плюсам можно отнести то, что JIBX позволяет работать напрямую с существующими классами в случаях, когда к существующему кода сервиса добавляются новые интерфейсы Web-сервиса. Для этой цели особенно полезен инструмент Jibx2Wsdl, поскольку он формирует все, что нужно для простого развертывания существующего кода как сервиса Axis2. Вы можете определить несколько связываний для одного класса, чтобы одновременно работать с различными версиями документов XML, используя одну модель данных. Модифицировав связывание, вы можете даже сохранить представление XML при изменении классов данных. В Листинге 9 показаны наиболее интересные фрагменты клиентского кода JiBX, использующего классы, соответствующие элементам сообщений. Этот код похож на его эквивалент для ADB, приведенный в Листинге 5, поэтому я не буду вдаваться в детали. Единственное заметное отличие вызвано тем, что и классы данных, и классы сообщений находятся под управлением пользователя, и поэтому при использовании JiBX очень просто добавлять классам вспомогательные конструкторы (как в случае с
В листинге 10 показан эквивалентный код JiBX с распаковкой. Как и в случае с кодом ADB с распаковкой, распакованную форму вызовов сервиса гораздо проще понимать и использовать, чем прямую. Единственная значительная разница между версиями JiBX и ADB состоит в том, что в случае JiBX вам не нужно создавать объект, когда значение не передается, как это было для вызова Листинг 10. Клиентский код JIBX с распаковкой
Поддержка распаковки в JiBX также отличается от ADB с точки зрения используемых классов. При использовании распаковки в ADB классы для всех элементов сообщений по-прежнему создаются и используются незаметно для пользователя. В JiBX при использовании прямой формы, как в Листинге 9; , необходимо определить классы для всех элементов сообщений; при работе с распакованной формой нужно определять и включать в определение связывания только классы, передаваемые в виде значений. В любом случае перед запуском инструмента Axis2 WSDL2Java необходимо создать определение связывания JiBX и передать его в параметре командной строки Самым большим недостатком подхода JiBX к связыванию, по меньшей мере, в отношении Web-сервисов, вероятно, является то, что на сегодняшний день в JiBX слабо реализована поддержка работы с определениями схемы XML. И даже эта слабая поддержка работы со схемой, в виде инструмента Xsd2Jibx, не интегрирована в инструмент формирования кода Axis2 WSDL2Java. Это означает, что перед запуском WSDL2Java для формирования кода связи Axis2 вам нужно создать определение связывания и классов данных Java. Шаг модификации байт-кода, необходимый в JiBX, также может вызывать проблемы в некоторых средах, поскольку его, в общем случае, необходимо выполнять во время сборки приложения; кроме того, он приводит к появлению кода в классах, не имеющих исходного кода. Связывание данных посредством JiBX имеет ряд уникальных преимуществ, которые обсуждались в начале этого раздела. В отношении использования Axis2 JiBX также предоставляет преимущество над другими средами, которое заключается в том, что он поддерживается вместе с исправлениями, которые могут быть добавлены в Axis2 для исправления проблем, найденных после выхода версии. Единственным способом получения исправлений в других средах является переход на еженощную сборку версий Axis2, что зачастую может вызвать другие проблемы. В будущем ожидается реализация в JiBX эффективного формирования кода и связывания на основании схемы. Когда это будет сделано, JiBX обещает стать отличной универсальной альтернативой связыванию данных для Axis2. До этого времени она скорее подходит для работы с существующим кодом Java, где отлично работает инструмент Jibx2Wsdl. РезюмеНа сегодняшний день в Axis2 реализована полная поддержка трех различных сред связывания данных:
То, что в Axis2 имеется возможность выбора сред связывания данных, замечательно, поскольку нет единого варианта, оптимально подходящего всем требованиям. В будущем в Axis2 будет также реализована полная поддержка JAXB 2.0, о котором я расскажу в рамках этой серии, в статье о JAX-WS 2.0. Знание сильных и слабых сторон каждой из сред может помочь вам сделать оптимальный выбор, соответствующий вашим потребностям, и предупредит о возможных проблемах до того, как вы встретитесь с ними в работе. В следующей статье этой серии вы узнаете о еще одном аспекте сравнения сред связывания данных в Axis2: производительности. |