A WSDL file defines a web service in a formal language that can be interpreted by a client computer to generate (automatically) the correct software interface to the web service. The syntax of WSDL is complex, obscure, and often non-intuitive; and a WSDL definition, to be useful, must be complete, accurate, and unambiguous. By generating the WSDL definition of a web service from the Lisp server definition, we relieve the programmer of a redundant, tedious, and error-prone task.
This application provides a Web Service that allows clients to access Lisp bignum arithmetic.
We define methods named calculate, decodeNum, and encodeNum:
Method calculate Input: opname -> "+" "-" "*" "ash" "truncate" "ceiling" "factorial" "rem" "gcd" "expt" num1 -> a string of digits num2 -> a string of digits Output: calcResult -> a string of digits Method decodeNum Input: num -> a string of digits base -> an integer Output: decResult -> an array of integers example: num: "12345678901234567890" base: 10000 output: 123 4567 8901 2345 6789 Method encodeNum Input: bigits -> an array of integers base -> an integer Output: encResult -> a string of digits
We define a Lisp SOAP server that implements the application. The Lisp code for the Web Service is in file bignum-server.cl.
To try the code examples, copy this file to the folder where Allegro CL will be started and compile and load the file with the :cload command. We assume this server is running in the examples below.
For simplicity, we define a very tolerant server that allows any namespace (or no namespace at all) on application tag names.
In a future Tech Corner entry we will delve into the namespace issue.
We start the server with the call (start-server). The default server advertises on host "localhost" at port 1776. Optional port and host arguments to start-server can be specified.
We can try the server in the same Lisp image with (try-server).
A WSDL definition of the Web Service allows remote applications to access the service automatically. Since the server has already been started in the Lisp image, we create a WSDL definiton file with
(encode-wsdl-file "bignum-server.wsdl" :servers *server*)
The result of the above call is a file bignum-server.wsdl. The file contains an XML description of the Web Service. We include the file sample-bignum-server.wsdl that shows the generated WSDL definition. (The file is for illustration only.)
A Lisp application can generate a client interface to the Web Service, just like a Java or C++ application could access the Lisp Web Service with the help of the same WSDL definition. We include the file sample-bignum-client.cl that shows the generated Lisp client code.
The following commands will actually generate the client file (we use two images, which is easier; it can be done in one image):
(require :soap) (use-package :net.xmp.soap) (decode-wsdl-namespaces :file "bignum-server.wsdl") (setf *wsdl* (decode-wsdl-file "bignum-server.wsdl")) (make-client-interface *wsdl* 0 "bignum-client.cl")
The result is a file bignum-client.cl that contains the Lisp code necessary to access the Web Service. A comment at the beginning of the file shows a summary description of the methods available in the service.
We assume the server started in the example above is still running.
(require :soap) :ld bignum-client ... (client-1 :|opname| "ash" :|num1| "1" :|num2| "500") ... (client-5 :|num| "1234567890" :|base| 100) ... (client-3 :|bigits| '(12 34 56 78 90) :|base| 100) ...
See soap.htm for more details on Allegro CL/SOAP and WSDL generation.
The file bignum-server.cl. To download this file, click here.
;; copyright (c) 2002-2004 Franz Inc, Oakland, CA - All rights reserved. ;; ;; The software, data and information contained herein are proprietary ;; to, and comprise valuable trade secrets of, Franz, Inc. They are ;; given in confidence by Franz, Inc. pursuant to a written license ;; agreement, and may be stored and used only in accordance with the terms ;; of such license. ;; ;; Restricted Rights Legend ;; ------------------------ ;; Use, duplication, and disclosure of the software, data and information ;; contained herein by any agency, department or entity of the U.S. ;; Government are subject to restrictions of Restricted Rights for ;; Commercial Software developed at private expense as specified in ;; DOD FAR Supplement 52.227-7013 (c) (1) (ii), as applicable. ;; ;; The file bignum-server.cl. An example file associated with ;; the tech corner entry 'The WSDL generation facility in Allegro ;; CL/SOAP API (added 6/14/04)'. (in-package :user) (eval-when (compile load eval) ;; This form is needed to make the SOAP module available (require :soap) ;; This form is to let us use unqualified names (use-package :net.xmp.soap) ) ;; This form is to allow a short package prefix for Schema symbols (defpackage :net.xmp.schema (:use) (:nicknames :xsd)) ;; A Lisp function to translate ;; a string to a function name symbol ;; a second string to an arbitrarily large integer ;; a third string to a second integer ;; The result is a string of digits representing the answer. (defun calculate (op arg1 arg2) (let* ((opsym (read-from-string (format nil "~A" op))) (num1 (parse-integer arg1)) (num2 (parse-integer arg2))) (case opsym ((+ - * ash truncate ceiling expt factorial gcd rem) (format nil "~A" (funcall opsym num1 num2))) (otherwise (error "Unknown operation")) ))) ;; A Lisp function to translate a string of digits into a list ;; of digits in some arbitrary base. ;; This function allows a foreign language caller to view the ;; large integer as a vector of integers within the integer ;; range of the foreign language. (defun decode-num (string base) (let ((num (parse-integer string)) rem res) (loop (multiple-value-setq (num rem) (truncate num base)) (push rem res) (when (zerop num) (return))) res)) ;; A Lisp function to translate a sequence of digits into ;; a string representing the actual number. (defun encode-num (seq base &aux (res 0)) (dotimes (i (length seq) (format nil "~A" res)) (setf res (+ (* res base) (elt seq i))))) (defun factorial (n dummy &aux (r 1)) (declare (ignore dummy)) (loop (when (< n 2) (return r)) (setf r (* r n)) (decf n))) (define-soap-element nil "calculate" '(:complex (:seq (:element "opname" xsd:|string|) (:element "num1" xsd:|string|) (:element "num2" xsd:|string|)) :action "calculate" )) (define-soap-element nil "calculateResponse" `(:complex (:seq (:element "calcResult" xsd:|string|)))) (define-soap-element nil "decodeNum" '(:complex (:seq (:element "num" xsd:|string|) (:element "base" xsd:|int|)) :action "decodeNum" )) (define-soap-type nil :|arrayOfBigits| '(:array xsd:|int| :array-item (:element "item" :send-type t) )) (define-soap-element nil "decodeNumResponse" '(:complex (:seq (:element "decResult" :|arrayOfBigits|)))) (define-soap-element nil "encodeNum" `(:complex (:seq (:element "bigits" :|arrayOfBigits|) (:element "base" xsd:|int|)) :action "encodeNum" )) (define-soap-element nil "encodeNumResponse" `(:complex (:seq (:element "encResult" xsd:|string|)))) (defvar *server* nil) (defun start-server (&optional (port 1776) (host "localhost")) (let ((server (soap-message-server :start (list :port port) :lisp-package :keyword :message-dns *wsdl-1.1-namespaces* :url (format nil "http://~A:~A/SOAP" host port) :service-name "BigNumService" ))) (soap-export-method server "calculate" '("opname" "num1" "num2") :lisp-name 'calculate-method :return "calculateResponse" :action "calculate" ) (soap-export-method server "decodeNum" '("num" "base") :lisp-name 'decode-num-method :return "decodeNumResponse" :action "decodeNum" ) (soap-export-method server "encodeNum" '("bigits" "base") :lisp-name 'encode-num-method :return "encodeNumResponse" :action "encodeNum" ) (setf *server* server))) (defun calculate-method (&key |opname| |num1| |num2|) (list "calcResult" (calculate |opname| |num1| |num2|))) (defun decode-num-method (&key |num| |base|) (list "decResult" (decode-num |num| |base|))) (defun encode-num-method (&key |bigits| |base|) (list "encResult" (encode-num |bigits| |base|))) (defun try-server (&optional (port 1776) (host "localhost")) (let ((client (soap-message-client :url (format nil "http://~A:~A/SOAP" host port) ))) (values (call-soap-method client "calculate" "opname" "factorial" "num1" "17" "num2" "1") client)))
The file sample-bignum-client.cl. A file similar to this one is generated in the above example.
(in-package #:common-lisp-user) #| Defined functions: client-5 Send client message decodeNum Message "decodeNum" takes 2 arguments: The element "num" of type net.xmp.schema:string The element "base" of type net.xmp.schema:int and returns a result decodeNumResponse containing the element(s): The element "decResult" of type net.xmp.wsdl::arrayOfBigits The type net.xmp.wsdl::arrayOfBigits is array of net.xmp.schema:int client-3 Send client message encodeNum Message "encodeNum" takes 2 arguments: The element "bigits" of type net.xmp.wsdl::arrayOfBigits The type net.xmp.wsdl::arrayOfBigits is array of net.xmp.schema:int The element "base" of type net.xmp.schema:int and returns a result encodeNumResponse containing the element(s): The element "encResult" of type net.xmp.schema:string client-1 Send client message calculate Message "calculate" takes 3 arguments: The element "opname" of type net.xmp.schema:string The element "num1" of type net.xmp.schema:string The element "num2" of type net.xmp.schema:string and returns a result calculateResponse containing the element(s): The element "calcResult" of type net.xmp.schema:string Defined SOAP elements: "decodeNumResponse" "decodeNum" "encodeNumResponse" "encodeNum" "calculateResponse" "calculate" Defined SOAP types: net.xmp.wsdl::arrayOfBigits Defined parameters: *application-namespaces* Defined packages: :net.xmp.schema :net.xmp.schema-instance :net.xmp.soap.envelope :net.xmp.soap.encoding :net.xmp.wsdl.soap :net.xmp.wsdl #:common-lisp-user Lisp package of generated file: #:common-lisp-user |# (defpackage #:common-lisp-user (:use #:net.xmp.soap #:common-lisp #:excl)) (defpackage :net.xmp.wsdl (:use)) (defpackage :net.xmp.wsdl.soap (:use)) (defpackage :net.xmp.soap.encoding (:use)) (defpackage :net.xmp.soap.envelope (:use)) (defpackage :net.xmp.schema-instance (:use)) (defpackage :net.xmp.schema (:use)) (defparameter *application-namespaces* (quote (nil (:net.xmp.wsdl "wsdl" "http://schemas.xmlsoap.org/wsdl/") (:net.xmp.wsdl.soap "soap" "http://schemas.xmlsoap.org/wsdl/soap/") (:net.xmp.soap.encoding "soapenc" "http://schemas.xmlsoap.org/soap/encoding/") (:net.xmp.soap.envelope "soapenv" "http://schemas.xmlsoap.org/soap/envelope/") (:net.xmp.schema-instance "xsi" "http://www.w3.org/2000/10/XMLSchema-instance") (:net.xmp.schema "xsd" "http://www.w3.org/2000/10/XMLSchema")))) (define-soap-type nil 'net.xmp.wsdl::arrayOfBigits '(:array net.xmp.schema:int)) (define-soap-element nil '"calculate" '(:complex (:seq1 (:element "opname" net.xmp.schema:string) (:element "num1" net.xmp.schema:string) (:element "num2" net.xmp.schema:string)) :namespaces (nil (nil "tns" "ThisWebServiceNamespace")) :encoding "http://schemas.xmlsoap.org/soap/encoding/" :action "calculate")) (define-soap-element nil '"calculateResponse" '(:complex (:seq1 (:element "calcResult" net.xmp.schema:string)) :namespaces (nil (nil "tns" "ThisWebServiceNamespace")) :encoding "http://schemas.xmlsoap.org/soap/encoding/")) ;; Send client message calculate ;; calculate (defun client-1 (&rest args &key opname num1 num2) (declare (ignore opname num1 num2)) (let ((conn (soap-message-client :url "http://localhost:1776/SOAP" :message-dns *application-namespaces* :lisp-package :keyword))) (values (apply 'call-soap-method conn '"calculate" args) conn))) (define-soap-element nil '"encodeNum" '(:complex (:seq1 (:element "bigits" net.xmp.wsdl::arrayOfBigits) (:element "base" net.xmp.schema:int)) :namespaces (nil (nil "tns" "ThisWebServiceNamespace")) :encoding "http://schemas.xmlsoap.org/soap/encoding/" :action "encodeNum")) (define-soap-element nil '"encodeNumResponse" '(:complex (:seq1 (:element "encResult" net.xmp.schema:string)) :namespaces (nil (nil "tns" "ThisWebServiceNamespace")) :encoding "http://schemas.xmlsoap.org/soap/encoding/")) ;; Send client message encodeNum ;; encodeNum (defun client-3 (&rest args &key bigits base) (declare (ignore bigits base)) (let ((conn (soap-message-client :url "http://localhost:1776/SOAP" :message-dns *application-namespaces* :lisp-package :keyword))) (values (apply 'call-soap-method conn '"encodeNum" args) conn))) (define-soap-element nil '"decodeNum" '(:complex (:seq1 (:element "num" net.xmp.schema:string) (:element "base" net.xmp.schema:int)) :namespaces (nil (nil "tns" "ThisWebServiceNamespace")) :encoding "http://schemas.xmlsoap.org/soap/encoding/" :action "decodeNum")) (define-soap-element nil '"decodeNumResponse" '(:complex (:seq1 (:element "decResult" net.xmp.wsdl::arrayOfBigits)) :namespaces (nil (nil "tns" "ThisWebServiceNamespace")) :encoding "http://schemas.xmlsoap.org/soap/encoding/")) ;; Send client message decodeNum ;; decodeNum (defun client-5 (&rest args &key num base) (declare (ignore num base)) (let ((conn (soap-message-client :url "http://localhost:1776/SOAP" :message-dns *application-namespaces* :lisp-package :keyword))) (values (apply 'call-soap-method conn '"decodeNum" args) conn)))
Copyright © 2023 Franz Inc., All Rights Reserved | Privacy Statement |