Fork me on GitHub
#clojure
<
2021-12-16
>
jsyrjala12:12:56

How to do SOAP clients with Clojure these days? There is a WSDL file and based on that a client is needed that calls some operations. Previously we have generated java classes with Java8 tools and used those classes from clojure: `wsimport -Xnocompile -p com.foo api.wsdl`

Tomas Brejla13:12:18

I had to touch SOAP in a java project recently and the tech stack was pretty much the same as it used to be a decade(s?) ago. No simplification of the process, no fancy new easier-to-use libraries, same old pain-in-the-...-to-set-up-correctly maven plugins. So I was curious to try to call the same endpoint from clojure, just to see if it's possible. Gave https://github.com/xapix-io/paos (+ clj-http.client) a try and <15 minutes and 57 LOC later, I was able to call the same SOAP web service from clojure. Including auth via certificate, custom headers etc. No weird magic hidden accross tens of layers of bloated abstractions, no cr*p that gets silently preinitialized (ie. with global hidden state) using configuration from META-INF/services (yay! fun with issues with classloaders!), no unnecessary zombie-dead technologies in the tech stack etc. Maybe I'm just too grumpy when it comes to SOAP/XML techs in java, but I'll always prefer simple solution (if it's working well enough) such as the one that PAOS uses.

Tomas Brejla13:12:34

But I have no idea how stable/maintained the paos library is. Better check there first and be sure to test the solution well before relying on it "in prod".

azimpel13:12:43

I've successfully generated POJOs from a WS description (WSDL, XSD) using wsimport and then implemented a thin interop layer around it using the https://github.com/clojure/java.data library. No additional stuff, only JAX-WS.

potetm14:12:10

The java.data thing is an interesting trick.

potetm14:12:58

I would really like a library that took a wsdl+soap-payload and returned a regular clojure data structure, and vice versa.

potetm14:12:17

so that we don’t have to go through the intermediate of a generated java class

Tomas Brejla14:12:47

The question is whether there's any significant benefit in having to deal with POJOs at all. I mean.. you get HTTP(s) response (XML), spend time turning that XML into typed POJOs.. and all that just so that you can turn those POJOs into generic clojure structures..

potetm14:12:21

No, that’s my whole point.

potetm14:12:50

There’s not any benefit except that the path through pojos exist and the direct xml->clj does not.

potetm14:12:17

It’s a major pain to deal with direct XML.

potetm14:12:55

Every time you want to extract a single data point, you’ve got to recurse/zip/select through a massive data structure.

potetm14:12:30

Well, to be a little more clear, you can’t just do xml->clj. You need the wsdl.

Tomas Brejla14:12:57

> library that took a wsdl+soap-payload and returned a regular clojure data structure, and vice versa. I'd look into that paos, it might either fit that need or give some inspiration at least. It'll probably be lacking more advanced features (such as ws-security etc.), but for many simple scenarios, it might do the job just fine.

potetm14:12:21

idk it still looks like too much stuff

potetm14:12:38

plus it appears to be doing the same xml->pojo->clojure trick

potetm14:12:34

could be wrong on that last count

potetm14:12:23

yeah I mean, it’s basically a wrapper around reficio/soap-ws

potetm14:12:02

every time I think about this, I consider writing one myself

seancorfield18:12:27

Years ago I needed to work with a SOAP API from Clojure and I tried a bunch of things, including trying to get clj-soap working on a recent version of Clojure, and I finally gave up and went back to wsdl2java, compile those classes, and use them from Clojure. Ugly, but straightforward.

seancorfield18:12:59

Since then we did do some stuff with Apache Axis (1.x!) and interop for an external service, but I don't remember the details and we no longer use whatever that service was. Axis via Clojure wasn't as horrible as I'd expected (but it wasn't great either).

rmxm14:12:44

Just a casual reflection. I really do like how S-expressions are atomic within context, rather than part of larger block (like file). I used to dislike the fact I have to solve dependencies within same file (defn x [] 2) (defn y [] (x)) where x has to be defined prior to y. At first its very annoying (as it seems something compiler/vm solves in file-oriented languages). Now in every language I go, I do it (but sadly dont get warnings/errors) just because it brings (most often) a very natural order. Its way better than random order, very often better than arbitrary order.

mkvlr14:12:55

there’s a lot less in here than I would expect (using org.clojure/java.classpath {:mvn/version "1.0.0"} ). Anybody know what could cause this?

(clojure.java.classpath/classpath)
;;=> (#object[java.io.File 0x6fbcdeaa "/Library/Java/JavaVirtualMachines/jdk-17.0.1.jdk/Contents/Home/lib/src.zip"])

vemv14:12:58

are you running it through cider/cider-nrepl by any chance?

mkvlr14:12:12

let me try without

vemv14:12:54

it's that

mkvlr14:12:18

ok yes, works without cider

mkvlr14:12:36

any idea what I can do to make it work with cider?

vemv14:12:50

https://github.com/clojure-emacs/orchard/tree/32459ccd2258f827f50d370f42b392c401459a35#configuration-options If you have it easy to -Dorchard.use-dynapath=false , I recommend that OR if you're an Emacs user, use cider master snapshot, enjoy the completely removed problematic software and some tasty goodies :) See https://clojurians.slack.com/archives/C0617A8PQ/p1639645952337100

mkvlr14:12:33

nice thanks

mkvlr14:12:52

can I also set this programmatically? I need this in Clerk to work correctly and it’s hard to enforce a specific version or set a system prop for the users of the lib

vemv14:12:44

no, you can't alter the underlying classloader stack But there's an alternative to

(clojure.java.classpath/classpath)
that isn't sensitive to these intricacies, namely (classpath/system-classpath)

mkvlr14:12:51

cool, that works, thanks a lot!

✌️ 1