Работать с SOAP часто бывает сложно, а работа с WSDL может стать огромным вкладом в сложность этой задачи. На самом деле, это может быть наименее ожидаемая вещь, с которой вы столкнетесь, если вы находитесь на современном и модном языке, таком как, например, Scala, который хорошо известен своей реактивностью и асинхронным способом обработки запросов.
Фактически, многие разработчики программного обеспечения, которые недавно вошли в отрасль, могут даже не знать о протоколах SOAP и WSDL и быстро раздражаться или даже злиться при первой попытке подключения к такой устаревшей службе. Итак, должны ли мы отказаться от этого в пользу современного технологического стека или есть менее болезненное решение?
Вам также может понравиться:
Понимание WSDL
МЫЛО: Наследие
Трудно утверждать, что эта вещь SOAP звучит довольно устаревшей в настоящее время, особенно в отличие от текущего состояния технологии. Написание клиента WSDL с нуля с использованием Kotlin, Scala или другого современного языка может быть проблемой, а отсутствие надлежащей документации для него не облегчает жизнь. Но у меня для вас хорошие новости; в темном королевстве SOAP есть пятно света.
Ну, на самом деле, WSDL является единственным. Несмотря на то, что он тяжелый и несколько уродливый, у него есть определенное преимущество. Чрезмерность формата WSDL делает довольно простым генерирование клиентского (а также серверного) кода (возможно, не для людей, но определенно для автоматизированных систем).
Даже по сравнению с современными API-спецификациями, он может фактически остаться на уровне OpenAPI или причудливых концепций Swagger API, где все описано в не зависящей от языка спецификации. Это открывает огромные возможности для взаимодействия между различными платформами и языками, вплоть до уровня реализации. Например, если один предоставляет, скажем, веб-сервис .NET со спецификацией WSDL, другой может автоматически сгенерировать клиент на основе JVM, чтобы подключиться к нему практически без проблем при преобразовании или несовместимости форматов данных.
WSDL Import Magic
Давайте продолжим и поговорим об автоматической генерации кода. Вы можете быть удивлены, но большинство корпоративных платформ, в основном Java и .NET, поставляются с инструментами генерации кода WSDL из коробки. Например, есть wsimport, который входит в дистрибутив JDK. Такие инструменты достаточно мощные и должны охватывать задачи автоматической генерации из конца в конец. Единственная оставшаяся часть — это соединить вашу бизнес-логику с клиентским кодом и использовать его.
Итак, поскольку мы сейчас занимаемся темой Scala, давайте углубимся в wsimport
инструмент Java :
wsimport -p stockquote http://stockquote.example.com/quote?wsdl
Команда принимает схему WSDL в качестве обязательного параметра, и в основном этого достаточно для создания целого набора POJO и интерфейсов, помеченных всеми надлежащими аннотациями. Последние на самом деле делают свое дело: это то, что делает все возможное. При выполнении JVM связывает ваш клиентский код с внутренней клиентской реализацией веб-службы, которая выходит из коробки, поэтому вам не нужно сильно беспокоиться о низкоуровневых сетях и IO. Остальная часть бизнеса — правильно обрабатывать входы и выходы и быть осторожным с ошибками и исключениями.
Вывести автоматизацию на новый уровень с SBT
Хорошо, пришло время для некоторых практических. Представьте, что у нас есть несколько веб-сервисов SOAP, к your.server.com
которым нам нужно подключиться, и они предоставляют WSDL: скажем, для управления любым видом заказов. Запустите генератор кода при указании на orders
веб-сервис:
wsimport -s ../src/main/java -extension -p your.package.wsdl.orders \
-XadditionalHeaders -Xnocompile \
http://your.server.com/orders?wsdl
Она производит ряд сырого кода Java в выходной папке со всеми пакетами с префиксом , как указано выше: your.package.wsdl.orders
. Мы могли бы продолжить подключение нашей бизнес-логики, как предлагалось ранее. Но подождите секунду, что, если изменения на стороне сервера? Мы узнаем об этом только в момент фактического выполнения кода (или в момент сбоя интеграционных тестов, если он у нас есть). Не красиво Это быстро становится уродливым, если вы подумаете о добавлении всего этого стандартного Java-бина-кода в ваш первоначальный репозиторий Scala.
Конечно, было бы гораздо приятнее автоматически генерировать все эти вещи и сохранять их стройными и чистыми. Первым шагом для этого было бы автоматизировать получение всех классов WSDL с помощью одной команды и сделать из этого сценарий Shell. Я действительно сделал один для вас, чтобы вы могли посмотреть: wsdl_import.sh .
Тогда мы могли бы просто обернуть его задачей сборки: давайте возьмем SBT в качестве примера, так как мы находимся на Scala, поэтому что-то вроде этого должно работать:
lazy val wsdlImport = TaskKey[Unit]("wsdlImport", "Generates Java classes from WSDL")
wsdlImport := {
val wsdlSources = "./wsdl/src/main/java"
val d = file(wsdlSources)
if (d.isDirectory) {
// don't forget to rename to your fav one in line with WSDL generating sh
val gen = file(s"$wsdlSources/github/sainnr/wsdl")
if (!gen.exists() || gen.listFiles().isEmpty) {
import sys.process._
println("[wsdl_import] Importing Java beans from WSDL...")
"./wsdl/bin/wsdl_import.sh" !
} else
println("[wsdl_import] Looks like WSDL is already imported, skipping.")
} else
println(s"[wsdl_import] Make sure the directory ${d.absolutePath} exists.")
}
Теперь нам нужно убедиться, что у нас есть весь этот код, прежде чем Scala будет компилироваться по понятным причинам. Просто-напросто, у нас есть SBT, поэтому нам просто нужно выполнить сценарий Shell как задачу SBT, как описано выше, и выполнить все в правильном порядке, верно? Ну, это немного сложнее в реальной жизни. Не вдаваясь в подробности работы SBT, все станет намного проще, если мы разделим эту часть WSDL-Java на самоподдерживающийся подпроект и создадим правильную зависимость в основной конфигурации SBT.
lazy val wsdl = (project in file("wsdl"))
.settings (
publishSettings,
sources in (Compile, doc) := Seq.empty
)
lazy val root = (project in file("."))
.aggregate(wsdl)
.dependsOn(wsdl)
Когда вы компилируете главный проект, SBT сначала гарантирует, что подпроект уже скомпилирован. Но есть одна загвоздка: когда вы только что проверили свой репозиторий, вы, возможно, не выполнили компиляцию. Поэтому, когда вы впервые откроете его в редакторе, некоторые зависимости, конечно, будут отсутствовать. Надеюсь, единственное, что вам нужно, это запустить sbt compile
команду и, возможно, обновить проект в IDE.
Может быть другое предостережение, если вы запускаете приложение Scala в качестве автономного клиента или в компактном веб-контейнере (например, Netty, если вы используете Play Framework). В этом случае весьма вероятно, что во время выполнения приложения будет отсутствовать бит реализации, который помогает JVM сделать магию SOAP за вас, благодаря современным версиям JRE и проекту Java Jigsaw. Не нужно паниковать, просто добавьте несколько библиотек в список зависимостей или добавьте один rt.jar из дистрибутива JRE в качестве неуправляемой зависимости:
// ...
unmanagedJars in Test += Attributed.blank(
file(System.getenv("JAVA_HOME") + "/jre/lib")
),
// ...
Заключение
Итак, подведем итоги: мы немного узнали о SOAP и WSDL и, надеюсь, поняли, что работать не с этим кошмаром, благодаря всем этим генераторам кода и чрезмерным спецификациям WSDL. Мы также выяснили, как автоматизировать грязную работу и нашли способ сохранить наши репозитории нетронутыми и чистыми от нежелательного стандартного кода.
Потребовалось некоторое знание SBT, чтобы правильно настроить порядок компиляции и зависимости, но в конце концов, он должен работать довольно гладко. Чтобы еще больше упростить процесс, я создал небольшой шаблон начальной загрузки, который поможет вам запустить проект в следующий раз: https://github.com/sainnr/sbt-scala-wsdl-template .
Надеюсь, вам понравилось это маленькое путешествие в прошлое!
Дальнейшее чтение
Создание веб-службы SOAP с помощью веб-служб Spring Boot Starter