scala和sbt_使用用于SBT和Scala的WSDL导入自动化SOAP客户端自动生成例程

scala和sbt

Working with SOAP often gets tricky, and dealing with WSDL might be a huge contribution to the complexity of this task. Really, it could be the least expected thing to face when you are into a modern & fancy language like for example, Scala, that is well known for its reactiveness and asynchronous way of dealing with requests. In fact, many of the software developers that have made their way into industry quite recently, might not even know about SOAP and WSDL protocols, and get quickly annoyed or even enraged when first trying to connect to such a legacy service. So, should we deprecate this altogether in favour of modern technology stack, or maybe there is a less painful solution?

使用SOAP通常会很棘手,而处理WSDL可能会大大增加此任务的复杂性。 的确,当您使用像Scala这样的现代语言时,这可能是最不希望面对的事情,它以其React性和异步处理请求而闻名。 实际上,许多最近才进入行业的软件开发人员,甚至可能不了解SOAP和WSDL协议,并且在首次尝试连接到这种传统服务时会很快感到烦恼甚至恼怒。 因此,我们是否应该完全弃用此方法,而采用现代技术堆栈,或者有没有那么痛苦的解决方案?

SOAP:旧版 (SOAP: Legacy)

It’s hard to argue that this SOAP thing sounds quite outdated in these days, especially in contrast with the current state of the technology. Writing a WSDL client from scratch with Kotlin, Scala or other modern language could be a pain, and lack of proper documentation for it doesn’t make life easier. But I have good news for you, there is a spot of light in the dark SOAP kingdom. Well, actually WSDL itself is the one. Despite being heavyweight and somewhat ugly, it has a certain advantage. The excessiveness of WSDL format makes it quite easy generating the client (and also server) code, maybe not for humans but definitely for automated systems.

很难说这几天SOAP听起来已经过时了,特别是与当前的技术状态相比。 用Kotlin,Scala或其他现代语言从头开始编写WSDL客户端可能会很痛苦,并且缺少适当的文档资料并不能使工作变得更轻松。 但是我对您有个好消息,黑暗的SOAP王国里有一片光明。 好吧,实际上WSDL本身就是其中之一。 尽管它很笨重,而且有些丑陋,但它具有一定的优势。 WSDL格式的过多性使得生成客户端(以及服务器)代码非常容易,这可能不是人类的,而是绝对是自动化系统的。

Even compared to modern API specifications, it could actually stay on par with OpenAPI or fancy Swagger API concepts where everything is described in a language-agnostic spec. This enables huge opportunities for interoperability between different platforms and languages, down to the level of implementation. For example, if one exposes let’s say a .NET web service with WSDL spec, another could automatically generate a JVM-based client to connect to it with little to no pain of data formats conversion or incompatibility.

即使与现代API规范相比,它实际上也可以与OpenAPI或花哨的Swagger API概念保持一致,后者在与语言无关的规范中描述了所有内容。 这就为实现不同平台和语言之间的互操作性提供了巨大的机会。 例如,如果一个人公开了具有WSDL规范的.NET Web服务,另一个人可以自动生成一个基于JVM的客户端以连接到它,而几乎不用担心数据格式转换或不兼容的麻烦。

WSDL导入魔术 (WSDL Import Magic)

Let’s spin it further and talk about automated code generation. You might be surprised, but most enterprise-ish platforms, mainly Java and .NET, come with WSDL code generating tools out of the box. For example, there is wsimport that comes as a part of a JDK distribution. Such tools are quite powerful and should cover an auto-generation task end to end. The only remaining part is to connect your business logic to the client code and make use of it.

让我们进一步讨论自动代码生成。 您可能会感到惊讶,但是大多数企业级平台(主要是Java和.NET)都开箱即用了WSDL代码生成工具。 例如,在JDK发行版中有wsimport 。 这样的工具功能非常强大,应涵盖端到端的自动生成任务。 剩下的唯一部分是将业务逻辑连接到客户端代码并加以利用。

So, since we are on the Scala theme currently, let’s look deeper into Java’s wsimport tool:

因此,由于当前处于Scala主题,让我们更深入地研究Java的wsimport工具:

wsimport -p stockquote http://stockquote.example.com/quote?wsdl

The command takes a WSDL schema as a required parameter, and basically it’s just enough to produce a whole set of POJOs and interfaces, that are marked with all proper annotations. The latter ones actually do the trick: this is essentially what makes all things possible. When executed, JVM wires your client code together with internal web service client implementation, that comes out of the box, so you don’t need to bother much about low-level networking & IO. Rest of the business is to handle ins and outs properly, and be careful with errors and exceptions.

该命令将WSDL模式作为必需参数,基本上,这足以产生一整套POJO和接口,并用所有适当的注释进行标记。 后者实际上可以解决问题:从本质上讲,这使所有事情成为可能。 当执行时,JVM将您的客户端代码与内部Web服务客户端实现连接在一起,这是开箱即用的,因此您无需为低级网络和IO操心。 剩下的工作就是正确处理来龙去脉,并注意错误和异常。

借助SBT将自动化提升到一个新的水平 (Bring Automation to the Next Level with SBT)

Alright, it’s time for some hands-on. Imagine we have some SOAP web services that we need to connect too, and they expose WSDL. I deliberately took some for testing, for the sake of science & education only, of course. Run the code generator:

好了,是时候动手了。 想象一下,我们也需要连接一些SOAP Web服务,并且它们公开了WSDL。 我当然是出于科学和教育目的,故意带了一些测试。 运行代码生成器:

