Самодокументированный JAX-WS с поддержкой XSD Restrictions
В этой статье я расскажу о создании самодокументируемого web сервиса (jax-ws), который используется элементы XSD Restrictions.Описание задачи В книге «Программист прагматик» есть глава, посвящённая документации. В ней сказано, что должен быть один источник информации, а на основе него генерироваться производные формы этой информации. например, в одном формате хранится вся информация о структуре таблиц (это может быть файл любого подходящего или собственного формата), а на основе неё генерируются сами таблицы, документация по этим таблицам, DAO объекты для работы с таблицами. Плюсы это подхода в том, между производными формами представления информации не будет рассинхронизации, ведь единственное место, где будет редактироваться информация — это тот первоначальный источник.Минусы в том, что все заинтересованные люди должны уметь править этот файл и должны быть конвертеры в нужные форматы.В web services эти понятия называется contract first и contract last. Contract first — когда сначала создаётся wsdl, а по ней генерируется Java код. Contract last — наоборот, сначала Java код, а по нему wsdl.Про плюсы и минусы можно почитать по ссылкам: docs.spring.io/spring-ws/sites/1.5/reference/html/why-contract-first.html stackoverflow.com/questions/763827/which-is-the-better-approach-to-web-services-contract-first-or-contract-lastВ этой статье я рассмотрю создание SOAP Web Service с самодокументацией, что бы получить похожее решение — вся информация о web services будет содержаться в нём самом. Получается этакий «contract last» — сначала пишется код, а потом по нему получается документация в виде wsdl, которая содержит максимум информации об этом web сервисе — комментарии ко всем методам и типам, с которыми сервис работает и большую часть логики проверки на корректность данных. Для чего это решение может быть полезно? В том случае, когда вся разработка сосредоточена на написании кода и wsdl генерируется на основе него, а не наоборот. Или когда сложно содержать документацию изначально в wsdl. Но если веб методы пишутся по аналитике, то эту аналитику лучше всё же хранить в wsdl. Т.е. сначала wsdl, а по ней генерируются методы, классы и читаемая документация. Иначе будут проблемы проверки соответствия уже написанного сервиса с тех. заданием.Проверку на корректность входных данных тоже лучше вынести на сторону xsd, что бы можно было проверить запрос без подключения в web сервису и что бы эта логика была видна (в xsd), а не была зашита где то в недрах сервиса.
Варианты решения На текущий момент получить wsdl можно из любого jax-ws приложения. Но стандартные jax-ws/jaxb аннотации не поддерживают документацию к веб сервисам — для этого нужно пользоваться возможностями, которые предоставляют сторонние framework’и. А конкретно — apache CXF (jax-ws) предоставляет аннотацию WSDLDocumentation, которая применяется к веб методам и в которой указывается описание веб метода, которое в последствие попадёт в wsdl. Для документирования xml сущностей, которые будут использоваться в веб методах, используется библиотека jaxb-facets (dsg.tuwien.ac.at/staff/hummer/tools/jaxb-facets.html, github.com/whummer/jaxb-facets). Она позволяет, так же как CXF для веб методов, документировать классы и поля xml bean’ов. По мимо этого она добавляет поддержку XSD Restrictions — дополнительных ограничений на значения полей xml. (описание XSD Restrictions находится здесь www.w3schools.com/schema/schema_facets.asp) Это будет полезно, когда нужна логика проверки корректности значений сложнее чем обязательный/не обязательный — т.к. jaxb не в полной мере поддерживает возможности xsd по части валидации. А конкретно, будет возможно указать ограничения на минимальное, максимальное значения, использовать регулярные выражения, использовать различные способы обработки строк на пустые значения, установить ограничения на длину значения.Реализация В качестве примера создадим простой веб сервис с несколькими методами.В статье я опишу конкретные примеры с кусками кода. Весь же сервис можно скачать по ссылке dl.dropboxusercontent.com/u/7519092/jax-ws-example.zip Это IDEA maven проект. В IDEA можно открыть либо импортом IDEA проекта либо импортом maven проекта. В pom.xml настроена генерация wsdl файла с трансформацией его в html с помощью xslt, но к сожалению, этот wsdl не содержит тега documentation из jaxb-facets, хотя в генерируемом wsdl (доступным по ссылке /web_service/WebService? wsdl) этот тег есть. Так же в проекта отличается применение аннотаций — там аннотации применяются к get методам, а в этих примерах к полям класса. Это сделано для того, что бы сократить статью. Что бы применять аннотации к полям класса нужно либо не делать get методы либо указать @XmlAccessorType (XmlAccessType.FIELD).Примеры В качестве примера рассмотрим несколько конкретных задачОграничение на максимальное и минимальные значение даты и времени. Для date: @XmlSchemaType (name = «date») @Facets (minInclusive = »1900–01–01», maxInclusive = »9999–12–31Z») Date date; Сгенерируемая xsd схема:
У нас нужно было использовать т.к. СУБД поддерживала не весть диапазон дат, доступных в Java. Т.е. в web service можно передать дат, например, 1000–01–01 и в Java она корректно распарсится, но при записи в CУБД будет ошибка, т.к. она не поддерживает такой диапазон дат. И что бы ограничить принимаемые значения вводится это ограничение.Для даты максимальная дата указана с учётом часового пояса. Нужно, что бы поддерживались выходные даты 9999–12–31 с часовым поясом (например 9999–12–31+04:00). Если этого не делать, то при сериализации этого значения (при отдаче клиенту) будет ошибка xsd validation.
Ограничение на конкретную длину поля
@Facets (length = 16)
String id;
Хотя в случае id можно выделить отдельный тип Id:
public class Id { @Facets (length = 16) @XmlAttribute String attr; } И использовать его так:
Id id; Так у нас будет явно указано, что это переменная — уникальный идентификатор, при этом все ограничение будут работать.
Обнуление полей Некоторые необязательные поля нужно обнулять. Для строк это делается очень просто — шлётся пустой тег (e.g.) и при unmarshalling’е (преобразование xml запроса в java объекты) получим просто пустую строку (String.isEmpty () == true). И её вполне можно интерпретировать как строку, которую нужно установить в null.Но если нужно обнулить числа или даты, то при передаче пустого тега будет ошибка: Unmarshalling Error: cvc-datatype-valid.1.2.1: '' is not a valid value for 'integer'
Это вполне объяснимо — пустой тег нельзя представить в виде числа (null не считается — null это отсутсвие тега).
Решить это можно с помощью default значения для поля
@XmlElement (defaultValue = »-2147483648»)
Integer id;
Для дат этот же подход будет выглядеть так:
@XmlSchemaType (name = «date»)
@XmlElement (defaultValue = »1900–01–01»)
Date date;
@WSDLDocumentation («Пример документации для метода. Метод показывает работы с XML Schema Restrictions»)
String facetsExampleMethod (FacetsExample request);
Генерация документации по сервису (wsdl to html)
Для преобразовании сгенерированной wsdl в читаемый вид воспользуемся файлом xslt tomi.vanek.sk/index.php? page=wsdl-viewer Так же SoapUI может генерировать читаемый html. Всё это описано на StackOverflow: stackoverflow.com/questions/686103/generating-html-documentation-from-wsdl Преобразование в html на этапе сборки реализовано в pom.xml Дальше эта страница будет доступна по адресу /WebService.htmlНе сколько замечаний по генерации html: Параметры web методов корректно отображаются только если указан @SOAPBinding (style = SOAPBinding.Style.RPC).Аннотация Documentation попадает в wsdl и, соответственно, в документацию в html только при runtime генерации wsdl из работающего сервиса. При сборке wsdl не содержит документации. Почему так — не знаю. Скорее всего ошибка генератора wsdl.Остальные возможности jaxb-facets
Вкратце расскажу о остальных возможностях jaxb-facets, которые я использовал: Проверка значения по регулярному выражению — Facets.pattern.Установка логики обработки пробельных символов — Facets.whiteSpace. Пробельные символы можно учитывать, убирать, заменять на пробелы (для табов и символов переноса строки). Работает только при проверке в xsd, т.е. в Java код попадёт значение как есть, с пробельными символами. Полезно будет для проверки обязательности заполнения строкового поля, когда поле заполненное пробелами, тоже должно интерпретироваться как пустое.Вообще jaxb-facets поддерживает все xsd restrinction. Ознакомиться с ними можно в конце страницы www.w3schools.com/schema/schema_facets.asp Так же в последней версии (2.2.6) появилась поддержка xs: assert. Пример xs: assert с сайта IBM (www.ibm.com/developerworks/ru/library/x-xml11pt2/):
Замечания Повторюсь, вообще если пишется сервис «с нуля» то лучше сделать корректный wsdl, а по нему уже генерировать Java код и документацию. Но если уже есть написанный web сервис, то добавление в него XSD Restrictions позволит сделать его более прозрачным со стороны клиента — он будет видеть логику проверки входных значений (хотя, понятно, не всю — в некоторых случаях нельзя проверить корректность запросов без обращения к СУБД или каким то связанным ресурсам). Плюс генерация документации позволит удобно сравнить соответствие реальных web методов со своими параметрами, тех. заданию. Без этого пришлось бы проверять или методом «тыка» — смотря запросы и ответы web методов, либо разбираться в хитросплетениях сгенерируемой wsdl.