|
|||||||||||||
|
|
|
|
|
|
|
|
|
||||||
|
|
|||||||||||||
![]() ![]() ![]() ![]() ![]() ![]() |
WSDL Support in Allegro CL/SOAP (added 6/14/04)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. Using WSDL to Access a Web Service with the ACL SOAP Module1. The ApplicationThis 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 2. The Lisp ServerWe 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). 3. The WSDL DefinitionA 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.) 4. A Generated ClientA 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):
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. To access the service.We assume the server started in the example above is still running.
See soap.htm for more details on Allegro CL/SOAP and WSDL generation. Return to Tech Corner page. 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)))
© 2008 Franz Inc - Privacy Statement |