Commits

taka2ru committed 0c5560a

initial import

Comments (0)

Files changed (4)

+# clj-soap
+
+clj-soap is SOAP server and client using Apache Axis2.
+
+## Usage
+
+### Client
+
+You can call remote SOAP method as following:
+
+  (require '(clj-soap (core :as soap)))
+  (let [client (soap/client-fn "http://... (URL for WSDL)")]
+    (client :someMethod param1 param2 ...))
+
+### Server
+
+To make SOAP service:
+
+  (require '(clj-soap (core :as soap)))
+
+  ;; Defining service class
+  (soap/defservice my.some.SoapClass
+    ^String (someMethod [^Integer x ^String s]
+              (str "x is " x "\ts is " s)))
+
+  ;; Start SOAP Service
+  (serve "my.some.SoapClass")
+
+`defservice` needs to be AOT-compiled.
+For example, `lein compile` before running server.
+
+#### Type Hint
+
+SOAP services need typehints.
+`String` for arguments nad `void` for return value,
+if you don't specify typehints.
+
+## License
+
+Copyright (C) 2011 Tetsuya Takatsuru
+
+Distributed under the Eclipse Public License, the same as Clojure.
+
+(defproject clj-soap "1.0.0-SNAPSHOT"
+  :description "FIXME: write description"
+  :dependencies [[org.clojure/clojure "1.2.1"]
+                 [org.clojure/clojure-contrib "1.2.0"]
+                 [org.apache.axis2/axis2-adb "1.6.0"]
+                 [org.apache.axis2/axis2-transport-http "1.6.0"]
+                 [org.apache.axis2/axis2-transport-local "1.6.0"]]
+  :dev-dependencies [[vimclojure/server "2.2.0"]
+                     [org.clojars.autre/lein-vimclojure "1.0.0"]]
+  :aot [clj-soap.test.core])
+

src/clj_soap/core.clj

+(ns clj-soap.core
+  (:use [clojure.contrib.core]))
+
+;;; Defining SOAP Server
+
+(defmacro defservice
+  "Define SOAP class.
+  i.e. (defsoap some.package.KlassName (myfunc [String a int b] String (str a (* b b))))"
+  [class-name & method-defs]
+  (let [prefix (str (gensym "prefix"))]
+    `(do
+       (gen-class
+         :name ~class-name
+         :prefix ~prefix
+         :methods ~(vec (for [method-def method-defs]
+                          (let [[method-name arglist & _] method-def]
+                            [method-name
+                             (vec (for [arg arglist]
+                                    (or (:tag (meta arg)) String)))
+                             (or (:tag (meta method-def)) 'void)]))))
+       ~@(for [[method-name arglist & body] method-defs]
+           `(defn ~(symbol (str prefix method-name))
+              ~(vec (cons 'this arglist))
+              ~@body)))))
+
+(defn serve
+  "Start SOAP server.
+  argument classes is list of strings of classnames."
+  [& classes]
+  (let [server (org.apache.axis2.engine.AxisServer.)]
+    (doseq [c classes]
+      (.deployService server (str c)))))
+
+;; Client call
+
+(defn axis-service-namespace [axis-service]
+  (.get (.getNamespaceMap axis-service) "ns"))
+
+(defn axis-service-operations [axis-service]
+  (iterator-seq (.getOperations axis-service)))
+
+(defn axis-op-name [axis-op]
+  (.getLocalPart (.getName axis-op)))
+
+(defn axis-op-namespace [axis-op]
+  (.getNamespaceURI (.getName axis-op)))
+
+(defn axis-op-args [axis-op]
+  (for [elem (-?> (first (filter #(= "out" (.getDirection %))
+                                 (iterator-seq (.getMessages axis-op))))
+                  .getSchemaElement .getSchemaType
+                  .getParticle .getItems .getIterator iterator-seq)]
+    {:name (.getName elem) :type (-?> elem .getSchemaType .getName keyword)}))
+
+(defn axis-op-rettype [axis-op]
+  (-?> (first (filter #(= "in" (.getDirection %))
+                      (iterator-seq (.getMessages axis-op))))
+       .getSchemaElement .getSchemaType .getParticle .getItems .getIterator
+       iterator-seq first
+       .getSchemaType .getName
+       keyword))
+
+(defn value-str [valobj argtype]
+  (case argtype
+    :integer (str valobj)
+    :double (str valobj)
+    :string (str valobj)
+    :anyType (str valobj)
+    :boolean (str valobj))) 
+
+(defn get-result [op retelem]
+  (let [ret-str (.getText (first (iterator-seq (.getChildElements retelem))))]
+    (case (axis-op-rettype op)
+      :integer (Integer/parseInt ret-str)
+      :double (Double/parseDouble ret-str)
+      :string ret-str
+      :anyType ret-str
+      :boolean (Boolean/parseBoolean ret-str))))
+
+(defn make-client [url]
+  (doto (org.apache.axis2.client.ServiceClient. nil (java.net.URL. url) nil nil)
+    (.setOptions
+      (doto (org.apache.axis2.client.Options.)
+        (.setTo (org.apache.axis2.addressing.EndpointReference. url))))))
+
+(defn make-request [op & args]
+  (let [factory (org.apache.axiom.om.OMAbstractFactory/getOMFactory)
+        request (.createOMElement
+                  factory (javax.xml.namespace.QName.
+                            (axis-op-namespace op) (axis-op-name op)))
+        op-args (axis-op-args op)]
+    (doseq [[argval argtype] (map list args op-args)]
+      (.addChild request
+                 (doto (.createOMElement
+                         factory (javax.xml.namespace.QName. (:name argtype)))
+                   (.setText (value-str argval (:type argtype))))))
+    request))
+
+(defn client-call [client op & args]
+  (if (isa? (class op) org.apache.axis2.description.OutOnlyAxisOperation)
+    (.sendRobust client (.getName op) (apply make-request op args))
+    (get-result
+      op (.sendReceive client (.getName op) (apply make-request op args)))))
+
+(defn client-proxy [url]
+  (let [client (make-client url)]
+    (->> (for [op (axis-service-operations (.getAxisService client))]
+               [(keyword (axis-op-name op))
+                (fn soap-call [& args] (apply client-call client op args))])
+      (into {}))))
+
+(defn client-fn [url]
+  (let [px (client-proxy url)]
+    (fn [opname & args]
+      (apply (px opname) args))))
+

test/clj_soap/test/core.clj

+(ns clj-soap.test.core
+  (:use [clj-soap.core])
+  (:use [clojure.test]))
+
+(def test-value (ref false))
+
+(defservice jp.myclass.MyApp
+  (changeval [^String string] (dosync (ref-set test-value string)))
+  ^Double (hypotenuse [^Double x ^Double y] (Math/sqrt (+ (* x x) (* y y)))))
+
+(deftest test-my-app
+  (serve "jp.myclass.MyApp")
+  (let [cl (client-fn "http://localhost:6060/axis2/services/MyApp?wsdl")]
+    (is (= 5.0 (cl :hypotenuse 3 4)) "SOAP call with return value")
+    (cl :changeval "piyopiyo")
+    (is (= "piyopiyo" @test-value) "SOAP call withoug return value")))
+