wsimport -s ../src/main/java -extension -p your.package.wsdl.nl \
  -XadditionalHeaders -Xnocompile \
  http://webservices.oorsprong.org/websamples.countryinfo/CountryInfoService.wso?WSDL

It produces a number of raw Java code in the output folder. We could proceed with connecting our business logic, as suggested above. But wait a second, what if the server side changes — we will be aware of it only at the moment of actual code execution (or on the integration tests failure moment, if we have some). Not pretty. It quickly gets not pretty at all if you will think of committing all this boilerplate Java bean code into your pristine Scala repository.

它在输出文件夹中生成许多原始Java代码。 如上所述,我们可以继续连接业务逻辑。 但是请稍等一下,如果服务器端发生了变化,我们将只在实际代码执行时(或在集成测试失败时,如果有的话)才意识到这一点。 不漂亮。 如果您考虑将所有这些样板Java Bean代码提交到原始的Scala存储库中,它很快就会变得很不漂亮。

Of course, it would be way nicer to generate all that stuff automatically and keep things lean & clean. A first step for this would be to automate getting all WSDL classes with one command and make a Shell script out of this. I actually made one for you so you can have a look: wsdl_import.sh.

当然,自动生成所有内容并保持内容简洁明了会更好。 第一步是使用一个命令自动获取所有WSDL类,并从中制作Shell脚本。 我实际上为您制作了一个,以便您查看: wsdl_import.sh

Then we could just wrap it with a build task: let’s take SBT as an example, since we are on Scala, so something like this should work:

然后,我们可以将其与构建任务一起包装:让我们以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.")
}

Source

资源

Now, we need to make sure we have all this code before Scala part compiles, for obvious reasons. Easy-peasy, we have SBT so we just need to execute the Shell script as an SBT task like above and run things in the right order, correct? Well, it’s a bit more complicated in real life. Without getting into much of the details of how SBT works, things get much easier if we separate this WSDL-Java part into a self-containing sub-project, and make a proper dependency in the master SBT configuration.

现在,出于明显的原因,我们需要确保在Scala部件编译之前拥有所有这些代码。 轻而易举,我们有SBT,所以我们只需要像上面的SBT任务一样执行Shell脚本,并以正确的顺序运行事情,对吗? 好吧,在现实生活中它有点复杂。 如果不深入探讨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)

Source

资源

When you compile the master project, SBT first ensures the sub-project is already compiled. But there is a catch: when you have just checked out your repository, you may not have executed the compilation. So when you first open it in the editor, some of the dependencies will be missing, of course. Hopefully, the only thing you need is to run an sbt compilecommand and, perhaps, refresh the project in IDE.

编译主项目时,SBT首先确保子项目已被编译。 但是有一个陷阱:刚签出存储库时,可能未执行编译。 因此,当您第一次在编辑器中打开它时,当然会缺少某些依赖项。 希望,您唯一需要做的就是运行sbt compilecommand并可能在IDE中刷新项目。

There might be another caveat if you are running your Scala application as a stand-alone client or in a lean web container (e.g. Netty if you are using Play Framework). In this case, it’s very likely the application runtime will be missing the implementation bit that helps JVM to do the SOAP magic for you, thanks to modern JRE versions and Java Jigsaw project. No need to panic though, just add a few libraries to your dependency list, or throw a single rt.jar from your JRE distribution as an unmanaged dependency:

如果您将Scala应用程序作为独立客户端或在精益Web容器中运行(例如,如果使用的是Play框架,则为Netty),则可能需要另外警告。 在这种情况下,由于现代的JRE版本和Java Jigsaw项目,应用程序运行时很可能会缺少帮助JVM为您完成SOAP魔术的实现位。 不过,您不必担心,只需将一些库添加到您的依赖项列表中,或将JRT分发中的一个rt.jar作为非托管依赖项抛出即可:

unmanagedJars in Test += Attributed.blank(
      file(System.getenv("JAVA_HOME") + "/jre/lib")
    )

作为结论 (As a Conclusion)

Alright, as a recap: we have learnt a bit about SOAP and WSDL and hopefully realised it’s not such a nightmare to work with, thanks to all these code generators and excessive WSDL spec. We also figured out how to automate a dirty job and found a way to keep our repositories pristine and clean from unwanted boilerplate code. It took some knowledge of SBT to configure compilation order & dependencies properly, but after all, it should work quite smoothly. To simplify things further, I made a small bootstrap template that should help you kick-start a project next time: https://github.com/sainnr/sbt-scala-wsdl-template. Hope you enjoyed this little back-to-the-past journey!

好的,回顾一下:由于所有这些代码生成器和过多的WSDL规范,我们已经了解了一些有关SOAP和WSDL的知识,并希望意识到使用它并不是一场噩梦。 我们还想出了如何自动执行肮脏的工作,并找到了一种方法,可以使我们的存储库保持原始状态,并清除不需要的样板代码。 正确配置编译顺序和依赖项需要一些SBT知识,但是毕竟它应该运行得很顺利。 为了进一步简化操作,我制作了一个小的引导程序模板,该模板可以帮助您下​​次启动项目: https : //github.com/sainnr/sbt-scala-wsdl-template 。 希望您喜欢这个小小的过去之旅!

参考文献 (References)

Please drop me a message if you see any typos or mistakes.

如果您发现任何错别字或错误,请给我留言。

This article was originally published in my blog fullstackme.co.uk with little modifications.

这篇文章最初发表在我的博客fullstackme.co.uk中 ,几乎没有修改。

翻译自: https://habr.com/en/post/470349/

scala和sbt

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值