Search code examples
soapclojurejax-ws

Clojure SOAP server JAX-WS


I am trying to do a simple SOAP webserver with Clojure and jax-ws

(ns test-ws.routes.home
(:import (javax.jws WebMethod WebService)
       (javax.xml.ws Endpoint)))

(defprotocol Calculator (add [this a b]))

(deftype ^{WebService {:targetNamespace "test.seltzer" }}
     CalcWeb [] Calculator (^{WebMethod []} add [this a b] (+ a b)))

(def endpoint (Endpoint/publish "http://localhost:8080/calcWeb" (CalcWeb.)))

After that WSDL is available at http://localhost:8080/calcWeb?wsdl but when I am trying to call it from SOAP-UI I got: com.sun.org.apache.xerces.internal.dom.ElementNSImpl cannot be cast to java.lang.Number

My idea was that clojure cannot map SOAP request to number so I added annotation

(defprotocol Calculator (add [this ^Integer a ^Integer b]))
(deftype ^{WebService {:targetNamespace "test.seltzer" }}
     CalcWeb [] Calculator (^{WebMethod []} add [this ^Integer a ^Integer b] (+ a b)))

Now on compile time I have the following error: Can't find matching method: add, leave off hints for auto match.

I also tried this

(defprotocol Calculator (add [this ^Integer a ^Integer b]))

(deftype ^{WebService {:targetNamespace "test-ws.routes.home"}}
     CalcWeb [] Calculator (^{WebMethod []} add [this
                               #^{WebParam {:name "param0"} :tag Integer} a
                               #^{WebParam {:name "param1"} :tag Integer} b] (+ a b)))

I gave me same error.

Am I missing something ?


Solution

  • I really was close to solve the problem. The trick was to use interface instead of protocol. I hope that this will be useful for some one:

    (ns wstest.core
      (:import (javax.jws WebMethod WebService WebParam)
           (javax.xml.ws Endpoint)
           (wstest.java Person)))
    
    (definterface IFooService
      (^int add [^int a ^int b])
      (^String sayHi [^String name])
      (^wstest.java.Person makePerson [^String firstName ^String lastName])
      (^String sayHiTo [^wstest.java.Person person]))
    
    (deftype ^{WebService {:targetNamespace "o.m.g"}} FooService []
      IFooService
      (^int ^{WebMethod []} add [this ^int #^{WebParam {:name "a"}} a ^int #^{WebParam {:name "b"}} b]
        (int (+ a b)))
      (^String ^{WebMethod []} sayHi [this ^String #^{WebParam {:name "name"}} name]
        (str "Hello, " name "!"))
      (^wstest.java.Person ^{WebMethod []} makePerson [this ^String #^{WebParam {:name "firstName"}} firstName ^String #^{WebParam {:name "lastName"}} lastName]
        (doto (wstest.java.Person.)
          (.setFirstName firstName)
          (.setLastName lastName)))
      (^String ^{WebMethod []} sayHiTo [this ^wstest.java.Person #^{WebParam {:name "person"}} person]
        (str "Hello, " (.getFirstName person) " " (.getLastName person) "!")))
    
    (def endpoint (atom nil))
    
    (do
      (if-not (nil? @endpoint)
        (.stop @endpoint))
      (reset! endpoint (Endpoint/publish "http://localhost:8080/fooService" (FooService.))))
    

    Person.java:

    package wstest.java;
    
    import javax.xml.bind.annotation.XmlAccessType;
    import javax.xml.bind.annotation.XmlAccessorType;
    import javax.xml.bind.annotation.XmlElement;
    import javax.xml.bind.annotation.XmlType;
    
    @XmlAccessorType(XmlAccessType.FIELD)
     @XmlType(name = "Person")
     public class Person {
    @XmlElement
    protected String firstName;
    @XmlElement
    protected String lastName;
    
    public String getFirstName() {
        return firstName;
    }
    
    public void setFirstName(String value) {
        this.firstName = value;
    }
    
    public String getLastName() {
        return lastName;
    }
    
    public void setLastName(String value) {
        this.lastName = value;
    }
    }