#LyX 1.4.4 created this file. For more info see http://www.lyx.org/ \lyxformat 245 \begin_document \begin_header \textclass article \begin_preamble \usepackage{ae,aecompl} \usepackage{html} \end_preamble \language english \inputencoding auto \fontscheme default \graphics default \paperfontsize default \spacing single \papersize default \use_geometry true \use_amsmath 1 \cite_engine basic \use_bibtopic false \paperorientation portrait \leftmargin 2.5cm \topmargin 2cm \rightmargin 2cm \bottommargin 2cm \secnumdepth 3 \tocdepth 3 \paragraph_separation skip \defskip medskip \quotes_language english \papercolumns 1 \papersides 1 \paperpagestyle default \tracking_changes false \output_changes true \end_header \begin_body \begin_layout Title Interoperable Python ZSI WSDL/SOAP Web Services tutorial \end_layout \begin_layout Author Holger Joukl \newline LBBW Financial Markets Technologies \end_layout \begin_layout Abstract This is the follow-up to \begin_inset Quotes eld \end_inset Interoperable WSDL/SOAP web services introduction: Python ZSI, Excel XP, gSOAP C/C++ & Applix SS \begin_inset Quotes erd \end_inset \begin_inset LatexCommand \cite{InteropHOLGER} \end_inset , with a more explicit focus on Python ZSI Web Services. \newline \newline Now that the \begin_inset Quotes eld \end_inset services/SOA \begin_inset Quotes erd \end_inset hype & buzzword-storm has calmed down, building SOAP web services servers and clients is widely regarded to have become a commodity. While the complexness of the actual protocols/specs remains, toolkit magic is getting more robust so users seldom need to get down to the nitty-gritty any more. Also, toolkit documentation has broadened and matured. We believe however that a) there can never be enough documentation and b) the intention of this document to be a step-by-step tutorial in the sense of a complete walkthrough still fills a gap. \newline It features \end_layout \begin_deeper \begin_layout Itemize as its focus the Python ZSI module (2.0 and 2.1alpha) that is used to build the server side machinery and \end_layout \begin_layout Itemize several clients that access the exposed services from \end_layout \begin_deeper \begin_layout Itemize Python (ZSI 2.0 + 2.1alpha) \end_layout \begin_layout Itemize C/C++ (gSOAP 2.7.9) \end_layout \end_deeper \end_deeper \begin_layout Standard Copyright Đ 2007-2008 Holger Joukl. All rights reserved. \end_layout \begin_layout Standard All trademarks mentioned in this document are the property of their respective owners. \end_layout \begin_layout Standard Redistribution and use in source (LyX, LaTeX) and 'compiled' forms (SGML, HTML, PDF, PostScript, RTF and so forth) with or without modification, are permitted provided that the following conditions are met: \end_layout \begin_layout Enumerate Redistributions of source code (LyX, LaTeX) must retain the above copyright notice, this list of conditions and the following disclaimer as the first lines of this file unmodified. \end_layout \begin_layout Enumerate Redistributions in compiled form (transformed to other DTDs, converted to PDF, PostScript, RTF and other formats) must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentati on and/or other materials provided with the distribution. \end_layout \begin_layout Standard THIS DOCUMENTATION IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS DOCUMENTATIO N, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Standard \backslash pagebreak \end_layout \end_inset \end_layout \begin_layout Standard \begin_inset LatexCommand \tableofcontents{} \end_inset \end_layout \begin_layout Section Introduction \end_layout \begin_layout Standard This is the follow-up to article \begin_inset LatexCommand \cite{InteropHOLGER} \end_inset , mainly prompted by the need to get up-to-date with newer toolkit versions, especially the many changes and improvements in ZSI. While not explicitly stated, ZSI has been the strong focus of this installment from the very beginning, easily noticeable by the fact that all the server code has been Python ZSI-based. Therefore, this document focuses on ZSI and reduces the number of discussed Web Service client implementations in comparison to \begin_inset LatexCommand \cite{InteropHOLGER} \end_inset . \end_layout \begin_layout Standard The toolkit versions discussed here are: \end_layout \begin_layout Itemize ZSI 2.0 + 2.1alpha1 (Python 2.4.4) \end_layout \begin_layout Itemize gSOAP 2.7.9 (gcc 2.95.2, gcc 3.4.4) \end_layout \begin_layout Standard These are being put to use for several example Web Services. \end_layout \begin_layout Standard For legacy reference, the steps to instrument VistaSource Applixware 4.43 with Web Services capabilities (which is probably of minor interest to the average ZSI/gSOAP user) have been updated for the few gSOAP handling changes and still remain available in appendix \begin_inset LatexCommand \ref{sec:VistaSource-Applixware-spreadsheets} \end_inset . \end_layout \begin_layout Standard Appendix \begin_inset LatexCommand \ref{sec:ZSI-hack} \end_inset features a (arguably nifty) way to make ZSI tracing more verbose. \end_layout \begin_layout Subsection Goals & Concepts \end_layout \begin_layout Standard This is a practice-report-gone-tutorial. The content and code examples presented here have been developed with the primary goal to \begin_inset Quotes eld \end_inset make (fairly) simple examples work (at all) \begin_inset Quotes erd \end_inset , in a learning-by-doing manner. Thus there is lots of room for enhancements, e.g. getting rid of hardcoded path names etc. \end_layout \begin_layout Standard All code examples are complete, executable and delivered with all the necessary command-line options to create and/or compile and/or run them. \end_layout \begin_layout Standard Conceptually, we use a WSDL-centric approach, sometimes referred to as top-down development: The starting point for all example service and client implementati ons will be the WSDL description. Note that this might differ from certain toolkits that start out with the service implementation in the host language and generate the WSDL for you to expose the implemented service. We regard the latter to have a tendency to not promote interoperability and to tie in implementation language details, which is certainly not what we want. \begin_inset Foot status open \begin_layout Standard In the first place, this came up partly due to the fact that the chosen server implementation (Python ZSI) offered no such tool and partly as a gut feeling. Since then, this opinion has grown stronger and has also been backed up by several practitionersī readings at a conference (Stuttgarter Softwaretechnik Forum 2005, Stuttgart-Vaihingen, Germany) as well as experience. \end_layout \end_inset \end_layout \begin_layout Standard Striving for interoperability, only the WS-I-compliant \begin_inset Foot status open \begin_layout Standard The \series bold W \series default eb \series bold S \series default ervices- \series bold I \series default nteroperability Organization ( \begin_inset ERT status open \begin_layout Standard \backslash htmladdnormallink{www.ws-i.org}{http://www.ws-i.org} \end_layout \end_inset ) provides so-called \begin_inset Quotes eld \end_inset profile \begin_inset Quotes erd \end_inset guidelines on using Web Services specifications in an interoperable manner. \end_layout \end_inset rpc/literal and document/literal WSDL styles are presented here. \end_layout \begin_layout Standard Throughout this document, certain host names or ports ( \begin_inset Quotes eld \end_inset 8080 \begin_inset Quotes erd \end_inset ) might used in the examples - you will have to substitute those with the appropriate setup for your site, of course. Naturally, this can affect URLs defined in the example WSDLs and used to retrieve these WSDLs. \end_layout \begin_layout Standard Whenever you see sample client or server output throughout this document this is real output copied from live components but might have been reformatted to fit into the document somewhat nicer. \end_layout \begin_layout Subparagraph Acknowledgments: \end_layout \begin_layout Standard Thank you to the ZSI developers for supporting a great product! \end_layout \begin_layout Standard The DateService WSDL (sect. \begin_inset LatexCommand \ref{sub:The-DateService-WSDL} \end_inset ) and worker code (sect. \begin_inset LatexCommand \ref{sub:A-Python-ZSI-DateService-server} \end_inset ) are courtesy of Rainer Kluger (LBBW Financial Markets Technologies). \end_layout \begin_layout Subsection Prerequisites \end_layout \begin_layout Standard We assume the reader is familiar with Python and/or C/C++ to a certain degree. The web service server components are implemented in Python, so to build a working server with the code samples some Python knowledge is needed, but any of the client side technologies can be skipped if not of particular interest. \end_layout \begin_layout Standard While some basic concepts regarding WSDL, SOAP, HTTP servers are presented here implicitly, this document is not a tutorial on these. If you want to know more thereīs plenty of stuff on the web. \end_layout \begin_layout Subsection A glance at the toolkits \end_layout \begin_layout Subsubsection ZSI \end_layout \begin_layout Standard The Python ZSI package \begin_inset LatexCommand \cite{ZSIrefdoc2.0} \end_inset is the actively maintained one of two \begin_inset Quotes eld \end_inset pywebsvcs \begin_inset Quotes erd \end_inset \begin_inset Foot status open \begin_layout Standard \begin_inset ERT status open \begin_layout Standard \backslash htmladdnormallink{http://pywebsvcs.sourceforge.net/}{http://pywebsvcs.sourceforge.ne t/} \end_layout \end_inset \end_layout \begin_layout Standard SourceForge project page: \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Standard \backslash htmladdnormallink{http://sourceforge.net/projects/pywebsvcs}{http://sourceforge.ne t/projects/pywebsvcs} \end_layout \end_inset \end_layout \end_inset packages \begin_inset Foot status open \begin_layout Standard The other being SOAPpy. \end_layout \end_inset implementing web services for Python, namely SOAP 1.1 messaging and WSDL capabilities. It is powerful and easy to get started with. With the arrival of ZSI 2.0 not only have the ZSI developers honed the features and ease-of-use of their fine product but also the documentation has enhanced quite a bit (see especially the ZSI user guide \begin_inset LatexCommand \cite{ZSIuserguide2.0} \end_inset ). As always, thereīs still room for improvement, and we hope to provide some added value by presenting simple, concise examples here. \end_layout \begin_layout Standard At the time of writing, ZSI is in flux again with regard to a planned move to WSGI \begin_inset Foot status open \begin_layout Standard \begin_inset ERT status collapsed \begin_layout Standard \backslash htmladdnormallink{Python Web Server Gateway Interface v1.0}{http://www.python.org/d ev/peps/pep-0333/} \end_layout \end_inset \end_layout \end_inset support, which 2.1alpha1 already gives a notion of, and certain code generation/ naming conventions. For the time being, we rely on \begin_inset Quotes eld \end_inset classic \begin_inset Quotes erd \end_inset ZSI usage here, presenting both 2.0 and 2.1alpha code versions in our examples. All code denoted as ZSI 2.1 refers to 2.1alpha1-compatible code, as of release 2.1alpha1 (2007-11-01). As a side note, ZSI 2.1 gains a massive performance boost from switching to minidom as default parser, removing the dependency on PyXML. \end_layout \begin_layout Subsubsection gSOAP \end_layout \begin_layout Standard gSOAP is an impressive open source web services development toolkit for C/C++. It seems to be very mature and complete and has an extensive record of being used in real-world applications by major companies. One of its key-features is the support for XML-to-C/C++ mapping for native C and C++ data types. It claims to be the fastest SOAP 1.1/1.2 compliant C/C++ implementation out there and comes with good documentation. \end_layout \begin_layout Section Simple datatypes: The rpc/literal SquareService \end_layout \begin_layout Standard This first example will implement an overly simple service that exposes a function which takes a \family typewriter double \family default argument and returns the square of it ( \begin_inset Formula $x{}^{\textrm{2}}$ \end_inset ) as a \family typewriter double \family default . I.e. this examples uses simple scalar datatypes, one single argument and one single return value. \end_layout \begin_layout Subsection The SquareService WSDL \end_layout \begin_layout Standard This is the WSDL file that determines the contract for the SquareService, called \family typewriter SquareService.wsdl \family default : \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code the square method \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code Returns x^2 (x**2, square(x)) for a given float x \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout Standard Comments: \end_layout \begin_layout Itemize The \family typewriter style \family default \begin_inset Quotes eld \end_inset rpc \begin_inset Quotes erd \end_inset and the \family typewriter use \family default \begin_inset Quotes eld \end_inset literal \begin_inset Quotes erd \end_inset are used, to be WS-I-compliant. WS-I only supports rpc/literal and document/literal. \end_layout \begin_layout Itemize A custom port 8080 is used instead of the usual HTTP port 80. \end_layout \begin_layout Itemize The server name is a \begin_inset Quotes eld \end_inset symbolic \begin_inset Quotes erd \end_inset name, not a real known name in our site. This can cause the need to give explicit URLs from time to time. \end_layout \begin_layout Subsection A Python ZSI server for the SquareService \end_layout \begin_layout Subsubsection Generating stubs from WSDL \end_layout \begin_layout Paragraph ZSI 2.1 \end_layout \begin_layout Standard ZSI 2.1 comes with a python script \family typewriter wsdl2py \family default to generate code from a WSDL file. It creates \end_layout \begin_layout Itemize python bindings for the service and data structures and \end_layout \begin_layout Itemize a server skeleton for service dispatch where the actual service worker code will be hooked into. \end_layout \begin_layout Standard If you have installed ZSI on top of your python installation you can invoke the scripts like this (change your installation base path according to your setup): \begin_inset Foot status open \begin_layout Standard The installation base path for all ZSI 2.1 examples here is /apps/pydev/hjoukl. \end_layout \end_inset \end_layout \begin_layout LyX-Code /apps/pydev/hjoukl/bin/wsdl2py SquareService.wsdl \end_layout \begin_layout Standard This will generate the files \end_layout \begin_layout Itemize \family typewriter SquareService_client.py, \end_layout \begin_layout Itemize \family typewriter SquareService_server.py \family default and \family typewriter \end_layout \begin_layout Itemize \family typewriter SquareService_types.py \family default . \end_layout \begin_layout Paragraph ZSI 2.0 \end_layout \begin_layout Standard ZSI 2.0 uses the two scripts \family typewriter wsdl2py \family default and \family typewriter wsdl2dispatch \family default for code generation and uses different names for the generated files: \end_layout \begin_layout LyX-Code $ /apps/pydev/bin/wsdl2py --file SquareService.wsdl \end_layout \begin_layout LyX-Code $ /apps/pydev/bin/wsdl2dispatch --file SquareService.wsdl \end_layout \begin_layout LyX-Code $ $ ls \end_layout \begin_layout LyX-Code SquareService.wsdl@ SquareService_services_server.py \end_layout \begin_layout LyX-Code SquareService_services.py SquareService_services_types.py \end_layout \begin_layout Standard What do we have now? We have bindings to work with the services in python and a skeleton for dispatching to the actual worker methods. What we still need is \end_layout \begin_layout Itemize the main program that runs a (HTTP-) server with a request handler for the services and \end_layout \begin_layout Itemize the hooks to invoke the worker methods. \end_layout \begin_layout Standard Luckily, ZSI includes the ZSI.ServiceContainer module which implements all this machinery for us. \end_layout \begin_layout Subsubsection Writing the SquareService web server \begin_inset LatexCommand \label{sub:SquareService-web-server} \end_inset \end_layout \begin_layout Standard This is our main program \family typewriter mySquareServer.py \family default . It basically imports the necessary ZSI stuff, adds minimal command line configurability for logging purposes and lets us choose a server HTTP port: \end_layout \begin_layout Paragraph ZSI 2.1 \end_layout \begin_layout LyX-Code #!/apps/pydev/hjoukl/bin/python2.4 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # *** ZSI 2.1 *** \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code from optparse import OptionParser \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Import the ZSI stuff you'd need no matter what \end_layout \begin_layout LyX-Code from ZSI.wstools import logging \end_layout \begin_layout LyX-Code from ZSI.ServiceContainer import AsServer \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Import the generated Server Object \end_layout \begin_layout LyX-Code from SquareService_server import SquareService \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Create a Server implementation by inheritance \end_layout \begin_layout LyX-Code class MySquareService(SquareService): \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Make WSDL available for HTTP GET \end_layout \begin_layout LyX-Code _wsdl = "".join(open("SquareService.wsdl").readlines()) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def soap_getSquare(self, ps, **kw): \end_layout \begin_layout LyX-Code # Call the generated base class method to get appropriate \end_layout \begin_layout LyX-Code # input/output data structures \end_layout \begin_layout LyX-Code request, response = SquareService.soap_getSquare(self, ps, **kw) \end_layout \begin_layout LyX-Code response._return = self.getSquare(request._x) \end_layout \begin_layout LyX-Code return request, response \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def getSquare(self, x): \end_layout \begin_layout LyX-Code return x**2 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code op = OptionParser(usage="%prog [options]") \end_layout \begin_layout LyX-Code op.add_option("-l", "--loglevel", help="loglevel (DEBUG, WARN)", \end_layout \begin_layout LyX-Code metavar="LOGLEVEL") \end_layout \begin_layout LyX-Code op.add_option("-p", "--port", help="HTTP port", \end_layout \begin_layout LyX-Code metavar="PORT", default=8080, type="int") \end_layout \begin_layout LyX-Code options, args = op.parse_args() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # set the loglevel according to cmd line arg \end_layout \begin_layout LyX-Code if options.loglevel: \end_layout \begin_layout LyX-Code loglevel = eval(options.loglevel, logging.__dict__) \end_layout \begin_layout LyX-Code logger = logging.getLogger("") \end_layout \begin_layout LyX-Code logger.setLevel(loglevel) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Run the server with a given list services \end_layout \begin_layout LyX-Code AsServer(port=options.port, services=[MySquareService(),]) \end_layout \begin_layout Standard ZSI 2.0 \end_layout \begin_layout LyX-Code #!/apps/pydev/bin/python2.4 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # *** ZSI 2.0 *** \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code from optparse import OptionParser \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Import the ZSI stuff you'd need no matter what \end_layout \begin_layout LyX-Code from ZSI.wstools import logging \end_layout \begin_layout LyX-Code from ZSI.ServiceContainer import AsServer \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Import the generated Server Object \end_layout \begin_layout LyX-Code from SquareService_services_server import SquareService \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Create a Server implementation by inheritance \end_layout \begin_layout LyX-Code class MySquareService(SquareService): \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Make WSDL available for HTTP GET \end_layout \begin_layout LyX-Code _wsdl = "".join(open("SquareService.wsdl").readlines()) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def soap_getSquare(self, ps, **kw): \end_layout \begin_layout LyX-Code # Call the generated base class method to get appropriate \end_layout \begin_layout LyX-Code # input/output data structures \end_layout \begin_layout LyX-Code response = SquareService.soap_getSquare(self, ps, **kw) \end_layout \begin_layout LyX-Code request = self.request \end_layout \begin_layout LyX-Code response._return = self.getSquare(request._x) \end_layout \begin_layout LyX-Code return response \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def getSquare(self, x): \end_layout \begin_layout LyX-Code return x**2 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code op = OptionParser(usage="%prog [options]") \end_layout \begin_layout LyX-Code op.add_option("-l", "--loglevel", help="loglevel (DEBUG, WARN)", \end_layout \begin_layout LyX-Code metavar="LOGLEVEL") \end_layout \begin_layout LyX-Code op.add_option("-p", "--port", help="HTTP port", \end_layout \begin_layout LyX-Code metavar="PORT", default=8080, type="int") \end_layout \begin_layout LyX-Code options, args = op.parse_args() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # set the loglevel according to cmd line arg \end_layout \begin_layout LyX-Code if options.loglevel: \end_layout \begin_layout LyX-Code loglevel = eval(options.loglevel, logging.__dict__) \end_layout \begin_layout LyX-Code logger = logging.getLogger("") \end_layout \begin_layout LyX-Code logger.setLevel(loglevel) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Run the server with a given list services \end_layout \begin_layout LyX-Code AsServer(port=options.port, services=[MySquareService(),]) \end_layout \begin_layout Standard Apart from the command line parsing stuff which is only usability sugar, ZSI provides us with everything we need. All we have to do is \end_layout \begin_layout Enumerate Create a class \family typewriter MySquareService \family default that inherits from the generated \family typewriter SquareService \family default class, to hook in the worker code. This is done with the overridden \family typewriter soap_getSquare() \family default method, which \end_layout \begin_deeper \begin_layout Itemize calls its base class method to retrieve the (input and) output data structure (i.e. request & response) and \end_layout \begin_layout Itemize invokes the worker code that performs the actual calculation, and populates the response data structure. Note that the names in these data structure differ from the names in the WSDL in that they use leading underscores. This is done with good reason (e.g., you can not use the name \begin_inset Quotes eld \end_inset return \begin_inset Quotes erd \end_inset for the result message part \family typewriter \family default as a valid Python attribute name) due to XML allowing a far wider range of element names than the Python language. You can find details on ZSI naming concepts ( \begin_inset Quotes eld \end_inset aname \begin_inset Quotes erd \end_inset ) in the ZSI User Guide \begin_inset LatexCommand \cite{ZSIuserguide2.0} \end_inset . \end_layout \begin_layout Standard Hereīs the only difference between the 2.0 & 2.1 implementations, as 2.1 slightly changes the soap_<...> methods signature and return value. \end_layout \begin_layout Standard We also add a _wsdl attribute that contains the WSDL, to be served when some client issues a HTTP GET (on \family typewriter ?wsdl \family default ), which is actually common behaviour to get the service description (apart from UDDI). \begin_inset Foot status open \begin_layout Standard The service invocation itself uses HTTP POST. You might want to take a look at the ZSI ServiceContainer module and its \family typewriter SOAPRequestHandler \family default class for more inside information. \end_layout \end_inset ZSI 2.0 wouldnīt need this additional step as its code generation mechanisms put all the WSDL source into the generated files anyway, but 2.1 does not (and we consider 2.1 behaviour cleaner). \end_layout \end_deeper \begin_layout Enumerate Put an instance of our class into the \family typewriter services \family default argument of the \family typewriter AsServer() \family default function. \end_layout \begin_layout Standard Thatīs it. We can now run a full-fledged WS-Server that hosts our SquareService. \end_layout \begin_layout Standard Comments: \end_layout \begin_layout Itemize The dispatch to the appropriate service operation is handled in the \family typewriter soapAction \family default dictionary in the generated code. This dictionary maps the action that is requested to the method that gets invoked. The ZSI standard request handler takes the HTTP header field soapAction and propagates its value to this dispatch mechanism. Thus, everything works out-of-the-box if you use the \begin_inset Quotes eld \end_inset soapAction \begin_inset Quotes erd \end_inset operation-attribute in your WSDL file (and if your service client actually provides this header field with its request). Weīll see another way to drive the ZSI request handler in section \begin_inset LatexCommand \ref{sub:Python-ZSI-FinancialService-server} \end_inset . \end_layout \begin_layout Itemize Command-line options allow us to make use of ZSIīs logging capabilities, to print out more information on ZSI server workings (unfortunately, not the incoming XML request. See section \begin_inset LatexCommand \ref{sub:Logging-the-client-request} \end_inset for help on this). \end_layout \begin_layout Subsection A Python ZSI client for the SquareService \begin_inset LatexCommand \label{sub:ZSI-SquareService-client} \end_inset \end_layout \begin_layout Subsubsection Using generated client code \begin_inset LatexCommand \label{sub:ZSI-SquareService-client-generated} \end_inset \end_layout \begin_layout Standard We implement a client that calls getSquare from the SquareService in \family typewriter mySquareClient.py \family default as follows: \end_layout \begin_layout Paragraph ZSI 2.1 \end_layout \begin_layout LyX-Code #!/apps/pydev/hjoukl/bin/python2.4 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # *** ZSI 2.1 *** \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code import sys \end_layout \begin_layout LyX-Code from optparse import OptionParser \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Import the generated client code \end_layout \begin_layout LyX-Code from SquareService_client import * \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code op = OptionParser(usage="%prog [options]") \end_layout \begin_layout LyX-Code op.add_option("-u", "--url", help="service URL", metavar="URL") \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code options, args = op.parse_args() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code service = SquareServiceLocator().getSquarePort(url=options.url, tracefile=sys.stdou t) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code print ' \backslash nAccessing service SquareService, method getSquare...' \end_layout \begin_layout LyX-Code while 1: \end_layout \begin_layout LyX-Code x = float(raw_input("Enter x: ")) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code request = getSquareRequest(x=x) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code response = service.getSquare(request) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code print "x**2 =", response._return \end_layout \begin_layout Paragraph ZSI 2.0 \end_layout \begin_layout LyX-Code #!/apps/pydev/bin/python2.4 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # *** ZSI 2.0 *** \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code import sys \end_layout \begin_layout LyX-Code from optparse import OptionParser \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Import the generated client code \end_layout \begin_layout LyX-Code from SquareService_services import * \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code op = OptionParser(usage="%prog [options]") \end_layout \begin_layout LyX-Code op.add_option("-u", "--url", help="service URL", metavar="URL") \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code options, args = op.parse_args() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code service = SquareServiceLocator().getSquarePortType(url=options.url, tracefile=sys.s tdout) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code print ' \backslash nAccessing service SquareService, method getSquare...' \end_layout \begin_layout LyX-Code while 1: \end_layout \begin_layout LyX-Code x = float(raw_input("Enter x: ")) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code request = getSquareRequest() \end_layout \begin_layout LyX-Code request._x = x \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code response = service.getSquare(request) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code print "x**2 =", response._return \end_layout \begin_layout Standard This is pretty straightforward. Some of the code handles command line stuff which has nothing to do with web services in the first place. The things we have to do is to \end_layout \begin_layout Enumerate Import the generated code we need, from either the \family typewriter SquareService_client \family default (ZSI 2.1) / \family typewriter SquareService_services \family default (ZSI 2.0) module, \end_layout \begin_layout Enumerate Create a \family typewriter SquareServiceLocator \family default , \end_layout \begin_layout Enumerate Invoke its \family typewriter getSquarePort() \family default (ZSI 2.1) / \family typewriter getSquarePortType() \family default (ZSI 2.0)method to retrive the square service binding, \end_layout \begin_layout Enumerate populate the request data structure \end_layout \begin_deeper \begin_layout Itemize using keyword-arguments (ZSI 2.1) \end_layout \begin_layout Itemize by explicitly setting request instance attributes (ZSI 2.0) \end_layout \end_deeper \begin_layout Enumerate call the service bindingīs \family typewriter getSquare() \family default method with the request data as input parameter and \end_layout \begin_layout Enumerate read the result data structure. \end_layout \begin_layout Standard Again, the naming concepts of ZSI as noted in section \begin_inset LatexCommand \ref{sub:SquareService-web-server} \end_inset apply. \end_layout \begin_layout Standard This is a sample session of our new client: \end_layout \begin_layout LyX-Code $ ./mySquareClient.py -u "http://adevp02:8080/SquareService" \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code Accessing service SquareService, method getSquare... \end_layout \begin_layout LyX-Code Enter x: 3 \end_layout \begin_layout LyX-Code _________________________________ Wed Jan 2 10:16:23 2008 REQUEST: \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code 3.000000 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code _________________________________ Wed Jan 2 10:16:23 2008 RESPONSE: \end_layout \begin_layout LyX-Code 200 \end_layout \begin_layout LyX-Code OK \end_layout \begin_layout LyX-Code ------- \end_layout \begin_layout LyX-Code Server: ZSI/1.1 BaseHTTP/0.3 Python/2.4.4 \end_layout \begin_layout LyX-Code Date: Wed, 02 Jan 2008 09:16:23 GMT \end_layout \begin_layout LyX-Code Content-type: text/xml; charset="utf-8" \end_layout \begin_layout LyX-Code Content-Length: 497 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code 9.000000 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code x**2 = 9.0 \end_layout \begin_layout LyX-Code Enter x: \end_layout \begin_layout Standard Comments: \end_layout \begin_layout Itemize We use an explicit URL to access the service, as our WSDL contains only the \begin_inset Quotes eld \end_inset symbolic \begin_inset Quotes erd \end_inset server name not known by our siteīs naming services. In a real-world application, you'd not even need to do so, as the generated client code knows the service URL as given in the WSDL. \end_layout \begin_layout Itemize The client does not issue any HTTP GET for service access, because all the necessary data structures have been pre-generated from the WSDL service description. \end_layout \begin_layout Subsubsection Using ServiceProxy \begin_inset LatexCommand \label{sub:SquareService-ZSI-ServiceProxy} \end_inset \end_layout \begin_layout Standard ZSI also offers an option to implement a client without using the pre-generated stub code: Everything can be accessed through the \family typewriter ServiceProxy \family default object that gets all the necessary information from the WSDL, on the fly. This is an example of a ServiceProxy-client (the very same code works with both ZSI 2.1 & 2.0): \end_layout \begin_layout LyX-Code #!/apps/pydev/bin/python2.4 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code import sys \end_layout \begin_layout LyX-Code from optparse import OptionParser \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code from ZSI.ServiceProxy import ServiceProxy \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code op = OptionParser(usage="%prog [options]") \end_layout \begin_layout LyX-Code op.add_option("-u", "--url", help="service URL (mandatory)", metavar="URL") \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code options, args = op.parse_args() \end_layout \begin_layout LyX-Code if not options.url: \end_layout \begin_layout LyX-Code op.print_help() \end_layout \begin_layout LyX-Code sys.exit(1) \end_layout \begin_layout LyX-Code else: \end_layout \begin_layout LyX-Code # "Canonicalize" WSDL URL \end_layout \begin_layout LyX-Code options.url = options.url.replace("?WSDL", "?wsdl") \end_layout \begin_layout LyX-Code if not options.url.endswith("?wsdl"): \end_layout \begin_layout LyX-Code options.url += "?wsdl" \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code service = ServiceProxy(wsdl=options.url, tracefile=sys.stdout) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code print ' \backslash nAccessing service SquareService, method getSquare...' \end_layout \begin_layout LyX-Code while 1: \end_layout \begin_layout LyX-Code x = float(raw_input("Enter x: ")) \end_layout \begin_layout LyX-Code resultDict = service.getSquare(x=x) \end_layout \begin_layout LyX-Code print "x**2 =", resultDict['return'] \end_layout \begin_layout Standard Basically, all you need to do is to create a \family typewriter ServiceProxy \family default instance with the information where to get the WSDL. You can then simply access its getSquare method; only make sure you give its argument as a keyword argument. \end_layout \begin_layout Standard This is the output of an example ServiceProxy-client run: \end_layout \begin_layout LyX-Code $ ./mySquareSPclient.py -u "http://adevp02:8080/SquareService?wsdl" \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code Accessing service SquareService, method getSquare... \end_layout \begin_layout LyX-Code Enter x: 4 \end_layout \begin_layout LyX-Code _________________________________ Wed Jan 2 10:30:17 2008 REQUEST: \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code 4.000000 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code _________________________________ Wed Jan 2 10:30:18 2008 RESPONSE: \end_layout \begin_layout LyX-Code 200 \end_layout \begin_layout LyX-Code OK \end_layout \begin_layout LyX-Code ------- \end_layout \begin_layout LyX-Code Server: ZSI/1.1 BaseHTTP/0.3 Python/2.4.4 \end_layout \begin_layout LyX-Code Date: Wed, 02 Jan 2008 09:30:17 GMT \end_layout \begin_layout LyX-Code Content-type: text/xml; charset="utf-8" \end_layout \begin_layout LyX-Code Content-Length: 498 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code 16.000000 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code x**2 = 16.0 \end_layout \begin_layout LyX-Code Enter x: \end_layout \begin_layout Standard Comments: \end_layout \begin_layout Itemize When running the ServiceProxy-notion of a ZSI WS-client, take a good look at your ZSI server. You will then see that the ServiceProxy client issues an HTTP GET first, to retrieve the WSDL information and create the necessary code and data structures. \end_layout \begin_layout Itemize To tell the whole truth, ServiceProxy basically does much the same code generation like \family typewriter wsdl2py \family default under the hood, and creates a cache dir \family typewriter ~/.zsi_service_proxy_dir \family default (ZSI 2.1) resp. \family typewriter ./.service_proxy_dir \family default (ZSI 2.0) for the generated files. This cache dir is then appended to \family typewriter sys.path \family default for module import. \begin_inset Foot status open \begin_layout Standard The attentive reader might observe that this may have an implication on ServiceProxy-client behaviour depending on where you run it. As the local directory is the 1st \family typewriter sys.path \family default entry per default, existing pre-generated (from \family typewriter wsdl2py \family default ) code might beat the cache-dir modules in import order - depending on module naming. \end_layout \end_inset \end_layout \begin_layout Subsection A gSOAP C++ client for the SquareService \end_layout \begin_layout Subsubsection Generation from WSDL \end_layout \begin_layout Standard With our SquareService server running, we can generate the client stubs: \end_layout \begin_layout Enumerate First, gSOAP needs to create a header file for the service using the \family typewriter wsdl2h \family default generator: \end_layout \begin_deeper \begin_layout LyX-Code /apps/pydev/bin/wsdl2h -o squareService.h \end_layout \begin_layout LyX-Code http://adevp02:8080/SquareService?wsdl \end_layout \begin_layout Standard Just specify the name for the header file and the URL where the WSDL can be received with a HTTP GET request. The \family typewriter squareService.h \family default header will be created: \end_layout \begin_layout LyX-Code /apps/pydev/bin/wsdl2h -o squareService.h http://adevp02:8080/SquareService?wsdl \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code ** The gSOAP WSDL parser for C and C++ 1.2.9l \end_layout \begin_layout LyX-Code ** Copyright (C) 2000-2007 Robert van Engelen, Genivia Inc. \end_layout \begin_layout LyX-Code ** All Rights Reserved. This product is provided "as is", without any warranty. \end_layout \begin_layout LyX-Code ** The gSOAP WSDL parser is released under one of the following two licenses: \end_layout \begin_layout LyX-Code ** GPL or the commercial license by Genivia Inc. Use option -l for more info. \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code Saving squareService.h \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code Cannot open file 'typemap.dat' \end_layout \begin_layout LyX-Code Problem reading type map file typemap.dat. \end_layout \begin_layout LyX-Code Using internal type definitions for C++ instead. \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code Connecting to 'http://adevp02:8080/SquareService?wsdl' to retrieve WSDL/XSD... connected, receiving... \end_layout \begin_layout LyX-Code Warning: part 'return' uses literal style and should refer to an element rather than a type \end_layout \begin_layout LyX-Code Warning: part 'x' uses literal style and should refer to an element rather than a type \end_layout \begin_layout LyX-Code Warning: part 'x' uses literal style and should refer to an element rather than a type \end_layout \begin_layout LyX-Code Warning: part 'x' uses literal style and should refer to an element rather than a type \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code To complete the process, compile with: \end_layout \begin_layout LyX-Code soapcpp2 squareService.h \end_layout \begin_layout Standard Note that gSOAP tells us about what it thinks might cause problems in our WSDL; we ignore that for this example. \end_layout \end_deeper \begin_layout Enumerate Next we let gSOAP create the stub code for us, using the newly-created header: \end_layout \begin_deeper \begin_layout LyX-Code $ /apps/pydev/bin/soapcpp2 -I /data/pydev/DOWNLOADS/WebServices/C++/gsoap-2.7/soa pcpp2/import squareService.h \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code ** The gSOAP Stub and Skeleton Compiler for C and C++ 2.7.9l \end_layout \begin_layout LyX-Code ** Copyright (C) 2000-2007, Robert van Engelen, Genivia Inc. \end_layout \begin_layout LyX-Code ** All Rights Reserved. This product is provided "as is", without any warranty. \end_layout \begin_layout LyX-Code ** The gSOAP compiler is released under one of the following three licenses: \end_layout \begin_layout LyX-Code ** GPL, the gSOAP public license, or the commercial license by Genivia Inc. \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code Saving soapStub.h \end_layout \begin_layout LyX-Code Saving soapH.h \end_layout \begin_layout LyX-Code Saving soapC.cpp \end_layout \begin_layout LyX-Code Saving soapClient.cpp \end_layout \begin_layout LyX-Code Saving soapClientLib.cpp \end_layout \begin_layout LyX-Code Saving soapServer.cpp \end_layout \begin_layout LyX-Code Saving soapServerLib.cpp \end_layout \begin_layout LyX-Code Using ns1 service name: SquareBinding \end_layout \begin_layout LyX-Code Using ns1 service style: document \end_layout \begin_layout LyX-Code Using ns1 service encoding: literal \end_layout \begin_layout LyX-Code Using ns1 service location: http://adevp02:8080/SquareService \end_layout \begin_layout LyX-Code Using ns1 schema namespace: http://services.zsiserver.net/SquareService \end_layout \begin_layout LyX-Code Saving soapSquareBindingProxy.h client proxy \end_layout \begin_layout LyX-Code Saving soapSquareBindingObject.h server object \end_layout \begin_layout LyX-Code Saving SquareBinding.getSquare.req.xml sample SOAP/XML request \end_layout \begin_layout LyX-Code Saving SquareBinding.getSquare.res.xml sample SOAP/XML response \end_layout \begin_layout LyX-Code Saving SquareBinding.nsmap namespace mapping table \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code Compilation successful \end_layout \begin_layout Standard We must explicitly give the \family typewriter gSOAP/soapcpp2 \family default directory as include directory. This is not being installed with the gSOAP installation (in the \begin_inset Quotes eld \end_inset make install \begin_inset Quotes erd \end_inset step) but resides in the path where you extracted the gSOAP tarball. It contains some special header files gSOAP does not install on your system. \end_layout \begin_layout Standard You might have noticed that \family typewriter soapcpp2 \family default says it is using service style \begin_inset Quotes eld \end_inset document \begin_inset Quotes erd \end_inset as opposed to whatīs defined in the WSDL ( \begin_inset Quotes eld \end_inset rpc \begin_inset Quotes erd \end_inset ); this seems to be a cosmetic issue only and does not affect the usability of the generated code. \end_layout \begin_layout Standard The above command produces the following client stubs (server skeleton code also by the way, but we will not use it here): \end_layout \begin_layout LyX-Code $ ls -l \end_layout \begin_layout LyX-Code total 105 \end_layout \begin_layout LyX-Code -rw-r--r-- 1 lb54320 intern 444 Jan 2 10:48 SquareBinding.getSquare.req.xml \end_layout \begin_layout LyX-Code -rw-r--r-- 1 lb54320 intern 470 Jan 2 10:48 SquareBinding.getSquare.res.xml \end_layout \begin_layout LyX-Code -rw-r--r-- 1 lb54320 intern 556 Jan 2 10:48 SquareBinding.nsmap \end_layout \begin_layout LyX-Code -rw-r--r-- 1 lb54320 intern 57364 Jan 2 10:48 soapC.cpp \end_layout \begin_layout LyX-Code -rw-r--r-- 1 lb54320 intern 2326 Jan 2 10:48 soapClient.cpp \end_layout \begin_layout LyX-Code -rw-r--r-- 1 lb54320 intern 464 Jan 2 10:48 soapClientLib.cpp \end_layout \begin_layout LyX-Code -rw-r--r-- 1 lb54320 intern 15694 Jan 2 10:48 soapH.h \end_layout \begin_layout LyX-Code -rw-r--r-- 1 lb54320 intern 3152 Jan 2 10:48 soapServer.cpp \end_layout \begin_layout LyX-Code -rw-r--r-- 1 lb54320 intern 464 Jan 2 10:48 soapServerLib.cpp \end_layout \begin_layout LyX-Code -rw-r--r-- 1 lb54320 intern 2470 Jan 2 10:48 soapSquareBindingObject.h \end_layout \begin_layout LyX-Code -rw-r--r-- 1 lb54320 intern 1826 Jan 2 10:48 soapSquareBindingProxy.h \end_layout \begin_layout LyX-Code -rw-r--r-- 1 lb54320 intern 6915 Jan 2 10:48 soapStub.h \end_layout \begin_layout LyX-Code -rw-r--r-- 1 lb54320 intern 6662 Jan 2 10:38 squareService.h \end_layout \end_deeper \begin_layout Standard Whatīs left now is to implement the client program and make use of the generated code. \end_layout \begin_layout Subsubsection Client implementation \begin_inset LatexCommand \label{sub:gsoap-square-client} \end_inset \end_layout \begin_layout Standard This is a sample client to access the SquareService: \end_layout \begin_layout LyX-Code $ cat myCSquareClient.cpp \end_layout \begin_layout LyX-Code #include "soapH.h" \end_layout \begin_layout LyX-Code #include "SquareBinding.nsmap" \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code #include \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code int main(void) \end_layout \begin_layout LyX-Code { \end_layout \begin_layout LyX-Code struct soap soap; \end_layout \begin_layout LyX-Code double x = 0; \end_layout \begin_layout LyX-Code struct ns1__getSquareResponse response; \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code soap_init(&soap); \end_layout \begin_layout LyX-Code while (1) { \end_layout \begin_layout LyX-Code std::cout << "Enter x value: "; \end_layout \begin_layout LyX-Code std::cin >> x; \end_layout \begin_layout LyX-Code if (soap_call_ns1__getSquare(&soap, NULL, NULL, x, response) == SOAP_OK) { \end_layout \begin_layout LyX-Code std::cout << "Result: " << response.return_ << std::endl; \end_layout \begin_layout LyX-Code } else { \end_layout \begin_layout LyX-Code soap_print_fault(&soap, stderr); \end_layout \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code soap_destroy(&soap); \end_layout \begin_layout LyX-Code soap_end(&soap); \end_layout \begin_layout LyX-Code soap_done(&soap); \end_layout \begin_layout LyX-Code return 0; \end_layout \begin_layout LyX-Code } \end_layout \begin_layout Standard Comments: \end_layout \begin_layout Itemize Note gSOAPīs renaming of the WSDL result message part \family typewriter \family default to \family typewriter return_ \family default in the response data structure \family typewriter struct ns1__getSquareResponse \family default to cater for conflicts with reserved C/C++ words, similar to the ZSI \begin_inset Quotes eld \end_inset aname \begin_inset Quotes erd \end_inset concept \end_layout \begin_layout Itemize There are also other ways to invoke this service with the gSOAP mechanisms, namely the \family typewriter SquareBinding \family default class defined in \family typewriter soapSquareBindingProxy.h \family default , which would take care of the initialization & destruction activities needed in the above client code. We will use this (better) approach in the next examples. \end_layout \begin_layout Subsubsection gSOAP client compilation \end_layout \begin_layout Paragraph gcc 2.95.2 \end_layout \begin_layout LyX-Code $ g++ -o myCSquareClient -R/apps/prod/lib -I/apps/pydev/include \end_layout \begin_layout LyX-Code -L/apps/pydev/lib soapC.cpp soapClient.cpp myCSquareClient.cpp -lgsoap++ -lsocket \end_layout \begin_layout Paragraph gcc 3.4.4 \end_layout \begin_layout Standard Note: Compiling with gcc 3.4.4, the nsl library had to be added to the linked libraries: \end_layout \begin_layout LyX-Code /apps/local/gcc/3.4.4/bin/g++ -o myCSquareClient -R /apps/prod/gcc/3.4.4/lib -I/apps/pydev/gcc/3.4.4/include -L/apps/pydev/gcc/3.4.4/lib soapC.cpp soapClient.cpp myCSquareClient.cpp -lgsoap++ -lsocket -lnsl \end_layout \begin_layout Standard You can then run this simple C++ Web Service client: \end_layout \begin_layout LyX-Code $ ./myCSquareClient \end_layout \begin_layout LyX-Code Enter x value: 3 \end_layout \begin_layout LyX-Code Result: 9 \end_layout \begin_layout LyX-Code Enter x value: ^C \end_layout \begin_layout Section Structured datatypes: The rpc/literal DateService \end_layout \begin_layout Standard Letīs move on to a more elaborate service, elaborate in the sense of using structured datatypes now (not that the service example itself was particularly ingenious). Anyway, we will now implement the DateService service that exposes two methods: \end_layout \begin_layout Itemize getCurrentDate() takes a string argument and returns the current date as a datetime structure \end_layout \begin_layout Itemize getDate(, ) takes an integer offset and a date structure as arguments and returns the given date plus the offset (in days) as a date structure \end_layout \begin_layout Subsection The DateService WSDL \begin_inset LatexCommand \label{sub:The-DateService-WSDL} \end_inset \end_layout \begin_layout Standard The DateService is described in \family typewriter DateService.wsdl \family default : \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code Date Web Service \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout Standard Comments: \end_layout \begin_layout Itemize Again, rpc/literal has been chosen. \end_layout \begin_layout Itemize A ComplexType \begin_inset Quotes eld \end_inset Date \begin_inset Quotes erd \end_inset is defined in the section. This type is being used as a return type (getCurrentDate, getDate) and as a method argument type (getDate). \end_layout \begin_layout Subsection A Python ZSI DateService server \begin_inset LatexCommand \label{sub:A-Python-ZSI-DateService-server} \end_inset \end_layout \begin_layout Standard The tasks at hand are the same as for the SquareService example. \end_layout \begin_layout Subsubsection Code generation from WSDL \end_layout \begin_layout Paragraph ZSI 2.1 \end_layout \begin_layout LyX-Code $ /apps/pydev/hjoukl/bin/wsdl2py DateService.wsdl \end_layout \begin_layout LyX-Code $ ls -l \end_layout \begin_layout LyX-Code total 15 \end_layout \begin_layout LyX-Code -rw-r--r-- 1 lb54320 intern 3180 Jan 2 14:12 DateService.wsdl \end_layout \begin_layout LyX-Code -rw-r--r-- 1 lb54320 intern 3935 Jan 2 14:12 DateService_client.py \end_layout \begin_layout LyX-Code -rw-r--r-- 1 lb54320 intern 3209 Jan 2 14:12 DateService_server.py \end_layout \begin_layout LyX-Code -rw-r--r-- 1 lb54320 intern 2896 Jan 2 14:12 DateService_types.py \end_layout \begin_layout Paragraph ZSI 2.0 \end_layout \begin_layout LyX-Code $ /apps/pydev/bin/wsdl2py -f DateService.wsdl \end_layout \begin_layout LyX-Code $ /apps/pydev/bin/wsdl2dispatch -f DateService.wsdl \end_layout \begin_layout LyX-Code $ ls -l \end_layout \begin_layout LyX-Code total 34 \end_layout \begin_layout LyX-Code lrwxrwxrwx 1 lb54320 intern 28 Jan 3 17:45 DateService.wsdl -> ../2.1alpha/DateS ervice.wsdl \end_layout \begin_layout LyX-Code -rw-r--r-- 1 lb54320 intern 3525 Jan 7 09:56 DateService_services.py \end_layout \begin_layout LyX-Code -rw-r--r-- 1 lb54320 intern 4428 Jan 7 09:56 DateService_services_server.py \end_layout \begin_layout LyX-Code -rw-r--r-- 1 lb54320 intern 2818 Jan 7 09:56 DateService_services_types.py \end_layout \begin_layout Subsubsection The DateService server-side implementation \begin_inset LatexCommand \label{sub:The-DateService-server-side-impl} \end_inset \end_layout \begin_layout Standard The server implementation needs exactly the same steps as seen in section \begin_inset LatexCommand \ref{sub:SquareService-web-server} \end_inset , with the difference of putting a DateService instance into the ServiceContaine r now. We create a server \family typewriter myDateServer.py \family default like this: \end_layout \begin_layout Paragraph ZSI 2.1 \end_layout \begin_layout LyX-Code #!/apps/pydev/hjoukl/bin/python2.4 \end_layout \begin_layout LyX-Code import time \end_layout \begin_layout LyX-Code from optparse import OptionParser \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Import the ZSI stuff you'd need no matter what \end_layout \begin_layout LyX-Code from ZSI.wstools import logging \end_layout \begin_layout LyX-Code from ZSI.ServiceContainer import AsServer \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Import the generated Server Object \end_layout \begin_layout LyX-Code from DateService_server import * \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Create a Server implementation by inheritance \end_layout \begin_layout LyX-Code class MyDateService(simple_Date_Service): \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Make WSDL available for HTTP GET \end_layout \begin_layout LyX-Code _wsdl = "".join(open("DateService.wsdl").readlines()) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def soap_getCurrentDate(self, ps, **kw): \end_layout \begin_layout LyX-Code # Call the generated base class method to get appropriate \end_layout \begin_layout LyX-Code # input/output data structures \end_layout \begin_layout LyX-Code request, response = simple_Date_Service.soap_getCurrentDate(self, ps, **kw) \end_layout \begin_layout LyX-Code print "Client says:", request._input \end_layout \begin_layout LyX-Code dt = time.localtime(time.time()) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Use a structurally equivalent (to the WSDL complex type structure) \end_layout \begin_layout LyX-Code # python class object \end_layout \begin_layout LyX-Code class today: \end_layout \begin_layout LyX-Code _year = dt[0] \end_layout \begin_layout LyX-Code _month = dt[1] \end_layout \begin_layout LyX-Code _day = dt[2] \end_layout \begin_layout LyX-Code _hour = dt[3] \end_layout \begin_layout LyX-Code _minute = dt[4] \end_layout \begin_layout LyX-Code _second = dt[5] \end_layout \begin_layout LyX-Code _weekday = dt[6] \end_layout \begin_layout LyX-Code _dayOfYear = dt[7] \end_layout \begin_layout LyX-Code _dst = dt[8] \end_layout \begin_layout LyX-Code response._today = today \end_layout \begin_layout LyX-Code return request, response \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def soap_getDate(self, ps, **kw): \end_layout \begin_layout LyX-Code # Call the generated base class method to get appropriate \end_layout \begin_layout LyX-Code # input/output data structures \end_layout \begin_layout LyX-Code request, response = simple_Date_Service.soap_getDate(self, ps, **kw) \end_layout \begin_layout LyX-Code date = request._someday \end_layout \begin_layout LyX-Code offset = request._offset \end_layout \begin_layout LyX-Code if not offset: \end_layout \begin_layout LyX-Code offset = 0 \end_layout \begin_layout LyX-Code if not date: \end_layout \begin_layout LyX-Code raise RuntimeError("missing input data") \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # actual worker code, add give offset to given input date \end_layout \begin_layout LyX-Code sec = 3600 * 24 # seconds/hour * 24h \end_layout \begin_layout LyX-Code providedDate_tuple = (date._year, date._month, date._day, \end_layout \begin_layout LyX-Code date._hour, date._minute, date._second, \end_layout \begin_layout LyX-Code date._weekday, date._dayOfYear, date._dst) \end_layout \begin_layout LyX-Code providedDate_sec = time.mktime(providedDate_tuple) \end_layout \begin_layout LyX-Code offset_sec = sec * offset \end_layout \begin_layout LyX-Code newDate_sec = providedDate_sec + offset_sec \end_layout \begin_layout LyX-Code newDate_tuple = time.localtime(newDate_sec) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code response = getDateResponse() \end_layout \begin_layout LyX-Code # Use a structurally equivalent (to the WSDL complex type structure) \end_layout \begin_layout LyX-Code # python class object \end_layout \begin_layout LyX-Code class day: \end_layout \begin_layout LyX-Code _year = newDate_tuple[0] \end_layout \begin_layout LyX-Code _month = newDate_tuple[1] \end_layout \begin_layout LyX-Code _day = newDate_tuple[2] \end_layout \begin_layout LyX-Code _hour = newDate_tuple[3] \end_layout \begin_layout LyX-Code _minute = newDate_tuple[4] \end_layout \begin_layout LyX-Code _second = newDate_tuple[5] \end_layout \begin_layout LyX-Code _weekday = newDate_tuple[6] \end_layout \begin_layout LyX-Code _dayOfYear = newDate_tuple[7] \end_layout \begin_layout LyX-Code _dst = newDate_tuple[8] \end_layout \begin_layout LyX-Code response._day = day \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code return request, response \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code op = OptionParser(usage="%prog [options]") \end_layout \begin_layout LyX-Code op.add_option("-l", "--loglevel", help="loglevel (DEBUG, WARN)", \end_layout \begin_layout LyX-Code metavar="LOGLEVEL") \end_layout \begin_layout LyX-Code op.add_option("-p", "--port", help="HTTP port", \end_layout \begin_layout LyX-Code metavar="PORT", default=8080, type="int") \end_layout \begin_layout LyX-Code options, args = op.parse_args() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # set the loglevel according to cmd line arg \end_layout \begin_layout LyX-Code if options.loglevel: \end_layout \begin_layout LyX-Code loglevel = eval(options.loglevel, logging.__dict__) \end_layout \begin_layout LyX-Code logger = logging.getLogger("") \end_layout \begin_layout LyX-Code logger.setLevel(loglevel) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Run the server with a given list services \end_layout \begin_layout LyX-Code AsServer(port=options.port, services=[MyDateService(),]) \end_layout \begin_layout Standard As in the previous example, the actual implementation has been hooked into the server skeleton, by inheriting from the generated service class. \end_layout \begin_layout Standard If you take a closer look at the two method implementations, you will notice that the data structures used for the returned date are just (nested) python classes. ZSI handles the serialization of that class into the actual SOAP message for us. \begin_inset Foot status open \begin_layout Standard The python object must be \begin_inset Quotes eld \end_inset structurally equivalent \begin_inset Quotes erd \end_inset to the XML datatype that is defined in the WSDL and that constitutes ZSIīs typecodes. \end_layout \end_inset In \end_layout \begin_layout Standard Comments: \end_layout \begin_layout Itemize This time, we did not strictly separate the worker code from the \family typewriter soap_<...> \family default methods. \end_layout \begin_layout Itemize The handling of complex types can be made more convenient by using the --complex Type option of wsdl2py. This is the recommended way to go and weīll see how it works in section \begin_inset LatexCommand \ref{sec:FinancialService-document/literal-service} \end_inset , but it requires metaclass magic and thus new-style classes to work, so you will require Python >= 2.2. \end_layout \begin_layout Paragraph ZSI 2.0 \end_layout \begin_layout Standard Remembering the SquareService example, the ZSI 2.1 and 2.0 server code differed only in the return value and method signature of the \family typewriter soap_<...>() \family default -method implementation-wise. We can exploit this to reuse the code written for ZSI 2.1, by help of an adapter module that provides some facilities to auto-wrap these methods. Using this adapter module, we can now implement a working ZSI 2.0 server like this: \end_layout \begin_layout LyX-Code import time \end_layout \begin_layout LyX-Code from optparse import OptionParser \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Import the ZSI stuff you'd need no matter what \end_layout \begin_layout LyX-Code from ZSI.wstools import logging \end_layout \begin_layout LyX-Code from ZSI.ServiceContainer import AsServer \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Import the generated Server Object \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code from ZSI.version import Version as zsiversion \end_layout \begin_layout LyX-Code print "*** ZSI version %s ***" % (zsiversion,) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Import the generated Server Object \end_layout \begin_layout LyX-Code try: \end_layout \begin_layout LyX-Code # 2.1alpha \end_layout \begin_layout LyX-Code from DateService_server import simple_Date_Service \end_layout \begin_layout LyX-Code # Dummy implementation \end_layout \begin_layout LyX-Code def adapt(method): \end_layout \begin_layout LyX-Code return method \end_layout \begin_layout LyX-Code except ImportError, e: \end_layout \begin_layout LyX-Code # 2.0final \end_layout \begin_layout LyX-Code from DateService_services_server import simple_Date_Service \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code from service_adaptor import ServiceAdaptor21 \end_layout \begin_layout LyX-Code adapt = ServiceAdaptor21.adapt \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Wrap generated 2.0 class to 2.1 soap_ method behaviour \end_layout \begin_layout LyX-Code simple_Date_Service = ServiceAdaptor21(simple_Date_Service) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Create a Server implementation by inheritance \end_layout \begin_layout LyX-Code # 2.1alpha implementation \end_layout \begin_layout LyX-Code class MyDateService(simple_Date_Service): \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Make WSDL available for HTTP GET \end_layout \begin_layout LyX-Code _wsdl = "".join(open("DateService.wsdl").readlines()) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code @adapt \end_layout \begin_layout LyX-Code def soap_getCurrentDate(self, ps, **kw): \end_layout \begin_layout LyX-Code # Call the generated base class method to get appropriate \end_layout \begin_layout LyX-Code # input/output data structures \end_layout \begin_layout LyX-Code request, response = simple_Date_Service.soap_getCurrentDate(self, ps, **kw) \end_layout \begin_layout LyX-Code print "Client says:", request._input \end_layout \begin_layout LyX-Code dt = time.localtime(time.time()) \end_layout \begin_layout LyX-Code # Use a structurally equivalent (to the WSDL complex type structure) \end_layout \begin_layout LyX-Code # python class object \end_layout \begin_layout LyX-Code class today: \end_layout \begin_layout LyX-Code _year = dt[0] \end_layout \begin_layout LyX-Code _month = dt[1] \end_layout \begin_layout LyX-Code _day = dt[2] \end_layout \begin_layout LyX-Code _hour = dt[3] \end_layout \begin_layout LyX-Code _minute = dt[4] \end_layout \begin_layout LyX-Code _second = dt[5] \end_layout \begin_layout LyX-Code _weekday = dt[6] \end_layout \begin_layout LyX-Code _dayOfYear = dt[7] \end_layout \begin_layout LyX-Code _dst = dt[8] \end_layout \begin_layout LyX-Code response._today = today \end_layout \begin_layout LyX-Code return request, response \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code @adapt \end_layout \begin_layout LyX-Code def soap_getDate(self, ps, **kw): \end_layout \begin_layout LyX-Code # Call the generated base class method to get appropriate \end_layout \begin_layout LyX-Code # input/output data structures \end_layout \begin_layout LyX-Code request, response = simple_Date_Service.soap_getDate(self, ps, **kw) \end_layout \begin_layout LyX-Code date = request._someday \end_layout \begin_layout LyX-Code offset = request._offset \end_layout \begin_layout LyX-Code if not offset: \end_layout \begin_layout LyX-Code offset = 0 \end_layout \begin_layout LyX-Code if not date: \end_layout \begin_layout LyX-Code raise RuntimeError("missing input data") \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # actual worker code, add give offset to given input date \end_layout \begin_layout LyX-Code sec = 3600 * 24 # seconds/hour * 24h \end_layout \begin_layout LyX-Code providedDate_tuple = (date._year, date._month, date._day, \end_layout \begin_layout LyX-Code date._hour, date._minute, date._second, \end_layout \begin_layout LyX-Code date._weekday, date._dayOfYear, date._dst) \end_layout \begin_layout LyX-Code providedDate_sec = time.mktime(providedDate_tuple) \end_layout \begin_layout LyX-Code offset_sec = sec * offset \end_layout \begin_layout LyX-Code newDate_sec = providedDate_sec + offset_sec \end_layout \begin_layout LyX-Code newDate_tuple = time.localtime(newDate_sec) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Use a structurally equivalent (to the WSDL complex type structure) \end_layout \begin_layout LyX-Code # python class object \end_layout \begin_layout LyX-Code class day: \end_layout \begin_layout LyX-Code _year = newDate_tuple[0] \end_layout \begin_layout LyX-Code _month = newDate_tuple[1] \end_layout \begin_layout LyX-Code _day = newDate_tuple[2] \end_layout \begin_layout LyX-Code _hour = newDate_tuple[3] \end_layout \begin_layout LyX-Code _minute = newDate_tuple[4] \end_layout \begin_layout LyX-Code _second = newDate_tuple[5] \end_layout \begin_layout LyX-Code _weekday = newDate_tuple[6] \end_layout \begin_layout LyX-Code _dayOfYear = newDate_tuple[7] \end_layout \begin_layout LyX-Code _dst = newDate_tuple[8] \end_layout \begin_layout LyX-Code response._day = day \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code return request, response \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code op = OptionParser(usage="%prog [options]") \end_layout \begin_layout LyX-Code op.add_option("-l", "--loglevel", help="loglevel (DEBUG, WARN)", \end_layout \begin_layout LyX-Code metavar="LOGLEVEL") \end_layout \begin_layout LyX-Code op.add_option("-p", "--port", help="HTTP port", \end_layout \begin_layout LyX-Code metavar="PORT", default=8080, type="int") \end_layout \begin_layout LyX-Code options, args = op.parse_args() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # set the loglevel according to cmd line arg \end_layout \begin_layout LyX-Code if options.loglevel: \end_layout \begin_layout LyX-Code loglevel = eval(options.loglevel, logging.__dict__) \end_layout \begin_layout LyX-Code logger = logging.getLogger("") \end_layout \begin_layout LyX-Code logger.setLevel(loglevel) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Run the server with a given list services \end_layout \begin_layout LyX-Code AsServer(port=options.port, services=[MyDateService(),]) \end_layout \begin_layout Standard Now, this code will actually work with \emph on both \emph default ZSI 2.0 and 2.1, as it provides a ZSI-2.1-dummy for the \family typewriter @adapt \family default -decoration. However, as we have seen in the SquareService example, it is very feasible to manually make the changes needed for the different library versions, without depending on the \begin_inset Quotes eld \end_inset magic \begin_inset Quotes erd \end_inset \family typewriter service_adaptor \family default provides. \end_layout \begin_layout Standard Hereīs the implementation of the \family typewriter service_adaptor \family default module (you can skip this section if you're running 2.1 anyway or if you are not interested in the inner workings of this \begin_inset Foot status open \begin_layout Standard You might want to look at the Python cookbook recipee \begin_inset ERT status open \begin_layout Standard \backslash htmladdnormallink{"Method enhancement" by Ken Seehof (http://aspn.activestate.com/ ASPN/Cookbook/Python/Recipe/81982)}{http://www.python.org/dev/peps/pep-0333/} \end_layout \end_inset for background \end_layout \end_inset ): \end_layout \begin_layout LyX-Code $ cat service_adaptor.py \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code import types \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Based on "Method enhancement" Python cookbook recipee by Ken Seehof \end_layout \begin_layout LyX-Code # (http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81982)} \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code class ServiceAdaptor21(object): \end_layout \begin_layout LyX-Code """Helper class factory to provide ZSI 2.1 code compatibility. \end_layout \begin_layout LyX-Code Takes a ZSI 2.0 service class and returns a class derived from this service \end_layout \begin_layout LyX-Code class, that uses the ZSI 2.1 soap_method signature + return value \end_layout \begin_layout LyX-Code conventions. \end_layout \begin_layout LyX-Code Can be used to make a ZSI 2.1-based service class implementation work with \end_layout \begin_layout LyX-Code the ZSI 2.0 library. \end_layout \begin_layout LyX-Code Parameters: \end_layout \begin_layout LyX-Code ZSI20ServiceClass: Service class, as imported from ZSI 2.0-generated \end_layout \begin_layout LyX-Code module code \end_layout \begin_layout LyX-Code """ \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code @staticmethod \end_layout \begin_layout LyX-Code def enhanced_method(method, replacement): \end_layout \begin_layout LyX-Code """Return a callable wrapped with a replacement callable""" \end_layout \begin_layout LyX-Code def _f(*args, **kwargs): \end_layout \begin_layout LyX-Code return replacement(method, *args, **kwargs) \end_layout \begin_layout LyX-Code _f.__name__ = method.__name__ \end_layout \begin_layout LyX-Code return _f \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code @staticmethod \end_layout \begin_layout LyX-Code def _adaptTo21_soap_(orig_method, self, ps, **kwargs): \end_layout \begin_layout LyX-Code """Wrapper method implementation, ZSI 2.0 soap_ method to ZSI 2.1 soap_ \end_layout \begin_layout LyX-Code method interface \end_layout \begin_layout LyX-Code """ \end_layout \begin_layout LyX-Code response = orig_method(self, ps) \end_layout \begin_layout LyX-Code return self.request, response \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code @staticmethod \end_layout \begin_layout LyX-Code def _adaptTo20_soap_(orig_method, self, ps, **kwargs): \end_layout \begin_layout LyX-Code """Wrapper method implementation, ZSI 2.1 soap_ method to ZSI 2.0 soap_ \end_layout \begin_layout LyX-Code method interface \end_layout \begin_layout LyX-Code """ \end_layout \begin_layout LyX-Code request, response = orig_method(self, ps) \end_layout \begin_layout LyX-Code return response \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code @staticmethod \end_layout \begin_layout LyX-Code def adapt(method): \end_layout \begin_layout LyX-Code """Method decorator to decorate ZSI 2.1-conforming soap_ method to ZSI \end_layout \begin_layout LyX-Code 2.0 method interface \end_layout \begin_layout LyX-Code """ \end_layout \begin_layout LyX-Code return ServiceAdaptor21.enhanced_method( \end_layout \begin_layout LyX-Code method, ServiceAdaptor21._adaptTo20_soap_) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def __new__(cls, ZSI20ServiceClass): \end_layout \begin_layout LyX-Code derived_dict = {} \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code for methodName in ZSI20ServiceClass.__dict__: \end_layout \begin_layout LyX-Code if methodName.startswith("soap_"): \end_layout \begin_layout LyX-Code method = ZSI20ServiceClass.__dict__[methodName] \end_layout \begin_layout LyX-Code derived_dict[methodName] = cls.enhanced_method( \end_layout \begin_layout LyX-Code method, cls._adaptTo21_soap_) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code return types.ClassType(ZSI20ServiceClass.__name__, \end_layout \begin_layout LyX-Code bases=(ZSI20ServiceClass,), dict=derived_dict) \end_layout \begin_layout Subsection A Python ZSI client for the DateService \begin_inset LatexCommand \label{sub:A-Python-ZSI-client-DateService} \end_inset \end_layout \begin_layout Subsubsection Using generated type mapping \end_layout \begin_layout Standard As seen before, we use the ZSI-generated code to write a client that calls both service methods: \end_layout \begin_layout Paragraph ZSI 2.1 \end_layout \begin_layout LyX-Code #! /apps/pydev/hjoukl/bin/python2.4 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # *** ZSI 2.1 *** \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code import sys, time \end_layout \begin_layout LyX-Code from optparse import OptionParser \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code from ZSI.version import Version as zsiversion \end_layout \begin_layout LyX-Code print "*** ZSI version %s ***" % (zsiversion,) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code from DateService_client import * \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def main(): \end_layout \begin_layout LyX-Code op = OptionParser(usage="%prog [options]") \end_layout \begin_layout LyX-Code op.add_option("-u", "--url", help="service URL", metavar="URL") \end_layout \begin_layout LyX-Code op.add_option("-i", "--input", type="string", \end_layout \begin_layout LyX-Code help="input string for getCurrentDate WS method", \end_layout \begin_layout LyX-Code metavar="INPUT") \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code options, args = op.parse_args() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code loc = simple_Date_ServiceLocator() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code service = loc.getDateService_Port(url=options.url, tracefile=sys.stdout) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code while 1: \end_layout \begin_layout LyX-Code offset = raw_input("Enter offset as int [0]: ") \end_layout \begin_layout LyX-Code try: \end_layout \begin_layout LyX-Code offset = int(offset) \end_layout \begin_layout LyX-Code except ValueError: \end_layout \begin_layout LyX-Code offset = 0 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code currentRequest = getCurrentDateRequest() \end_layout \begin_layout LyX-Code currentRequest._input = options.input \end_layout \begin_layout LyX-Code currentResponse = service.getCurrentDate(currentRequest) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # We use the current date as input to getDate \end_layout \begin_layout LyX-Code dateRequest = getDateRequest(offset=offset, someday=currentResponse._toda y) \end_layout \begin_layout LyX-Code response = service.getDate(dateRequest) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code print ' \backslash n \backslash nRESULT' \end_layout \begin_layout LyX-Code print '%10s = %s' % ('today', make_asctime(currentResponse._today)) \end_layout \begin_layout LyX-Code print '%6s + %d = %s' % ('today', dateRequest._offset, make_asctime(respo nse._day)) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # just a helper \end_layout \begin_layout LyX-Code def make_asctime(date_object): \end_layout \begin_layout LyX-Code timeTuple = (date_object._year, date_object._month, date_object._day, \end_layout \begin_layout LyX-Code date_object._hour, date_object._minute, date_object._second, \end_layout \begin_layout LyX-Code date_object._weekday, date_object._dayOfYear, date_object._dst \end_layout \begin_layout LyX-Code ) \end_layout \begin_layout LyX-Code return time.asctime(timeTuple) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if __name__ == '__main__': \end_layout \begin_layout LyX-Code main() \end_layout \begin_layout Standard As can be seen, ZSI provides us with the \family typewriter getCurrentDateRequest \family default and \family typewriter getDateRequest \family default classes. These handle the serialization transparently and we use them to set the argument values. Then, we hand them into the corresponding methods of the service port object retrieved with the \family typewriter simple_Date_ServiceLocator\SpecialChar \- .getDate\SpecialChar \- Service\SpecialChar \- _Port() \family default method. \end_layout \begin_layout Standard Running this client gives the following output: \end_layout \begin_layout LyX-Code $ ./myDateClient.py -u "http://adevp02:8080/DateService?wsdl" -i HALLO \end_layout \begin_layout LyX-Code *** ZSI version (2, 1, 0) *** \end_layout \begin_layout LyX-Code Enter offset as int [0]: 0 \end_layout \begin_layout LyX-Code _________________________________ Thu Jan 3 08:40:56 2008 REQUEST: \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code HALLO \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code _________________________________ Thu Jan 3 08:40:56 2008 RESPONSE: \end_layout \begin_layout LyX-Code 200 \end_layout \begin_layout LyX-Code OK \end_layout \begin_layout LyX-Code ------- \end_layout \begin_layout LyX-Code Server: ZSI/1.1 BaseHTTP/0.3 Python/2.4.4 \end_layout \begin_layout LyX-Code Date: Thu, 03 Jan 2008 07:40:56 GMT \end_layout \begin_layout LyX-Code Content-type: text/xml; charset="utf-8" \end_layout \begin_layout LyX-Code Content-Length: 627 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code 200813 \end_layout \begin_layout LyX-Code 84056 \end_layout \begin_layout LyX-Code 330 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code _________________________________ Thu Jan 3 08:40:56 2008 REQUEST: \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code 0 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code 200813 \end_layout \begin_layout LyX-Code 84056 \end_layout \begin_layout LyX-Code 330 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code _________________________________ Thu Jan 3 08:40:56 2008 RESPONSE: \end_layout \begin_layout LyX-Code 200 \end_layout \begin_layout LyX-Code OK \end_layout \begin_layout LyX-Code ------- \end_layout \begin_layout LyX-Code Server: ZSI/1.1 BaseHTTP/0.3 Python/2.4.4 \end_layout \begin_layout LyX-Code Date: Thu, 03 Jan 2008 07:40:56 GMT \end_layout \begin_layout LyX-Code Content-type: text/xml; charset="utf-8" \end_layout \begin_layout LyX-Code Content-Length: 609 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code 200813 \end_layout \begin_layout LyX-Code 84056 \end_layout \begin_layout LyX-Code 330 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code RESULT \end_layout \begin_layout LyX-Code today = Thu Jan 3 08:40:56 2008 \end_layout \begin_layout LyX-Code today + 0 = Thu Jan 3 08:40:56 2008 \end_layout \begin_layout LyX-Code Enter offset as int [0]: ^C \end_layout \begin_layout Standard Note that we gave an explicit URL of \begin_inset Quotes eld \end_inset http://adevp02:8080/DateService?wsdl \begin_inset Quotes erd \end_inset . ZSI transparently uses the relevant part of this URL for service invocation, silently ignoring the \begin_inset Quotes eld \end_inset ?wsdl \begin_inset Quotes erd \end_inset suffix. \end_layout \begin_layout Paragraph ZSI 2.0 \end_layout \begin_layout LyX-Code #! /apps/pydev/bin/python2.4 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # *** ZSI 2.0 *** \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code import sys, time \end_layout \begin_layout LyX-Code from copy import copy \end_layout \begin_layout LyX-Code from optparse import OptionParser \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code from ZSI.version import Version as zsiversion \end_layout \begin_layout LyX-Code print "*** ZSI version %s ***" % (zsiversion,) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code from DateService_services import * \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def main(): \end_layout \begin_layout LyX-Code op = OptionParser(usage="%prog [options]") \end_layout \begin_layout LyX-Code op.add_option("-u", "--url", help="service URL", metavar="URL") \end_layout \begin_layout LyX-Code op.add_option("-i", "--input", type="string", \end_layout \begin_layout LyX-Code help="input string for getCurrentDate WS method", \end_layout \begin_layout LyX-Code metavar="INPUT") \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code options, args = op.parse_args() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code loc = simple_Date_ServiceLocator() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code service = loc.getDateService_PortType(url=options.url, tracefile=sys.stdout) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code while 1: \end_layout \begin_layout LyX-Code offset = raw_input("Enter offset as int [0]: ") \end_layout \begin_layout LyX-Code try: \end_layout \begin_layout LyX-Code offset = int(offset) \end_layout \begin_layout LyX-Code except ValueError: \end_layout \begin_layout LyX-Code offset = 0 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code currentRequest = getCurrentDateRequest() \end_layout \begin_layout LyX-Code currentRequest._input = options.input \end_layout \begin_layout LyX-Code currentResponse = service.getCurrentDate(currentRequest) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # We use the current date as input to getDate \end_layout \begin_layout LyX-Code dateRequest = getDateRequest() \end_layout \begin_layout LyX-Code dateRequest._offset = offset \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Workaround for ZSI 2.0 bug #1755740: \end_layout \begin_layout LyX-Code # Multiple calls to method _get_type_or_substitute erroneously changes \end_layout \begin_layout LyX-Code # attributes of a substitute typecode without making a (shallow) copy \end_layout \begin_layout LyX-Code currentResponse._today.typecode = copy(currentResponse._today.typecode) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code dateRequest._someday = currentResponse._today \end_layout \begin_layout LyX-Code response = service.getDate(dateRequest) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code print ' \backslash n \backslash nRESULT' \end_layout \begin_layout LyX-Code print '%10s = %s' % ('today', make_asctime(currentResponse._today)) \end_layout \begin_layout LyX-Code print '%6s + %d = %s' % ('today', dateRequest._offset, make_asctime(respo nse._day)) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # just a helper \end_layout \begin_layout LyX-Code def make_asctime(date_object): \end_layout \begin_layout LyX-Code timeTuple = (date_object._year, date_object._month, date_object._day, \end_layout \begin_layout LyX-Code date_object._hour, date_object._minute, date_object._second, \end_layout \begin_layout LyX-Code date_object._weekday, date_object._dayOfYear, date_object._dst \end_layout \begin_layout LyX-Code ) \end_layout \begin_layout LyX-Code return time.asctime(timeTuple) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if __name__ == '__main__': \end_layout \begin_layout LyX-Code main() \end_layout \begin_layout Standard Again the ZSI 2.0 implementation differs only in details: \end_layout \begin_layout Itemize import the generated code from \family typewriter DateService_services \family default (rather than \family typewriter DateService_client \family default in ZSI 2.1) \end_layout \begin_layout Itemize retrieve the service binding through the \family typewriter simple_Date_ServiceLocator.getDateService_PortType() \family default method (rather than \family typewriter simple_Date_ServiceLocator.getDateService_Port() \family default in ZSI 2.1 \end_layout \begin_layout Itemize instantiate the \family typewriter getDateRequest \family default structure and then populate it (as opposed to using keyword args in ZSI 2.1) \end_layout \begin_layout Standard Unfortunately, ZSI 2.0 has a bug that bites us so we need to use a workaround here (see code comments). \end_layout \begin_layout Subsubsection Using a ServiceProxy client \end_layout \begin_layout Standard Alternatively, we can implement a client with the ServiceProxy class without using the generated code. This version works for both ZSI 2.0 & 2.1: \end_layout \begin_layout LyX-Code # *** ZSI 2.0 & 2.1 *** \end_layout \begin_layout LyX-Code import sys, time \end_layout \begin_layout LyX-Code from optparse import OptionParser \end_layout \begin_layout LyX-Code from ZSI.version import Version as zsiversion \end_layout \begin_layout LyX-Code print "*** ZSI version %s ***" % (zsiversion,) \end_layout \begin_layout LyX-Code from ZSI.ServiceProxy import ServiceProxy \end_layout \begin_layout LyX-Code op = OptionParser(usage="%prog [options]") \end_layout \begin_layout LyX-Code op.add_option("-u", "--url", help="service URL (mandatory)", metavar="URL") \end_layout \begin_layout LyX-Code op.add_option("-i", "--input", type="string", \end_layout \begin_layout LyX-Code help="input string for getCurrentDate WS method", \end_layout \begin_layout LyX-Code metavar="INPUT") \end_layout \begin_layout LyX-Code # just a helper \end_layout \begin_layout LyX-Code def make_asctime(date_dict): \end_layout \begin_layout LyX-Code timeTuple = (date_dict["year"], date_dict["month"], date_dict["day"], \end_layout \begin_layout LyX-Code date_dict["hour"], date_dict["minute"], date_dict["second"], \end_layout \begin_layout LyX-Code date_dict["weekday"], date_dict["dayOfYear"], \end_layout \begin_layout LyX-Code date_dict["dst"]) \end_layout \begin_layout LyX-Code return time.asctime(timeTuple) \end_layout \begin_layout LyX-Code options, args = op.parse_args() \end_layout \begin_layout LyX-Code if not options.url: \end_layout \begin_layout LyX-Code op.print_help() \end_layout \begin_layout LyX-Code sys.exit(1) \end_layout \begin_layout LyX-Code else: \end_layout \begin_layout LyX-Code # "Canonicalize" WSDL URL \end_layout \begin_layout LyX-Code options.url = options.url.replace("?WSDL", "?wsdl") \end_layout \begin_layout LyX-Code if not options.url.endswith("?wsdl"): \end_layout \begin_layout LyX-Code options.url += "?wsdl" \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code service = ServiceProxy(wsdl=options.url, tracefile=sys.stdout) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code print ' \backslash nAccessing service DateService...' \end_layout \begin_layout LyX-Code while 1: \end_layout \begin_layout LyX-Code offset = raw_input("Enter offset as int [0]: ") \end_layout \begin_layout LyX-Code try: \end_layout \begin_layout LyX-Code offset = int(offset) \end_layout \begin_layout LyX-Code except ValueError: \end_layout \begin_layout LyX-Code offset = 0 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code currentResponse = service.getCurrentDate(input=options.input) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # We get back a dictionaray \end_layout \begin_layout LyX-Code print "currentResponse:", currentResponse \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Manually fill the argument dict for demonstration purposes only \end_layout \begin_layout LyX-Code somedayDict = { \end_layout \begin_layout LyX-Code 'year': currentResponse['today']['year'], \end_layout \begin_layout LyX-Code 'month': currentResponse['today']['month'], \end_layout \begin_layout LyX-Code 'day': currentResponse['today']['day'], \end_layout \begin_layout LyX-Code 'hour': currentResponse['today']['hour'], \end_layout \begin_layout LyX-Code 'minute': currentResponse['today']['minute'], \end_layout \begin_layout LyX-Code 'second': currentResponse['today']['second'], \end_layout \begin_layout LyX-Code 'weekday': currentResponse['today']['weekday'], \end_layout \begin_layout LyX-Code 'dayOfYear': currentResponse['today']['dayOfYear'], \end_layout \begin_layout LyX-Code 'dst': currentResponse['today']['dst'], \end_layout \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # We use the current date as input to getDate \end_layout \begin_layout LyX-Code response = service.getDate(offset=offset, \end_layout \begin_layout LyX-Code someday=currentResponse["today"]) \end_layout \begin_layout LyX-Code responseFromDict = service.getDate(offset=offset, someday=somedayDict) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code print ' \backslash n \backslash nRESULT' \end_layout \begin_layout LyX-Code print '%10s = %s' % ('today', make_asctime(currentResponse["today"])) \end_layout \begin_layout LyX-Code print '%6s + %d = %s' % ('today', offset, make_asctime(response["day"])) \end_layout \begin_layout Standard This is the output of a sample client sesssion of the ServiceProxy solution: \end_layout \begin_layout LyX-Code $ /apps/pydev/bin/python2.4 ./myDateSPclient.py -u "http://adevp02:8080/DateService ?wsdl" -i HALLO \end_layout \begin_layout LyX-Code *** ZSI version (2, 0, 0) *** \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code Accessing service DateService... \end_layout \begin_layout LyX-Code Enter offset as int [0]: 1 \end_layout \begin_layout LyX-Code _________________________________ Wed Jan 2 17:46:50 2008 REQUEST: \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code HALLO \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code _________________________________ Wed Jan 2 17:46:50 2008 RESPONSE: \end_layout \begin_layout LyX-Code 200 \end_layout \begin_layout LyX-Code OK \end_layout \begin_layout LyX-Code ------- \end_layout \begin_layout LyX-Code Server: ZSI/1.1 BaseHTTP/0.3 Python/2.4.4 \end_layout \begin_layout LyX-Code Date: Wed, 02 Jan 2008 16:46:50 GMT \end_layout \begin_layout LyX-Code Content-type: text/xml; charset="utf-8" \end_layout \begin_layout LyX-Code Content-Length: 628 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code 2008 \end_layout \begin_layout LyX-Code 1 \end_layout \begin_layout LyX-Code 2174650 \end_layout \begin_layout LyX-Code 220 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code currentResponse: {'today': {'second': 50, 'dayOfYear': 2, 'day': 2, 'month': 1, 'minute': 46, 'dst': 0, 'weekday': 2, 'hour': 17, 'year': 2008}} \end_layout \begin_layout LyX-Code _________________________________ Wed Jan 2 17:46:50 2008 REQUEST: \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code 1 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code 200812 \end_layout \begin_layout LyX-Code 174650 \end_layout \begin_layout LyX-Code 220 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code _________________________________ Wed Jan 2 17:46:50 2008 RESPONSE: \end_layout \begin_layout LyX-Code 200 \end_layout \begin_layout LyX-Code OK \end_layout \begin_layout LyX-Code ------- \end_layout \begin_layout LyX-Code Server: ZSI/1.1 BaseHTTP/0.3 Python/2.4.4 \end_layout \begin_layout LyX-Code Date: Wed, 02 Jan 2008 16:46:50 GMT \end_layout \begin_layout LyX-Code Content-type: text/xml; charset="utf-8" \end_layout \begin_layout LyX-Code Content-Length: 610 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code 200813 \end_layout \begin_layout LyX-Code 174650 \end_layout \begin_layout LyX-Code 330 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code _________________________________ Wed Jan 2 17:46:50 2008 REQUEST: \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code 1 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code 200812 \end_layout \begin_layout LyX-Code 174650 \end_layout \begin_layout LyX-Code 220 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code _________________________________ Wed Jan 2 17:46:50 2008 RESPONSE: \end_layout \begin_layout LyX-Code 200 \end_layout \begin_layout LyX-Code OK \end_layout \begin_layout LyX-Code ------- \end_layout \begin_layout LyX-Code Server: ZSI/1.1 BaseHTTP/0.3 Python/2.4.4 \end_layout \begin_layout LyX-Code Date: Wed, 02 Jan 2008 16:46:50 GMT \end_layout \begin_layout LyX-Code Content-type: text/xml; charset="utf-8" \end_layout \begin_layout LyX-Code Content-Length: 610 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code 200813 \end_layout \begin_layout LyX-Code 174650 \end_layout \begin_layout LyX-Code 330 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code RESULT \end_layout \begin_layout LyX-Code today = Wed Jan 2 17:46:50 2008 \end_layout \begin_layout LyX-Code today + 1 = Thu Jan 3 17:46:50 2008 \end_layout \begin_layout LyX-Code Enter offset as int [0]: \end_layout \begin_layout Standard Comments: \end_layout \begin_layout Itemize We have hinted so and now this becomes important: For ZSI 2.1, the presented code will only work if it is run from a directory that \emph on does not \emph default contain \family typewriter wsdl2py \family default -generated code, see section \begin_inset LatexCommand \ref{sub:SquareService-ZSI-ServiceProxy} \end_inset . This is because the ServiceProxy cache code, as opposed to the \family typewriter wsdl2py \family default -generated code, does not use \begin_inset Quotes eld \end_inset ZSI aname modification \begin_inset Quotes erd \end_inset . \begin_inset Foot status open \begin_layout Standard So one module will e.g. expect names not to start with \begin_inset Quotes eld \end_inset _ \begin_inset Quotes erd \end_inset while the other one will. \end_layout \end_inset This is not a problem for ZSI 2.0 as it uses different cache module naming. We consider this a bug in ZSI 2.1alpha. \end_layout \begin_layout Itemize For demonstration purposes only, the \family typewriter getDate() \family default method gets actually invoked twice, the second time with manually created dictionary input data. \end_layout \begin_layout Subsection A gSOAP C++ client for the DateService \end_layout \begin_layout Subsubsection Code generation from WSDL \begin_inset LatexCommand \label{sub:Code-generation-gsoap_DateService} \end_inset \end_layout \begin_layout Enumerate Header: \end_layout \begin_deeper \begin_layout LyX-Code $ /apps/pydev/bin/wsdl2h -o dateService.h http://adevp02:8080/DateService?wsdl \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code ** The gSOAP WSDL parser for C and C++ 1.2.9l \end_layout \begin_layout LyX-Code ** Copyright (C) 2000-2007 Robert van Engelen, Genivia Inc. \end_layout \begin_layout LyX-Code ** All Rights Reserved. This product is provided "as is", without any warranty. \end_layout \begin_layout LyX-Code ** The gSOAP WSDL parser is released under one of the following two licenses: \end_layout \begin_layout LyX-Code ** GPL or the commercial license by Genivia Inc. Use option -l for more info. \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code Saving dateService.h \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code Cannot open file 'typemap.dat' \end_layout \begin_layout LyX-Code Problem reading type map file typemap.dat. \end_layout \begin_layout LyX-Code Using internal type definitions for C++ instead. \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code Connecting to 'http://adevp02:8080/DateService?wsdl' to retrieve WSDL/XSD... connected, receiving... \end_layout \begin_layout LyX-Code Warning: part 'today' uses literal style and should refer to an element rather than a type \end_layout \begin_layout LyX-Code Warning: part 'input' uses literal style and should refer to an element rather than a type \end_layout \begin_layout LyX-Code Warning: part 'input' uses literal style and should refer to an element rather than a type \end_layout \begin_layout LyX-Code Warning: part 'input' uses literal style and should refer to an element rather than a type \end_layout \begin_layout LyX-Code Warning: part 'day' uses literal style and should refer to an element rather than a type \end_layout \begin_layout LyX-Code Warning: part 'offset' uses literal style and should refer to an element rather than a type \end_layout \begin_layout LyX-Code Warning: part 'someday' uses literal style and should refer to an element rather than a type \end_layout \begin_layout LyX-Code Warning: part 'offset' uses literal style and should refer to an element rather than a type \end_layout \begin_layout LyX-Code Warning: part 'someday' uses literal style and should refer to an element rather than a type \end_layout \begin_layout LyX-Code Warning: part 'offset' uses literal style and should refer to an element rather than a type \end_layout \begin_layout LyX-Code Warning: part 'someday' uses literal style and should refer to an element rather than a type \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code To complete the process, compile with: \end_layout \begin_layout LyX-Code soapcpp2 dateService.h \end_layout \begin_layout Standard gSOAP allows you to modify code generation e.g. with regard to schema namespace bindings (names of prefixes, \SpecialChar \ldots{} ) or type mappings. Take a look at the comments in the generated \family typewriter dateService.h \family default for instructions. \end_layout \end_deeper \begin_layout Enumerate Stub code: \end_layout \begin_deeper \begin_layout LyX-Code $ /apps/pydev/bin/soapcpp2 -I /data/pydev/DOWNLOADS/WebServices/C++/gsoap-2.7/soa pcpp2/import dateService.h \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code ** The gSOAP Stub and Skeleton Compiler for C and C++ 2.7.9l \end_layout \begin_layout LyX-Code ** Copyright (C) 2000-2007, Robert van Engelen, Genivia Inc. \end_layout \begin_layout LyX-Code ** All Rights Reserved. This product is provided "as is", without any warranty. \end_layout \begin_layout LyX-Code ** The gSOAP compiler is released under one of the following three licenses: \end_layout \begin_layout LyX-Code ** GPL, the gSOAP public license, or the commercial license by Genivia Inc. \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code Saving soapStub.h \end_layout \begin_layout LyX-Code Saving soapH.h \end_layout \begin_layout LyX-Code Saving soapC.cpp \end_layout \begin_layout LyX-Code Saving soapClient.cpp \end_layout \begin_layout LyX-Code Saving soapClientLib.cpp \end_layout \begin_layout LyX-Code Saving soapServer.cpp \end_layout \begin_layout LyX-Code Saving soapServerLib.cpp \end_layout \begin_layout LyX-Code Using ns3 service name: DateService_USCOREBinding \end_layout \begin_layout LyX-Code Using ns3 service style: document \end_layout \begin_layout LyX-Code Using ns3 service encoding: literal \end_layout \begin_layout LyX-Code Using ns3 service location: http://adevp02:8080/DateService \end_layout \begin_layout LyX-Code Using ns3 schema namespace: urn:DateService.wsdl \end_layout \begin_layout LyX-Code Saving soapDateService_USCOREBindingProxy.h client proxy \end_layout \begin_layout LyX-Code Saving soapDateService_USCOREBindingObject.h server object \end_layout \begin_layout LyX-Code Saving DateService_USCOREBinding.getCurrentDate.req.xml sample SOAP/XML request \end_layout \begin_layout LyX-Code Saving DateService_USCOREBinding.getCurrentDate.res.xml sample SOAP/XML response \end_layout \begin_layout LyX-Code Saving DateService_USCOREBinding.getDate.req.xml sample SOAP/XML request \end_layout \begin_layout LyX-Code Saving DateService_USCOREBinding.getDate.res.xml sample SOAP/XML response \end_layout \begin_layout LyX-Code Saving DateService_USCOREBinding.nsmap namespace mapping table \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code Compilation successful \end_layout \end_deeper \begin_layout Subsubsection Client implementation \end_layout \begin_layout Standard As already announced in section \begin_inset LatexCommand \ref{sub:gsoap-square-client} \end_inset weīll now access the service a bit more elegantly through the \family typewriter DateService_USCOREBinding \family default proxy class, which can be found in \family typewriter soapDateService_USCOREBindingProxy.h \family default . We use it to call the two WS-methods in our sample client: \end_layout \begin_layout LyX-Code // Contents of file "myclient_use_proxy.cpp" \end_layout \begin_layout LyX-Code //#include "soapH.h"; \end_layout \begin_layout LyX-Code #include "soapDateService_USCOREBindingProxy.h" \end_layout \begin_layout LyX-Code #include "DateService_USCOREBinding.nsmap" \end_layout \begin_layout LyX-Code int main() \end_layout \begin_layout LyX-Code { \end_layout \begin_layout LyX-Code DateService_USCOREBinding ds; \end_layout \begin_layout LyX-Code ns2__Date *today, *someday; \end_layout \begin_layout LyX-Code ns3__getCurrentDateResponse today_response; \end_layout \begin_layout LyX-Code ns3__getDateResponse someday_response; \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code std::string text, input; \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code text="TEST"; \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code std::cout << "(1) Calling 'getCurrentDate()'- Web Service method:" << std::endl; \end_layout \begin_layout LyX-Code if(ds.ns3__getCurrentDate(text, today_response) == SOAP_OK) \end_layout \begin_layout LyX-Code { \end_layout \begin_layout LyX-Code today = today_response.today; \end_layout \begin_layout LyX-Code std::cout << " \backslash nCurrent date:" << std::endl; \end_layout \begin_layout LyX-Code std::cout << " \backslash tyear: " << *today->year << std::endl; \end_layout \begin_layout LyX-Code std::cout << " \backslash tmonth: " << *today->month << std::endl; \end_layout \begin_layout LyX-Code std::cout << " \backslash tday: " << *today->day << std::endl; \end_layout \begin_layout LyX-Code std::cout << " \backslash thour: " << *today->hour << std::endl; \end_layout \begin_layout LyX-Code std::cout << " \backslash tminute: " << *today->minute << std::endl; \end_layout \begin_layout LyX-Code std::cout << " \backslash tsecond: " << *today->second << std::endl; \end_layout \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code else \end_layout \begin_layout LyX-Code soap_print_fault(ds.soap, stderr); \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code std::cout << " \backslash n(2) Calling 'getDate()'- Web Service method:" << std::endl; \end_layout \begin_layout LyX-Code std::cout << " \backslash n(2)Please enter an integer for the 'offset'"<< std::endl; \end_layout \begin_layout LyX-Code std::cout << " \backslash toffset = "; \end_layout \begin_layout LyX-Code std::cin >> input; \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code someday = today; \end_layout \begin_layout LyX-Code if(ds.ns3__getDate(input, someday, someday_response) == SOAP_OK) \end_layout \begin_layout LyX-Code { \end_layout \begin_layout LyX-Code someday = someday_response.day; \end_layout \begin_layout LyX-Code std::cout << " \backslash nSome Day:" << std::endl; \end_layout \begin_layout LyX-Code std::cout << " \backslash tyear: " << *someday->year << std::endl; \end_layout \begin_layout LyX-Code std::cout << " \backslash tmonth: "<< *someday->month << std::endl; \end_layout \begin_layout LyX-Code std::cout << " \backslash tday: " << *someday->day << std::endl; \end_layout \begin_layout LyX-Code std::cout << " \backslash thour: " << *today->hour << std::endl; \end_layout \begin_layout LyX-Code std::cout << " \backslash tminute: " << *today->minute << std::endl; \end_layout \begin_layout LyX-Code std::cout << " \backslash tsecond: " << *today->second << std::endl; \end_layout \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code else \end_layout \begin_layout LyX-Code return 0; \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code \end_layout \begin_layout Standard Comments: \end_layout \begin_layout Itemize You can find the classes/structs that represent the WSDL input and output datatypes in the generated file \family typewriter soapStub.h \family default . This file is really pretty much self-explaining, so the usage in client implementations should be straightforward. \end_layout \begin_layout Subsubsection gSOAP Client compilation \end_layout \begin_layout Paragraph gcc 2.95.2 \end_layout \begin_layout LyX-Code $ g++ -o myClient_use_proxy -I/apps/pydev/include -L/apps/pydev/lib \end_layout \begin_layout LyX-Code -R /apps/prod/lib myClient_use_proxy.cpp soapC.cpp soapClient.cpp \end_layout \begin_layout LyX-Code -lsocket -lgsoap++ \end_layout \begin_layout Paragraph gcc 3.4.4 \end_layout \begin_layout LyX-Code $ /apps/local/gcc/3.4.4/bin/g++ -o myClient_use_proxy -R /apps/prod/gcc/3.4.4/lib -I/apps/pydev/gcc/3.4.4/include -L/apps/pydev/gcc/3.4.4/lib soapC.cpp soapClient.cpp myClient_use_proxy.cpp -lgsoap++ -lsocket -lnsl \end_layout \begin_layout Standard Running the client gives the following output: \end_layout \begin_layout LyX-Code $ ./myClient_use_proxy \end_layout \begin_layout LyX-Code (1) Calling 'getCurrentDate()'- Web Service method: \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code Current date: \end_layout \begin_layout LyX-Code year: 2008 \end_layout \begin_layout LyX-Code month: 1 \end_layout \begin_layout LyX-Code day: 8 \end_layout \begin_layout LyX-Code hour: 17 \end_layout \begin_layout LyX-Code minute: 13 \end_layout \begin_layout LyX-Code second: 30 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code (2) Calling 'getDate()'- Web Service method: \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code (2)Please enter an integer for the 'offset' \end_layout \begin_layout LyX-Code offset = 88 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code Some Day: \end_layout \begin_layout LyX-Code year: 2008 \end_layout \begin_layout LyX-Code month: 4 \end_layout \begin_layout LyX-Code day: 5 \end_layout \begin_layout LyX-Code hour: 17 \end_layout \begin_layout LyX-Code minute: 13 \end_layout \begin_layout LyX-Code second: 30 \end_layout \begin_layout LyX-Code \end_layout \begin_layout Section A document/literal service: The FinancialService \begin_inset LatexCommand \label{sec:FinancialService-document/literal-service} \end_inset \end_layout \begin_layout Standard The previous example services had in common that they used rpc/literal binding. While this is WS-I-compliant, there is a tendency to propagate the usage of the document/literal fashion. With rpc/literal, you might define types or elements to be used in the request and response messages, in the \family typewriter \family default section. We did that for the DateService, see section \begin_inset LatexCommand \ref{sub:The-DateService-WSDL} \end_inset . You use these elements or types as arguments or receive them as return value of the service call. They are \begin_inset Quotes eld \end_inset packed \begin_inset Quotes erd \end_inset into the request or the response tags in the actual SOAP message, as can be seen in the sample client output in \begin_inset LatexCommand \ref{sub:A-Python-ZSI-client-DateService} \end_inset . \end_layout \begin_layout Standard The difference with document/literal binding is that the \emph on complete \emph default message inside \family typewriter \family default is described in the \family typewriter \family default section, as an XML schema - not only the call parameters/return values. The big advantage is obvious: Such a message can be XML Schema-validated. \end_layout \begin_layout Standard As it is quite possible with document/literal to leave the service method name out of the request/response message at all, depending on your \family typewriter \family default definitions, there can also be a downside to this. Because if you do so, it might be difficult for the server implementation to dispatch correctly. \begin_inset Foot status open \begin_layout Standard At least if dispatching does not rely on the SOAPAction header field. \end_layout \end_inset To avoid this, we explicitly model the method name into our request, as a toplevel element. This is called \begin_inset Quotes eld \end_inset document-wrapped \begin_inset Quotes erd \end_inset style. \end_layout \begin_layout Standard Most of the steps from WSDL to implementation should be familiar by now; we will thus concentrate on the doc/literal differences and mainly present the (commented) code. \end_layout \begin_layout Subsection The FinancialService WSDL \end_layout \begin_layout Standard The FinancialService is described in \family typewriter FinancialService.wsdl \family default : \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code Financial Web Service. Methods: -getPV(irate, CFSequence): \end_layout \begin_layout LyX-Code Return present value for given interest rate and Cash Flows. \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout Standard Notes: \end_layout \begin_layout Itemize The FinancialServiceīs getPV operation returns the net present value for a given interest rate \begin_inset Formula $r$ \end_inset and a series of cash flows. It uses document/literal binding, so the full ingoing and outgoing message structure is defined in the WSDL \family typewriter \family default XML Schema section. \end_layout \begin_layout Itemize The \family typewriter soapAction \family default attribute is empty ( \family typewriter \family default ). This means the server will have to use a different way to dispatch to the method implementation. \end_layout \begin_layout Itemize Note the use of \family typewriter elementFormDefault= \begin_inset Quotes erd \end_inset qualified \begin_inset Quotes erd \end_inset \family default in the XML Schema root element. This basically means that local elements (you might say sub-elements) must be namespace-qualified. The XML Schema default for this attribute is \begin_inset Quotes eld \end_inset unqualified \begin_inset Quotes erd \end_inset but we ran into interoperability problems when not setting this to \begin_inset Quotes eld \end_inset qualified \begin_inset Quotes erd \end_inset , which seems to be a ZSI 2.0/2.1alpha bug. \emph on Note: By the time of this writing, this has already been fixed with ZSI svn revision r1440. \end_layout \begin_layout Subsection A Python ZSI FinancialService server \begin_inset LatexCommand \label{sub:Python-ZSI-FinancialService-server} \end_inset \end_layout \begin_layout Subsubsection Code generation from WSDL \end_layout \begin_layout Standard We now take advantage of ZSIīs \family typewriter --complexType \family default option for code generation, to get some convenience getters, setters and factories: \end_layout \begin_layout Paragraph ZSI 2.1 \end_layout \begin_layout LyX-Code /apps/pydev/hjoukl/bin/wsdl2py --complexType FinancialService.wsdl \end_layout \begin_layout LyX-Code ==> FinancialService_client.py \end_layout \begin_layout LyX-Code ==> FinancialService_server.py \end_layout \begin_layout LyX-Code ==> FinancialService_types.py \end_layout \begin_layout Paragraph ZSI 2.0 \end_layout \begin_layout Enumerate \family typewriter wsdl2py \family default : \end_layout \begin_deeper \begin_layout LyX-Code /apps/pydev/bin/wsdl2py --complexType -f FinancialService.wsdl \end_layout \begin_layout LyX-Code ==> FinancialService_services.py \end_layout \begin_layout LyX-Code ==> FinancialService_services_types.py \end_layout \end_deeper \begin_layout Enumerate \family typewriter wsdl2dispatch: \end_layout \begin_deeper \begin_layout LyX-Code /apps/pydev/bin/wsdl2dispatch -f FinancialService.wsdl \end_layout \begin_layout LyX-Code ==> FinancialService_services_server.py \end_layout \end_deeper \begin_layout Subsubsection The FinancialService server implementation \end_layout \begin_layout Paragraph ZSI 2.0 & 2.1 \end_layout \begin_layout Standard Following the DateService example ( \begin_inset LatexCommand \ref{sub:A-Python-ZSI-DateService-server} \end_inset ), we create a server implementation that runs with both ZSI version 2.0 and 2.1: \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code from optparse import OptionParser \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Import the ZSI stuff you'd need no matter what \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code from ZSI.wstools import logging \end_layout \begin_layout LyX-Code from ZSI import ServiceContainer \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code from ZSI.version import Version as zsiversion \end_layout \begin_layout LyX-Code print "*** ZSI version %s ***" % (zsiversion,) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Import the generated Server Object \end_layout \begin_layout LyX-Code try: \end_layout \begin_layout LyX-Code # 2.1alpha \end_layout \begin_layout LyX-Code from FinancialService_server import * \end_layout \begin_layout LyX-Code # Dummy implementation \end_layout \begin_layout LyX-Code def adapt(method): \end_layout \begin_layout LyX-Code return method \end_layout \begin_layout LyX-Code except ImportError, e: \end_layout \begin_layout LyX-Code # 2.0final \end_layout \begin_layout LyX-Code from FinancialService_services_server import * \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code from service_adaptor import ServiceAdaptor21 \end_layout \begin_layout LyX-Code adapt = ServiceAdaptor21.adapt \end_layout \begin_layout LyX-Code # Wrap generated 2.0 class to 2.1 soap_ method behaviour \end_layout \begin_layout LyX-Code FinancialService = ServiceAdaptor21(FinancialService) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Create a Server implementation by inheritance \end_layout \begin_layout LyX-Code # 2.1alpha implementation \end_layout \begin_layout LyX-Code class MyFinancialService(FinancialService): \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Make WSDL available for HTTP GET \end_layout \begin_layout LyX-Code _wsdl = "".join(open("FinancialService.wsdl").readlines()) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code @adapt \end_layout \begin_layout LyX-Code def soap_getPV(self, ps, **kw): \end_layout \begin_layout LyX-Code # Call the generated base class method to get appropriate \end_layout \begin_layout LyX-Code # input/output data structures \end_layout \begin_layout LyX-Code request, response = FinancialService.soap_getPV(self, ps, **kw) \end_layout \begin_layout LyX-Code # Comment that out if need be to see what API these objects offer: \end_layout \begin_layout LyX-Code #print help(request) \end_layout \begin_layout LyX-Code #print help(response) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Using getters/setters \end_layout \begin_layout LyX-Code irate = request.get_element_irate() \end_layout \begin_layout LyX-Code CFs = request.get_element_CFSequence().get_element_CF() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Alternative way to access the input data \end_layout \begin_layout LyX-Code #irate = request._irate \end_layout \begin_layout LyX-Code #CFs = request._CFSequence._CF \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Invoke the service worker code \end_layout \begin_layout LyX-Code PV = self.getPV(irate, CFs) \end_layout \begin_layout LyX-Code #print "PV:", PV \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # assign return value to response object \end_layout \begin_layout LyX-Code response = getPVResponse(PV) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Another way to create a serializable response object \end_layout \begin_layout LyX-Code #class SimpleTypeWrapper(float): \end_layout \begin_layout LyX-Code # typecode = response.typecode \end_layout \begin_layout LyX-Code #response = SimpleTypeWrapper(PV) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code return request, response \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def getPV(self, irate, cashFlows): \end_layout \begin_layout LyX-Code # worker code method \end_layout \begin_layout LyX-Code t = 0 \end_layout \begin_layout LyX-Code PV = 0.0 \end_layout \begin_layout LyX-Code for CF in cashFlows: \end_layout \begin_layout LyX-Code PV += (CF or 0.0) * ((irate / 100.0 + 1) ** (-t)) \end_layout \begin_layout LyX-Code t += 1 \end_layout \begin_layout LyX-Code return PV \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code op = OptionParser(usage="%prog [options]") \end_layout \begin_layout LyX-Code op.add_option("-l", "--loglevel", help="loglevel (DEBUG, WARN)", \end_layout \begin_layout LyX-Code metavar="LOGLEVEL") \end_layout \begin_layout LyX-Code op.add_option("-p", "--port", help="HTTP port", \end_layout \begin_layout LyX-Code metavar="PORT", default=8080, type="int") \end_layout \begin_layout LyX-Code options, args = op.parse_args() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # set the loglevel according to cmd line arg \end_layout \begin_layout LyX-Code if options.loglevel: \end_layout \begin_layout LyX-Code loglevel = eval(options.loglevel, logging.__dict__) \end_layout \begin_layout LyX-Code logger = logging.getLogger("") \end_layout \begin_layout LyX-Code logger.setLevel(loglevel) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Run the server with a given list of services \end_layout \begin_layout LyX-Code ServiceContainer.AsServer(port=options.port, services=[MyFinancialService(),]) \end_layout \begin_layout Standard Comments: \end_layout \begin_layout Itemize The service input data is now retrieved from the \family typewriter request \family default object with \family typewriter request.get_element_() \family default getter methods. An alternative is presented in the code comments. \end_layout \begin_layout Itemize If you need information on an objects attributes and can not find it in the docs, it can be quite helpful to make use of the power of introspection, and of docstrings, e.g. putting things like \end_layout \begin_deeper \begin_layout LyX-Code print help(request) \end_layout \begin_layout LyX-Code print help(response) \end_layout \begin_layout Standard into the server code. \end_layout \end_deeper \begin_layout Itemize Remember we left out the \family typewriter soapACTION \family default field from the WSDL file (we left it empty, to be precise). So the dispatch to a serviceīs service method must now happen another way. And indeed, ZSI is capable of dispatching by making use of the name of the toplevel in-message element name. Thatīs the benefit of following the \begin_inset Quotes eld \end_inset document-wrapped \begin_inset Quotes erd \end_inset style, see above. \end_layout \begin_layout Subsection A Python ZSI client for the FinancialService \end_layout \begin_layout Standard Making use of the generated stub code, this is a client implementation that works with both ZSI 2.0 & 2.1: \end_layout \begin_layout LyX-Code import sys, time \end_layout \begin_layout LyX-Code from optparse import OptionParser \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code from ZSI.version import Version as zsiversion \end_layout \begin_layout LyX-Code print "*** ZSI version %s ***" % (zsiversion,) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code try: \end_layout \begin_layout LyX-Code # 2.1alpha \end_layout \begin_layout LyX-Code from FinancialService_client import * \end_layout \begin_layout LyX-Code portMethodName = "getFinancialService_Port" \end_layout \begin_layout LyX-Code except ImportError, e: \end_layout \begin_layout LyX-Code # 2.0final \end_layout \begin_layout LyX-Code from FinancialService_services import * \end_layout \begin_layout LyX-Code portMethodName = "getFinancialService_PortType" \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def main(): \end_layout \begin_layout LyX-Code op = OptionParser(usage="%prog [options]") \end_layout \begin_layout LyX-Code op.add_option("-u", "--url", help="service URL", metavar="URL") \end_layout \begin_layout LyX-Code options, args = op.parse_args() \end_layout \begin_layout LyX-Code loc = FinancialServiceLocator() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code service = getattr(loc, portMethodName)(url=options.url, tracefile=sys.stdout) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code print ' \backslash nAccessing service FinancialService, method getPV...' \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code while 1: \end_layout \begin_layout LyX-Code irate = None \end_layout \begin_layout LyX-Code while not (0.0 <= irate <= 100.0): \end_layout \begin_layout LyX-Code try: \end_layout \begin_layout LyX-Code irate = float(raw_input("Enter interest rate in percent: ")) \end_layout \begin_layout LyX-Code except ValueError, e: \end_layout \begin_layout LyX-Code print e \end_layout \begin_layout LyX-Code CFs = [] \end_layout \begin_layout LyX-Code period = 0 \end_layout \begin_layout LyX-Code while 1: \end_layout \begin_layout LyX-Code try: \end_layout \begin_layout LyX-Code CFs.append(float(raw_input("Enter CF(t=%s) [Ctrl-C to end]: " % (period,)))) \end_layout \begin_layout LyX-Code period += 1 \end_layout \begin_layout LyX-Code except ValueError, e: \end_layout \begin_layout LyX-Code print e \end_layout \begin_layout LyX-Code except KeyboardInterrupt: \end_layout \begin_layout LyX-Code print "...done." \end_layout \begin_layout LyX-Code break \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code #print "CFs list is:", CFs \end_layout \begin_layout LyX-Code #print "Calculation interest rate is:", irate \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code getPV = getPVRequest() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code getPV.set_element_irate(irate) \end_layout \begin_layout LyX-Code CFSequence = getPV.new_CFSequence() \end_layout \begin_layout LyX-Code CFSequence.set_element_CF(CFs) \end_layout \begin_layout LyX-Code getPV.set_element_CFSequence(CFSequence) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Alternatively, a valid request data structure can be provided like \end_layout \begin_layout LyX-Code # this (e.g. you do not want to use --complexType getters/setters) \end_layout \begin_layout LyX-Code #class CFSequence_class: \end_layout \begin_layout LyX-Code # _CF = CFs \end_layout \begin_layout LyX-Code #getPV._irate = irate \end_layout \begin_layout LyX-Code #getPV._CFSequence = CFSequence_class \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code result = service.getPV(getPV) \end_layout \begin_layout LyX-Code print 'result:', result \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if __name__ == '__main__': \end_layout \begin_layout LyX-Code main() \end_layout \begin_layout Standard After the necessary data has been read from the user, the appropriate attributes of the \family typewriter getPVRequest \family default instance are set. This is done by heavy use of the getters, setters and factory methods ZSI adds when using \family typewriter --complexType \family default code generation. \end_layout \begin_layout Standard An alternative is to model the sequence of cash flow elements with a the local class \family typewriter CFSequence_class \family default , see code comments. This class is \begin_inset Quotes eld \end_inset structurally equivalent \begin_inset Quotes erd \end_inset to the XML Schema \family typewriter CFSequence \family default complexType as defined in the WSDL. ZSI then handles all the type mapping and serialization issues for us. \end_layout \begin_layout Standard Sample client session output: \end_layout \begin_layout LyX-Code $ /apps/pydev/hjoukl/bin/python2.4 ./myFinancialClient.py -u "http://adevp02:8080/F inancialService" \end_layout \begin_layout LyX-Code *** ZSI version (2, 1, 0) *** \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code Accessing service FinancialService, method getPV... \end_layout \begin_layout LyX-Code Enter interest rate in percent: 7 \end_layout \begin_layout LyX-Code Enter CF(t=0) [Ctrl-C to end]: -100 \end_layout \begin_layout LyX-Code Enter CF(t=1) [Ctrl-C to end]: 8 \end_layout \begin_layout LyX-Code Enter CF(t=2) [Ctrl-C to end]: 8 \end_layout \begin_layout LyX-Code Enter CF(t=3) [Ctrl-C to end]: 108 \end_layout \begin_layout LyX-Code Enter CF(t=4) [Ctrl-C to end]: ^C...done. \end_layout \begin_layout LyX-Code _________________________________ Thu Jan 10 13:07:38 2008 REQUEST: \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code 7.000000 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code -100.000000 \end_layout \begin_layout LyX-Code 8.000000 \end_layout \begin_layout LyX-Code 8.000000 \end_layout \begin_layout LyX-Code 108.000000 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code _________________________________ Thu Jan 10 13:07:38 2008 RESPONSE: \end_layout \begin_layout LyX-Code 200 \end_layout \begin_layout LyX-Code OK \end_layout \begin_layout LyX-Code ------- \end_layout \begin_layout LyX-Code Server: ZSI/1.1 BaseHTTP/0.3 Python/2.4.4 \end_layout \begin_layout LyX-Code Date: Thu, 10 Jan 2008 12:07:38 GMT \end_layout \begin_layout LyX-Code Content-type: text/xml; charset="utf-8" \end_layout \begin_layout LyX-Code Content-Length: 456 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code 2.624316 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code result: 2.624316 \end_layout \begin_layout LyX-Code Enter interest rate in percent: \end_layout \begin_layout Subsection A gSOAP C++ client for the FinancialService \end_layout \begin_layout Subsubsection Code generation from WSDL \end_layout \begin_layout Enumerate Header: \end_layout \begin_deeper \begin_layout LyX-Code $ /apps/pydev/bin/wsdl2h -o financialService.h http://adevp02:8080/FinancialServi ce?wsdl \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code ** The gSOAP WSDL parser for C and C++ 1.2.9l \end_layout \begin_layout LyX-Code ** Copyright (C) 2000-2007 Robert van Engelen, Genivia Inc. \end_layout \begin_layout LyX-Code ** All Rights Reserved. This product is provided "as is", without any warranty. \end_layout \begin_layout LyX-Code ** The gSOAP WSDL parser is released under one of the following two licenses: \end_layout \begin_layout LyX-Code ** GPL or the commercial license by Genivia Inc. Use option -l for more info. \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code Saving financialService.h \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code Cannot open file 'typemap.dat' \end_layout \begin_layout LyX-Code Problem reading type map file typemap.dat. \end_layout \begin_layout LyX-Code Using internal type definitions for C++ instead. \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code Connecting to 'http://adevp02:8080/FinancialService?wsdl' to retrieve WSDL/XSD... connected, receiving... \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code To complete the process, compile with: \end_layout \begin_layout LyX-Code soapcpp2 financialService.h \end_layout \begin_layout LyX-Code \end_layout \end_deeper \begin_layout Enumerate Stub code: \end_layout \begin_deeper \begin_layout LyX-Code $ /apps/pydev/bin/soapcpp2 -I /data/pydev/DOWNLOADS/WebServices/C++/gsoap-2.7/soa pcpp2/import financialService.h \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code ** The gSOAP Stub and Skeleton Compiler for C and C++ 2.7.9l \end_layout \begin_layout LyX-Code ** Copyright (C) 2000-2007, Robert van Engelen, Genivia Inc. \end_layout \begin_layout LyX-Code ** All Rights Reserved. This product is provided "as is", without any warranty. \end_layout \begin_layout LyX-Code ** The gSOAP compiler is released under one of the following three licenses: \end_layout \begin_layout LyX-Code ** GPL, the gSOAP public license, or the commercial license by Genivia Inc. \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code Saving soapStub.h \end_layout \begin_layout LyX-Code Saving soapH.h \end_layout \begin_layout LyX-Code Saving soapC.cpp \end_layout \begin_layout LyX-Code Saving soapClient.cpp \end_layout \begin_layout LyX-Code Saving soapClientLib.cpp \end_layout \begin_layout LyX-Code Saving soapServer.cpp \end_layout \begin_layout LyX-Code Saving soapServerLib.cpp \end_layout \begin_layout LyX-Code Using ns1 service name: FinancialService_USCOREBinding \end_layout \begin_layout LyX-Code Using ns1 service style: document \end_layout \begin_layout LyX-Code Using ns1 service encoding: literal \end_layout \begin_layout LyX-Code Using ns1 service location: http://adevp02:8080/FinancialService \end_layout \begin_layout LyX-Code Using ns1 schema namespace: http://services.zsiserver.net/FinancialService.wsdl \end_layout \begin_layout LyX-Code Saving soapFinancialService_USCOREBindingProxy.h client proxy \end_layout \begin_layout LyX-Code Saving soapFinancialService_USCOREBindingObject.h server object \end_layout \begin_layout LyX-Code Saving FinancialService_USCOREBinding.getPV.req.xml sample SOAP/XML request \end_layout \begin_layout LyX-Code Saving FinancialService_USCOREBinding.getPV.res.xml sample SOAP/XML response \end_layout \begin_layout LyX-Code Saving FinancialService_USCOREBinding.nsmap namespace mapping table \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code Compilation successful \end_layout \end_deeper \begin_layout Subsubsection Client implementation \end_layout \begin_layout LyX-Code // Contents of file "myclient_use_proxy.cpp" \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code #include "soapFinancialService_USCOREBindingProxy.h" \end_layout \begin_layout LyX-Code #include "FinancialService_USCOREBinding.nsmap" \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code int main() \end_layout \begin_layout LyX-Code { \end_layout \begin_layout LyX-Code FinancialService_USCOREBinding fs; \end_layout \begin_layout LyX-Code _ns2__getPV getPV; \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code float PV; \end_layout \begin_layout LyX-Code float irate; \end_layout \begin_layout LyX-Code float CFval; \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code std::string input; \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code while (1) { \end_layout \begin_layout LyX-Code std::cout << "Enter interest rate in %: "; \end_layout \begin_layout LyX-Code std::cin >> irate; \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code std::vector CF; \end_layout \begin_layout LyX-Code for (int t=0;; t++) { \end_layout \begin_layout LyX-Code std::cout << "Enter CF(t=" << t << ")[e to exit]: "; \end_layout \begin_layout LyX-Code std::cin >> input; \end_layout \begin_layout LyX-Code if (input != "e") { \end_layout \begin_layout LyX-Code CFval = atof(input.c_str()); \end_layout \begin_layout LyX-Code CF.push_back(CFval); \end_layout \begin_layout LyX-Code } else { \end_layout \begin_layout LyX-Code break; \end_layout \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code getPV.irate = irate; \end_layout \begin_layout LyX-Code getPV.CFSequence.CF = CF; \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if (fs.__ns1__getPV(&getPV, PV) == SOAP_OK) { \end_layout \begin_layout LyX-Code std::cout << "PV=" << PV << std::endl; \end_layout \begin_layout LyX-Code } else \end_layout \begin_layout LyX-Code soap_print_fault(fs.soap, stderr); \end_layout \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code } \end_layout \begin_layout Subsubsection gSOAP Client compilation \end_layout \begin_layout Paragraph gcc 2.95.2 \end_layout \begin_layout LyX-Code g++ -o myClient -R/apps/prod/lib -I/apps/pydev/include -L/apps/pydev/lib soapC.cpp soapClient.cpp myClient_use_proxy.cpp -lgsoap++ -lsocket \end_layout \begin_layout Paragraph gcc 3.4.4 \end_layout \begin_layout LyX-Code /apps/local/gcc/3.4.4//bin/g++ -o myClient -R/apps/prod/gcc/3.4.4/lib -I/apps/pydev /gcc/3.4.4/include -L/apps/pydev/gcc/3.4.4/lib soapC.cpp soapClient.cpp myClient_use_p roxy.cpp -lgsoap++ -lsocket -lnsl \end_layout \begin_layout Section Afterthoughts \end_layout \begin_layout Standard With ZSI 2.0 and even more so the upcoming 2.1 version, ZSI has made important steps towards better usability, performance and functionality. When the original example code base presented here was developed with ZSI 1.6.1, several patches were necessary to get everything working, and a custom request handler was used to implement non-SOAPAction-based service dispatch. This time, ZSI 2.0 and 2.1 handled everything out-of-the-box, with little odds and ends. Still on the wish-list is more verbose built-in tracing capabilities; for the time being, it is necessary to either modify ZSI library code or to use some network sniffer to peek at the actual XML workload (or even the full HTTP header & body). \end_layout \begin_layout Standard A weaker point of ZSI still is documentation, though some nice 3rd party resources are available these days, notably \begin_inset LatexCommand \cite{HobbsCookbook} \end_inset and \begin_inset LatexCommand \cite{LokadTutorial} \end_inset . Hopefully, this is a valuable addition to the list. \end_layout \begin_layout LyX-Code \newpage \end_layout \begin_layout Section \start_of_appendix VistaSource Applixware spreadsheets as DateService clients \begin_inset LatexCommand \label{sec:VistaSource-Applixware-spreadsheets} \end_inset \end_layout \begin_layout Standard While Applixware 4.43 does not come with built-in web services support it is extensible with C shared libraries and its ELF macro extension language. We can use this feature to wrap gSOAP client code, making web services accessible from within Applix spreadsheets. \end_layout \begin_layout Paragraph Note: \end_layout \begin_layout Standard Example compiled with gcc 2.95.2. A short try with gcc 3.4.4 showed some compilation errors which werenīt \end_layout \begin_layout Subsubsection Code generation from WSDL \end_layout \begin_layout Standard The C/C++ client stubs must be generated as described in section \begin_inset LatexCommand \ref{sub:Code-generation-gsoap_DateService} \end_inset . \end_layout \begin_layout Subsubsection Applix client implementation \begin_inset LatexCommand \label{sub:Applix-client-implementation} \end_inset \end_layout \begin_layout Paragraph ELF C extension \end_layout \begin_layout Standard To make the DateService callable from Applix we need to write an ELF shared library C extension. The \family typewriter ax_DateService.cpp \family default extension implements the gSOAP web services access: \end_layout \begin_layout LyX-Code #include "elfapi.h" \end_layout \begin_layout LyX-Code #include "soapDateService_USCOREBindingProxy.h" \end_layout \begin_layout LyX-Code #include "DateService_USCOREBinding.nsmap" \end_layout \begin_layout LyX-Code #define TRUE 1 \end_layout \begin_layout LyX-Code extern "C" elfData getCurrentDate(); \end_layout \begin_layout LyX-Code extern "C" elfData getCurrentDate_2(); \end_layout \begin_layout LyX-Code extern "C" elfData getDate(); \end_layout \begin_layout LyX-Code extern "C" elfData getDate_2(); \end_layout \begin_layout LyX-Code /* \end_layout \begin_layout LyX-Code * Define the function table \end_layout \begin_layout LyX-Code */ \end_layout \begin_layout LyX-Code AxCallInfo_t funcTable[]={ \end_layout \begin_layout LyX-Code { "Web Services", /* func type ... "financial", "math" ... */ \end_layout \begin_layout LyX-Code getCurrentDate, /* The C routine to call */ \end_layout \begin_layout LyX-Code "getCurrentDate", /* name to use in Applixware (usually identical to the function name) */ \end_layout \begin_layout LyX-Code "string getCurrentDate(string)", /* shows the function name and its arguments */ TRUE /* An integer that governs the treatment of ERROR and NA values, and whether the function is displayed in the Spreadsheets Functions dialog box. */ \end_layout \begin_layout LyX-Code }, \end_layout \begin_layout LyX-Code { "Web Services", /* func type ... "financial", "math" ... */ \end_layout \begin_layout LyX-Code getCurrentDate_2, /* The C routine to call */ \end_layout \begin_layout LyX-Code "getCurrentDate_2", /* name to use in Applixware (usually identical to the function name) */ \end_layout \begin_layout LyX-Code "date getCurrentDate_2(string)", /* shows the function name and its arguments */ TRUE /* An integer that governs the treatment of ERROR and NA values, and whether the function is displayed in the Spreadsheets Functions dialog box. */ \end_layout \begin_layout LyX-Code }, \end_layout \begin_layout LyX-Code { "Web Services", /* func type ... "financial", "math" ... */ \end_layout \begin_layout LyX-Code getDate, /* The C routine to call */ \end_layout \begin_layout LyX-Code "getDate", /* name to use in Applixware (usually identical to the function name) */ \end_layout \begin_layout LyX-Code "date_string getDate(int, date)", /* shows the function name and its arguments */ \end_layout \begin_layout LyX-Code TRUE /* An integer that governs the treatment of ERROR and NA values, and whether the function is displayed in the Spreadsheets Functions dialog box. */ \end_layout \begin_layout LyX-Code }, \end_layout \begin_layout LyX-Code { "Web Services", /* func type ... "financial", "math" ... */ \end_layout \begin_layout LyX-Code getDate_2, /* The C routine to call */ \end_layout \begin_layout LyX-Code "getDate_2", /* name to use in Applixware (usually identical to the function name) */ \end_layout \begin_layout LyX-Code "date getDate_2(int, date)", /* shows the function name and its arguments */ \end_layout \begin_layout LyX-Code TRUE /* An integer that governs the treatment of ERROR and NA values, and whether the function is displayed in the Spreadsheets Functions dialog box. */ \end_layout \begin_layout LyX-Code }, \end_layout \begin_layout LyX-Code { NULL, \end_layout \begin_layout LyX-Code NULL, \end_layout \begin_layout LyX-Code NULL, \end_layout \begin_layout LyX-Code NULL, \end_layout \begin_layout LyX-Code NULL \end_layout \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code }; \end_layout \begin_layout LyX-Code /* \end_layout \begin_layout LyX-Code * Function AxGetCallInfo returns the function table. \end_layout \begin_layout LyX-Code * This function is called by Applixware when you run the macro \end_layout \begin_layout LyX-Code * RPC_CONNECT@ or INSTALL_C_LIBRARY@. This function must exist \end_layout \begin_layout LyX-Code * in the RPC program or Shared Library. \end_layout \begin_layout LyX-Code */ \end_layout \begin_layout LyX-Code DLL_EXPORT AxCallInfo_t *AxGetCallInfo() \end_layout \begin_layout LyX-Code { \end_layout \begin_layout LyX-Code return(funcTable); \end_layout \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code /*=========================================*/ \end_layout \begin_layout LyX-Code /* func */ \end_layout \begin_layout LyX-Code /*=========================================*/ \end_layout \begin_layout LyX-Code extern "C" elfData getCurrentDate(elfData args) /* args is an array of arguments passed from ELF */ \end_layout \begin_layout LyX-Code { \end_layout \begin_layout LyX-Code elfData arrayElem; \end_layout \begin_layout LyX-Code elfData retValue; \end_layout \begin_layout LyX-Code char *val; \end_layout \begin_layout LyX-Code /* Check argument number & types */ \end_layout \begin_layout LyX-Code if (AxArraySize(args) != 1) { \end_layout \begin_layout LyX-Code AxError(1, "function takes one single argument", "getCurrentDate"); \end_layout \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code arrayElem = AxArrayElement(args, 0); \end_layout \begin_layout LyX-Code if (!AxIsString(arrayElem)) AxError(1, "argument must be a string", "getCurrentDate"); \end_layout \begin_layout LyX-Code DateService_USCOREBinding ds; \end_layout \begin_layout LyX-Code ns2__Date *today; \end_layout \begin_layout LyX-Code ns3__getCurrentDateResponse today_response; \end_layout \begin_layout LyX-Code val = AxStrFromDataPtr(arrayElem); \end_layout \begin_layout LyX-Code std::string text(val); \end_layout \begin_layout LyX-Code std::string soapError = "SOAP Error"; \end_layout \begin_layout LyX-Code std::string result = ""; \end_layout \begin_layout LyX-Code if(ds.ns3__getCurrentDate(text, today_response) == SOAP_OK) \end_layout \begin_layout LyX-Code { \end_layout \begin_layout LyX-Code today = today_response.today; \end_layout \begin_layout LyX-Code result = *today->year \end_layout \begin_layout LyX-Code + "/" + *today->month \end_layout \begin_layout LyX-Code + "/" + *today->day \end_layout \begin_layout LyX-Code + " " + *today->hour \end_layout \begin_layout LyX-Code + ":" + *today->minute \end_layout \begin_layout LyX-Code + ":" + *today->second; \end_layout \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code else \end_layout \begin_layout LyX-Code { \end_layout \begin_layout LyX-Code //soap_print_fault(ds.soap, stderr); // gSOAP error \end_layout \begin_layout LyX-Code soapError = "SOAP error"; \end_layout \begin_layout LyX-Code AxError(1, soapError.c_str(), "getCurrentDate"); \end_layout \begin_layout LyX-Code //AxError(1, "SOAP error", "getCurrentDate"); \end_layout \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code retValue = AxMakeStrData(result.length(), result.c_str()); \end_layout \begin_layout LyX-Code return retValue; \end_layout \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code extern "C" elfData getCurrentDate_2(elfData args) /* args is an array of arguments passed from ELF */ \end_layout \begin_layout LyX-Code { \end_layout \begin_layout LyX-Code elfData arrayElem; \end_layout \begin_layout LyX-Code elfData retArray; \end_layout \begin_layout LyX-Code char *val; \end_layout \begin_layout LyX-Code /* Check argument number & types */ \end_layout \begin_layout LyX-Code if (AxArraySize(args) != 1) { \end_layout \begin_layout LyX-Code AxError(1, "function takes one single argument", "getCurrentDate"); \end_layout \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code arrayElem = AxArrayElement(args, 0); \end_layout \begin_layout LyX-Code if (!AxIsString(arrayElem)) AxError(1, "argument must be a string", "getCurrentDate"); \end_layout \begin_layout LyX-Code DateService_USCOREBinding ds; \end_layout \begin_layout LyX-Code ns2__Date *today; \end_layout \begin_layout LyX-Code ns3__getCurrentDateResponse today_response; \end_layout \begin_layout LyX-Code val = AxStrFromDataPtr(arrayElem); \end_layout \begin_layout LyX-Code std::string text(val); \end_layout \begin_layout LyX-Code std::string soapError = ""; \end_layout \begin_layout LyX-Code if(ds.ns3__getCurrentDate(text, today_response) == SOAP_OK) \end_layout \begin_layout LyX-Code { \end_layout \begin_layout LyX-Code today = today_response.today; \end_layout \begin_layout LyX-Code retArray = AxMakeArray(0); \end_layout \begin_layout LyX-Code retArray = AxAddIntToArray(retArray, 0, atoi((*today->year).c_str())) ; \end_layout \begin_layout LyX-Code retArray = AxAddIntToArray(retArray, 1, atoi((*today->month).c_str()) ); \end_layout \begin_layout LyX-Code retArray = AxAddIntToArray(retArray, 2, atoi((*today->day).c_str())); \end_layout \begin_layout LyX-Code retArray = AxAddIntToArray(retArray, 3, atoi((*today->hour).c_str())) ; \end_layout \begin_layout LyX-Code retArray = AxAddIntToArray(retArray, 4, atoi((*today->minute).c_str() )); \end_layout \begin_layout LyX-Code retArray = AxAddIntToArray(retArray, 5, atoi((*today->second).c_str() )); \end_layout \begin_layout LyX-Code retArray = AxAddIntToArray(retArray, 6, atoi((*today->weekday).c_str( ))); \end_layout \begin_layout LyX-Code retArray = AxAddIntToArray(retArray, 7, atoi((*today->dayOfYear).c_st r())); \end_layout \begin_layout LyX-Code retArray = AxAddIntToArray(retArray, 8, atoi((*today->dst).c_str())); \end_layout \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code else \end_layout \begin_layout LyX-Code { \end_layout \begin_layout LyX-Code //soap_print_fault(ds.soap, stderr); // gSOAP error \end_layout \begin_layout LyX-Code soapError = "SOAP error"; \end_layout \begin_layout LyX-Code AxError(1, soapError.c_str(), "getCurrentDate"); \end_layout \begin_layout LyX-Code //AxError(1, "SOAP error", "getCurrentDate"); \end_layout \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code return retArray; \end_layout \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code extern "C" elfData getDate(elfData args) /* args is an array of arguments passed from ELF */ { \end_layout \begin_layout LyX-Code elfData elf_offset, elf_date, retValue; \end_layout \begin_layout LyX-Code /* Check argument number & types */ \end_layout \begin_layout LyX-Code if (AxArraySize(args) != 2) { \end_layout \begin_layout LyX-Code AxError(1, "function takes 2 arguments", "getDate"); \end_layout \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code elf_offset = AxArrayElement(args, 0); \end_layout \begin_layout LyX-Code if (!AxIsInt(elf_offset)) AxError(1, "argument must be an integer", "getDate"); \end_layout \begin_layout LyX-Code elf_date = AxArrayElement(args, 1); \end_layout \begin_layout LyX-Code if (!AxIsArray(elf_date)) AxError(1, "argument must be an array", "getDate") ; \end_layout \begin_layout LyX-Code DateService_USCOREBinding ds; \end_layout \begin_layout LyX-Code //ns2__Date someday, date; \end_layout \begin_layout LyX-Code ns2__Date *someday, date; \end_layout \begin_layout LyX-Code ns3__getDateResponse someday_response; \end_layout \begin_layout LyX-Code std::string offset(AxStrFromDataPtr(elf_offset)); \end_layout \begin_layout LyX-Code std::string year(AxStrFromDataPtr(AxArrayElement(AxArrayElement(elf_date, 0), 0))); \end_layout \begin_layout LyX-Code std::string month(AxStrFromDataPtr(AxArrayElement(AxArrayElement(elf_date, 1), 0))); \end_layout \begin_layout LyX-Code std::string day(AxStrFromDataPtr(AxArrayElement(AxArrayElement(elf_date, 2), 0))); \end_layout \begin_layout LyX-Code std::string hour(AxStrFromDataPtr(AxArrayElement(AxArrayElement(elf_date, 3), 0))); \end_layout \begin_layout LyX-Code std::string minute(AxStrFromDataPtr(AxArrayElement(AxArrayElement(elf_date, 4), 0))); \end_layout \begin_layout LyX-Code std::string second(AxStrFromDataPtr(AxArrayElement(AxArrayElement(elf_date, 5), 0))); \end_layout \begin_layout LyX-Code std::string weekday(AxStrFromDataPtr(AxArrayElement(AxArrayElement(elf_date, 6), 0))); \end_layout \begin_layout LyX-Code std::string dayOfYear(AxStrFromDataPtr(AxArrayElement(AxArrayElement(elf_dat e, 7), 0))); \end_layout \begin_layout LyX-Code std::string dst(AxStrFromDataPtr(AxArrayElement(AxArrayElement(elf_date, 8), 0))); \end_layout \begin_layout LyX-Code date.year = &year; \end_layout \begin_layout LyX-Code date.month = &month; \end_layout \begin_layout LyX-Code date.day = &day; \end_layout \begin_layout LyX-Code date.hour = &hour; \end_layout \begin_layout LyX-Code date.minute = &minute; \end_layout \begin_layout LyX-Code date.second = &second; \end_layout \begin_layout LyX-Code date.weekday = &weekday; \end_layout \begin_layout LyX-Code date.dayOfYear = &dayOfYear; \end_layout \begin_layout LyX-Code date.dst = &dst; \end_layout \begin_layout LyX-Code std::string result = ""; \end_layout \begin_layout LyX-Code //retValue = AxMakeStrData(offset.length(), offset.c_str()); \end_layout \begin_layout LyX-Code //return retValue; \end_layout \begin_layout LyX-Code //std::cout << "(1) Calling 'getCurrentDate()'- Web Service method:" << std::endl; \end_layout \begin_layout LyX-Code if(ds.ns3__getDate(offset, &date, someday_response) == SOAP_OK) \end_layout \begin_layout LyX-Code { \end_layout \begin_layout LyX-Code someday = someday_response.day; \end_layout \begin_layout LyX-Code //someday = *(someday_response.o_USCOREsomeday); \end_layout \begin_layout LyX-Code // std::cout << " \backslash nCurrent date:" << std::endl; \end_layout \begin_layout LyX-Code // std::cout << " \backslash tyear: " << *someday->year << std::endl; \end_layout \begin_layout LyX-Code // std::cout << " \backslash tmonth: " << *someday->month << std::endl; \end_layout \begin_layout LyX-Code // std::cout << " \backslash tday: " << *someday->day << std::endl; \end_layout \begin_layout LyX-Code result = *someday->year \end_layout \begin_layout LyX-Code + "/" + *someday->month \end_layout \begin_layout LyX-Code + "/" + *someday->day \end_layout \begin_layout LyX-Code + " " + *someday->hour \end_layout \begin_layout LyX-Code + ":" + *someday->minute \end_layout \begin_layout LyX-Code + ":" + *someday->second; \end_layout \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code else \end_layout \begin_layout LyX-Code { \end_layout \begin_layout LyX-Code //soap_print_fault(ds.soap, stderr); // gSOAP error \end_layout \begin_layout LyX-Code AxError(1, "SOAP error", "getDate"); \end_layout \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code retValue = AxMakeStrData(result.length(), result.c_str()); \end_layout \begin_layout LyX-Code return retValue; \end_layout \begin_layout LyX-Code // return retArray; \end_layout \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code extern "C" elfData getDate_2(elfData args) /* args is an array of arguments passed from ELF */ \end_layout \begin_layout LyX-Code { \end_layout \begin_layout LyX-Code elfData elf_offset, elf_date, retArray; \end_layout \begin_layout LyX-Code /* Check argument number & types */ \end_layout \begin_layout LyX-Code if (AxArraySize(args) != 2) { \end_layout \begin_layout LyX-Code AxError(1, "function takes 2 arguments", "getCurrentDate"); \end_layout \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code elf_offset = AxArrayElement(args, 0); \end_layout \begin_layout LyX-Code if (!AxIsInt(elf_offset)) AxError(1, "argument must be an integer", "getDate"); \end_layout \begin_layout LyX-Code elf_date = AxArrayElement(args, 1); \end_layout \begin_layout LyX-Code if (!AxIsArray(elf_date)) AxError(1, "argument must be an array", "getDate") ; \end_layout \begin_layout LyX-Code DateService_USCOREBinding ds; \end_layout \begin_layout LyX-Code //ns2__Date someday, date; \end_layout \begin_layout LyX-Code ns2__Date *someday, date; \end_layout \begin_layout LyX-Code ns3__getDateResponse someday_response; \end_layout \begin_layout LyX-Code std::string offset(AxStrFromDataPtr(elf_offset)); \end_layout \begin_layout LyX-Code std::string year(AxStrFromDataPtr(AxArrayElement(AxArrayElement(elf_date, 0), 0))); \end_layout \begin_layout LyX-Code std::string month(AxStrFromDataPtr(AxArrayElement(AxArrayElement(elf_date, 1), 0))); \end_layout \begin_layout LyX-Code std::string day(AxStrFromDataPtr(AxArrayElement(AxArrayElement(elf_date, 2), 0))); \end_layout \begin_layout LyX-Code std::string hour(AxStrFromDataPtr(AxArrayElement(AxArrayElement(elf_date, 3), 0))); \end_layout \begin_layout LyX-Code std::string minute(AxStrFromDataPtr(AxArrayElement(AxArrayElement(elf_date, 4), 0))); \end_layout \begin_layout LyX-Code std::string second(AxStrFromDataPtr(AxArrayElement(AxArrayElement(elf_date, 5), 0))); \end_layout \begin_layout LyX-Code std::string weekday(AxStrFromDataPtr(AxArrayElement(AxArrayElement(elf_date, 6), 0))); \end_layout \begin_layout LyX-Code std::string dayOfYear(AxStrFromDataPtr(AxArrayElement(AxArrayElement(elf_dat e, 7), 0))); \end_layout \begin_layout LyX-Code std::string dst(AxStrFromDataPtr(AxArrayElement(AxArrayElement(elf_date, 8), 0))); \end_layout \begin_layout LyX-Code date.year = &year; \end_layout \begin_layout LyX-Code date.month = &month; \end_layout \begin_layout LyX-Code date.day = &day; \end_layout \begin_layout LyX-Code date.hour = &hour; \end_layout \begin_layout LyX-Code date.minute = &minute; \end_layout \begin_layout LyX-Code date.second = &second; \end_layout \begin_layout LyX-Code date.weekday = &weekday; \end_layout \begin_layout LyX-Code date.dayOfYear = &dayOfYear; \end_layout \begin_layout LyX-Code date.dst = &dst; \end_layout \begin_layout LyX-Code std::string result = ""; \end_layout \begin_layout LyX-Code int return_integer = TRUE; \end_layout \begin_layout LyX-Code //std::cout << "(1) Calling 'getCurrentDate()'- Web Service method:" << std::endl; \end_layout \begin_layout LyX-Code if(ds.ns3__getDate(offset, &date, someday_response) == SOAP_OK) \end_layout \begin_layout LyX-Code { \end_layout \begin_layout LyX-Code someday = someday_response.day; \end_layout \begin_layout LyX-Code //someday = *(someday_response.o_USCOREsomeday); \end_layout \begin_layout LyX-Code // std::cout << " \backslash nCurrent date:" << std::endl; \end_layout \begin_layout LyX-Code // std::cout << " \backslash tyear: " << *someday->year << std::endl; \end_layout \begin_layout LyX-Code // std::cout << " \backslash tmonth: " << *someday->month << std::endl; \end_layout \begin_layout LyX-Code // std::cout << " \backslash tday: " << *someday->day << std::endl; \end_layout \begin_layout LyX-Code retArray = AxMakeArray(0); \end_layout \begin_layout LyX-Code //std::string val; \end_layout \begin_layout LyX-Code //val = *someday->year; \end_layout \begin_layout LyX-Code //elfData err; \end_layout \begin_layout LyX-Code //err = AxMakeStrData(val.length(), val.c_str()); \end_layout \begin_layout LyX-Code //AxError(1, val.c_str(), err); \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if (return_integer == TRUE) \end_layout \begin_layout LyX-Code { \end_layout \begin_layout LyX-Code retArray = AxAddIntToArray(retArray, 0, atoi((*someday->year ).c_str())); \end_layout \begin_layout LyX-Code retArray = AxAddIntToArray(retArray, 1, atoi((*someday->mont h).c_str())); \end_layout \begin_layout LyX-Code retArray = AxAddIntToArray(retArray, 2, atoi((*someday->day). c_str())); \end_layout \begin_layout LyX-Code retArray = AxAddIntToArray(retArray, 3, atoi((*someday->hour ).c_str())); \end_layout \begin_layout LyX-Code retArray = AxAddIntToArray(retArray, 4, atoi((*someday->minu te).c_str())); \end_layout \begin_layout LyX-Code retArray = AxAddIntToArray(retArray, 5, atoi((*someday->seco nd).c_str())); \end_layout \begin_layout LyX-Code retArray = AxAddIntToArray(retArray, 6, atoi((*someday->week day).c_str())); \end_layout \begin_layout LyX-Code retArray = AxAddIntToArray(retArray, 7, atoi((*someday->dayO fYear).c_str())); retArray = AxAddIntToArray(retArray, 8, atoi((*someday->dst).c_str())); \end_layout \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code else \end_layout \begin_layout LyX-Code { \end_layout \begin_layout LyX-Code retArray = AxAddStrToArray(retArray, 0, (*someday->year).c_st r()); \end_layout \begin_layout LyX-Code retArray = AxAddStrToArray(retArray, 1, (*someday->month).c_s tr()); \end_layout \begin_layout LyX-Code retArray = AxAddStrToArray(retArray, 2, (*someday->day).c_str ()); \end_layout \begin_layout LyX-Code retArray = AxAddStrToArray(retArray, 3, (*someday->hour).c_st r()); \end_layout \begin_layout LyX-Code retArray = AxAddStrToArray(retArray, 4, (*someday->minute).c_ str()); \end_layout \begin_layout LyX-Code retArray = AxAddStrToArray(retArray, 5, (*someday->second).c_ str()); \end_layout \begin_layout LyX-Code retArray = AxAddStrToArray(retArray, 6, (*someday->weekday).c _str()); \end_layout \begin_layout LyX-Code retArray = AxAddStrToArray(retArray, 7, (*someday->dayOfYear ).c_str()); \end_layout \begin_layout LyX-Code retArray = AxAddStrToArray(retArray, 8, (*someday->dst).c_str ()); \end_layout \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code //result = *someday->year + "/" + *someday->month + "/" + *someday-> day; \end_layout \begin_layout LyX-Code //result = *(someday.year) + "/" + *(someday.month) + "/" + *(someday.d ay); \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code else \end_layout \begin_layout LyX-Code { \end_layout \begin_layout LyX-Code //soap_print_fault(ds.soap, stderr); // gSOAP error \end_layout \begin_layout LyX-Code AxError(1, "SOAP error", "getCurrentDate"); \end_layout \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code //retValue = AxMakeStrData(result.length(), result.c_str()); \end_layout \begin_layout LyX-Code //return retValue; \end_layout \begin_layout LyX-Code return retArray; \end_layout \begin_layout LyX-Code } \end_layout \begin_layout Standard The code above implements the following functions: \end_layout \begin_layout Itemize getCurrentDate: Return a string containing the current date. Takes a string argument. \end_layout \begin_layout Itemize getCurrentDate_2: Return the current date as an array. Takes a string argument. \end_layout \begin_layout Itemize getDate: Return the input date + offset as a string. Takes an integer offset and a 9-element date array as arguments. \end_layout \begin_layout Itemize getDate_2: Return input date + offset as an array. Takes an integer offset and a 9-element date array as arguments. \end_layout \begin_layout Standard To compile the code, you have to add the Applix ELF headers to the include path: \end_layout \begin_layout LyX-Code g++ -shared -o ax_DateService.so -R/apps/prod/lib \end_layout \begin_layout LyX-Code -I/apps/prod/applix/applix/versions/4.43.1021.544.343/axdata/elf \end_layout \begin_layout LyX-Code -I/apps/pydev/include -L/apps/pydev/lib soapC.cpp soap ax_DateService.cpp \end_layout \begin_layout LyX-Code -lgsoap++ -lsocket \end_layout \begin_layout Standard The resulting shared library \family typewriter ax_DateService.so \family default must be loaded into Applix before it can be used. This is done with the \family typewriter install_c_library@() \family default macro: \end_layout \begin_layout LyX-Code #define path "/data/pydev/WebServicesPresentation/DateService_rpc_literal/Applix /" \end_layout \begin_layout LyX-Code macro load() \end_layout \begin_layout LyX-Code install_c_library@(path++"ax_DateService.so") \end_layout \begin_layout LyX-Code endmacro \end_layout \begin_layout LyX-Code macro unload() \end_layout \begin_layout LyX-Code unbind_c_library@(path++"ax_DateService.so") \end_layout \begin_layout LyX-Code endmacro \end_layout \begin_layout LyX-Code macro reload() \end_layout \begin_layout LyX-Code unbind_c_library@(path++"ax_DateService.so") \end_layout \begin_layout LyX-Code install_c_library@(path++"ax_DateService.so") \end_layout \begin_layout LyX-Code endmacro \end_layout \begin_layout Standard After the shared library has been dynamically loaded, the functions can be used like built-in functions inside the spreadsheet. To make sensible use of the \family typewriter getDate_2 \family default function which returns the date struct as an array, you have to invoke it as an array function: \begin_inset Foot status open \begin_layout Standard Compare to section \begin_inset LatexCommand \ref{sub:VB-client-implementation-DateService} \end_inset . \end_layout \end_inset \end_layout \begin_layout Enumerate Select an array with the appropriate number of cells (9 cells, one-dimensional in our case). \end_layout \begin_layout Enumerate Enter the function call, e.g.: \end_layout \begin_deeper \begin_layout LyX-Code =getDate_2(offset_5, someday_5) \end_layout \end_deeper \begin_layout Enumerate Press CTRL-SHIFT-ENTER to insert the formula as array function. \end_layout \begin_layout Standard The actual extension function calls might also be wrapped into additional macro code. This makes it possible to invoke the functions from command buttons or to populate predefined, hardcoded result ranges with the results of a service call. Compared to Excel, an additional possibility for Applix is the definition of an extra argument for the output range \emph on name \emph default (as a string). Given the name, this range can then be filled with the result value structure, removing the need to hardcode range names in the macro code. \begin_inset Foot status open \begin_layout Standard In Applix, macros can be called from spreadsheet cells but places restrictions on modifying spreadsheet cells from within a macro. \end_layout \end_inset \end_layout \begin_layout Standard The following macro code show some of the possibilities. Refer to the code comments for explanations: \end_layout \begin_layout LyX-Code #include "spsheet_.am" \end_layout \begin_layout LyX-Code /* \end_layout \begin_layout LyX-Code * Macros for Web Service getCurrentDate() \end_layout \begin_layout LyX-Code */ \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code /* <<<< getCurrentDate_Macro >>>>> */ \end_layout \begin_layout LyX-Code ' --> Returning ASCII string \end_layout \begin_layout LyX-Code ' --> called from within a cell \end_layout \begin_layout LyX-Code ' --> providing an input parameter \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code Macro getCurrentDate_Macro(text) \end_layout \begin_layout LyX-Code var retStr \end_layout \begin_layout LyX-Code /* Web Service call */ \end_layout \begin_layout LyX-Code retStr = getCurrentDate(text) \end_layout \begin_layout LyX-Code return(retStr) \end_layout \begin_layout LyX-Code endmacro \end_layout \begin_layout LyX-Code /* <<<<<<<<<<<<< END >>>>>>>>>>>>>*/ \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code /* <<<< getCurrentDate_2_Macro >>>>> */ \end_layout \begin_layout LyX-Code ' --> no return value: returning ASCII string with population \end_layout \begin_layout LyX-Code ' --> called by Buttons \end_layout \begin_layout LyX-Code ' --> no input parameter possible \end_layout \begin_layout LyX-Code ' --> hard-coded input-parameter' \end_layout \begin_layout LyX-Code ' --> hard coded named range for result population \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code Macro getCurrentDate_2_Macro() \end_layout \begin_layout LyX-Code var format ss_cell_ cellInfo \end_layout \begin_layout LyX-Code var celladdr, cell \end_layout \begin_layout LyX-Code var retStr, text \end_layout \begin_layout LyX-Code cellAddr = ss_extract_range_info@("input_3") \end_layout \begin_layout LyX-Code cellInfo = ss_get_cell@(cellAddr[0], cellAddr[1], 0) \end_layout \begin_layout LyX-Code text=cellInfo.display_str \end_layout \begin_layout LyX-Code /* Web Service call */ \end_layout \begin_layout LyX-Code retStr = getCurrentDate(text) \end_layout \begin_layout LyX-Code /* Result Population */ \end_layout \begin_layout LyX-Code cellAddr = ss_extract_range_info@("today_1") \end_layout \begin_layout LyX-Code ss_put_cell@(ss_coordinate@(cellAddr[0], cellAddr[1], 0), retStr) \end_layout \begin_layout LyX-Code 'Alternativ \end_layout \begin_layout LyX-Code 'cell=ss_coordinate@(cellAddr[0], cellAddr[1], 0) \end_layout \begin_layout LyX-Code 'new_task@("ss_put_cell@", cell, retStr) \end_layout \begin_layout LyX-Code endmacro \end_layout \begin_layout LyX-Code /* <<<<<<<<<<<<< END >>>>>>>>>>>>>*/ \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code /* <<<< getCurrentDate_3_Macro >>>>> */ \end_layout \begin_layout LyX-Code ' --> no return value: returning date array with population \end_layout \begin_layout LyX-Code ' --> called by cell \end_layout \begin_layout LyX-Code ' --> input parameter for result population \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code Macro getCurrentDate_3_Macro(text, today) \end_layout \begin_layout LyX-Code 'for parameter today the name (string) of a named range has to be passed, not the named \end_layout \begin_layout LyX-Code ' ranged object by itself --> this would pass an array to the macro, where the \end_layout \begin_layout LyX-Code 'content of the named range is provided. But we need the coordinates of the cells of the \end_layout \begin_layout LyX-Code 'named range \end_layout \begin_layout LyX-Code var format ss_cell_ cellInfo \end_layout \begin_layout LyX-Code var celladdr, cell \end_layout \begin_layout LyX-Code var retStr, date, rowStart, rowEnd, col \end_layout \begin_layout LyX-Code var k, i \end_layout \begin_layout LyX-Code /* Web Service call */ \end_layout \begin_layout LyX-Code date = getCurrentDate_2(text) \end_layout \begin_layout LyX-Code /* Result Population */ \end_layout \begin_layout LyX-Code 'cellAddr = ss_extract_range_info@("today") \end_layout \begin_layout LyX-Code ' --> hardcoded if no parameter is allowed \end_layout \begin_layout LyX-Code cellAddr = ss_extract_range_info@(today) \end_layout \begin_layout LyX-Code ' --> not possible to get named range as parameter for result because \end_layout \begin_layout LyX-Code ' the content of the named range is used by APPLIX NOT THE COORDINATES \end_layout \begin_layout LyX-Code rowStart=cellAddr[1] \end_layout \begin_layout LyX-Code rowEnd=cellAddr[3] \end_layout \begin_layout LyX-Code col=cellAddr[2] \end_layout \begin_layout LyX-Code k=0 \end_layout \begin_layout LyX-Code for i=rowStart to rowEnd \end_layout \begin_layout LyX-Code cellInfo= ss_get_cell@(col, i, 0) \end_layout \begin_layout LyX-Code 'date[k]= { cellInfo.display_str+0 } \end_layout \begin_layout LyX-Code 'ss_put_cell@(ss_coordinate@(cellAddr[col], cellAddr[i], 0), date[k]) \end_layout \begin_layout LyX-Code 'Alternativ \end_layout \begin_layout LyX-Code cell=ss_coordinate@(col, i, 0) \end_layout \begin_layout LyX-Code new_task@("ss_put_cell@", cell, date[k]) \end_layout \begin_layout LyX-Code 'ss_put_cell@(col,i,date[k]) \end_layout \begin_layout LyX-Code k=k+1 \end_layout \begin_layout LyX-Code next i \end_layout \begin_layout LyX-Code endmacro \end_layout \begin_layout LyX-Code /* <<<<<<<<<<<<< END >>>>>>>>>>>>>*/ \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code /* \end_layout \begin_layout LyX-Code * Macros for Web Service getDate() \end_layout \begin_layout LyX-Code */ \end_layout \begin_layout LyX-Code /* <<<< getDate_Macro >>>>> */ \end_layout \begin_layout LyX-Code ' --> Returning ASCII string \end_layout \begin_layout LyX-Code ' --> called from within a cell \end_layout \begin_layout LyX-Code ' --> providing input parameter \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code Macro getDate_macro(offset, someday) \end_layout \begin_layout LyX-Code var retStr, date \end_layout \begin_layout LyX-Code date=someday \end_layout \begin_layout LyX-Code /* Web Service call */ \end_layout \begin_layout LyX-Code retStr = getDate(offset, date) \end_layout \begin_layout LyX-Code return(retStr) \end_layout \begin_layout LyX-Code endmacro \end_layout \begin_layout LyX-Code /* <<<<<<<<<<<<< END >>>>>>>>>>>>>*/ \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code /* <<<< getDate_1_Macro >>>>> */ \end_layout \begin_layout LyX-Code ' --> Not used \end_layout \begin_layout LyX-Code ' --> no return value: returning ASCII string with population \end_layout \begin_layout LyX-Code ' --> called by Buttons or from within a cell \end_layout \begin_layout LyX-Code ' --> no input parameter possible \end_layout \begin_layout LyX-Code ' --> hard-coded input-parameter' \end_layout \begin_layout LyX-Code ' --> hard coded named range for result population \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code Macro getDate_1_macro() \end_layout \begin_layout LyX-Code var format ss_cell_ cellInfo \end_layout \begin_layout LyX-Code var retStr, offset, date, rowStart, rowEnd, col \end_layout \begin_layout LyX-Code var cellAddr, k, i, cell \end_layout \begin_layout LyX-Code /* Preparing Web Service input parameter */ \end_layout \begin_layout LyX-Code cellAddr = ss_extract_range_info@("offset") \end_layout \begin_layout LyX-Code cellInfo = ss_get_cell@(cellAddr[0], cellAddr[1], 0) \end_layout \begin_layout LyX-Code offset=cellInfo.display_str+0 \end_layout \begin_layout LyX-Code cellAddr = ss_extract_range_info@("someday") \end_layout \begin_layout LyX-Code rowStart=cellAddr[1] \end_layout \begin_layout LyX-Code rowEnd=cellAddr[3] \end_layout \begin_layout LyX-Code col=cellAddr[2] \end_layout \begin_layout LyX-Code k=0 \end_layout \begin_layout LyX-Code for i=rowStart to rowEnd \end_layout \begin_layout LyX-Code cellInfo= ss_get_cell@(col, i, 0) \end_layout \begin_layout LyX-Code date[k]= { cellInfo.display_str+0 } \end_layout \begin_layout LyX-Code k=k+1 \end_layout \begin_layout LyX-Code next i \end_layout \begin_layout LyX-Code /* Web Service call */ \end_layout \begin_layout LyX-Code retStr = getDate(offset, date) \end_layout \begin_layout LyX-Code /* Result Population */ \end_layout \begin_layout LyX-Code cellAddr = ss_extract_range_info@("result_macro1") \end_layout \begin_layout LyX-Code /* Gilt fuer Start des Makros ueber Button */ \end_layout \begin_layout LyX-Code ' ss_put_cell@(ss_coordinate@(cellAddr[0], cellAddr[1], 0), retStr) \end_layout \begin_layout LyX-Code /* Gilt fuer Start des Makros in einer Zelle */ \end_layout \begin_layout LyX-Code cell=ss_coordinate@(cellAddr[0], cellAddr[1], 0) \end_layout \begin_layout LyX-Code 'cell="A:E12" \end_layout \begin_layout LyX-Code new_task@("ss_put_cell@", cell, retStr) \end_layout \begin_layout LyX-Code return(retStr) \end_layout \begin_layout LyX-Code endmacro \end_layout \begin_layout LyX-Code /* <<<<<<<<<<<<< END >>>>>>>>>>>>>*/ \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code /* <<<< getDate_2_Macro >>>>> */ \end_layout \begin_layout LyX-Code ' --> no return value: returning date array with population \end_layout \begin_layout LyX-Code ' --> called by Buttons \end_layout \begin_layout LyX-Code ' --> no input parameter possible \end_layout \begin_layout LyX-Code ' --> hard-coded input-parameter' \end_layout \begin_layout LyX-Code ' --> hard coded named range for result population \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code Macro getDate_2_macro() \end_layout \begin_layout LyX-Code var format ss_cell_ cellInfo \end_layout \begin_layout LyX-Code var celladdr, cell \end_layout \begin_layout LyX-Code var date, rowStart, rowEnd, col \end_layout \begin_layout LyX-Code var k, i \end_layout \begin_layout LyX-Code var offset, someday \end_layout \begin_layout LyX-Code /* Perparing Web Service input parameter */ \end_layout \begin_layout LyX-Code cellAddr = ss_extract_range_info@("offset_3") \end_layout \begin_layout LyX-Code cellInfo = ss_get_cell@(cellAddr[0], cellAddr[1], 0) \end_layout \begin_layout LyX-Code 'offset=cellInfo.display_str + 0 \end_layout \begin_layout LyX-Code offset=cellInfo.value \end_layout \begin_layout LyX-Code cellAddr = ss_extract_range_info@("someday_3") \end_layout \begin_layout LyX-Code rowStart=cellAddr[1] \end_layout \begin_layout LyX-Code rowEnd=cellAddr[3] \end_layout \begin_layout LyX-Code col=cellAddr[2] \end_layout \begin_layout LyX-Code k=0 \end_layout \begin_layout LyX-Code for i=rowStart to rowEnd \end_layout \begin_layout LyX-Code cellInfo = ss_get_cell@(col, i, 0) \end_layout \begin_layout LyX-Code someday[k] = {cellInfo.value} \end_layout \begin_layout LyX-Code 'someday[k]=ss_coordinate@(col, i, 0) \end_layout \begin_layout LyX-Code 'new_task@("ss_put_cell@", cell, date[k]) \end_layout \begin_layout LyX-Code 'ss_put_cell@(col,i,date[k]) --> not working \end_layout \begin_layout LyX-Code k=k+1 \end_layout \begin_layout LyX-Code next i \end_layout \begin_layout LyX-Code 'DUMP_ARRAY@(someday) \end_layout \begin_layout LyX-Code /* Web Serviuce call */ \end_layout \begin_layout LyX-Code date = getDate_2(offset, someday) \end_layout \begin_layout LyX-Code /* Result Population */ \end_layout \begin_layout LyX-Code cellAddr = ss_extract_range_info@("day_1") \end_layout \begin_layout LyX-Code ' --> not possible to get named range as parameter for result because \end_layout \begin_layout LyX-Code ' the content of the named range is used by APPLIX NOT THE COORDINATES \end_layout \begin_layout LyX-Code rowStart=cellAddr[1] \end_layout \begin_layout LyX-Code rowEnd=cellAddr[3] \end_layout \begin_layout LyX-Code col=cellAddr[2] \end_layout \begin_layout LyX-Code k=0 \end_layout \begin_layout LyX-Code for i=rowStart to rowEnd \end_layout \begin_layout LyX-Code cell=ss_coordinate@(col, i, 0) \end_layout \begin_layout LyX-Code new_task@("ss_put_cell@", cell, date[k]) \end_layout \begin_layout LyX-Code 'ss_put_cell@(col,i,date[k]) --> not working \end_layout \begin_layout LyX-Code k=k+1 \end_layout \begin_layout LyX-Code next i \end_layout \begin_layout LyX-Code /* \end_layout \begin_layout LyX-Code * DUMP_ARRAY@(retStruct) \end_layout \begin_layout LyX-Code */ \end_layout \begin_layout LyX-Code endmacro \end_layout \begin_layout LyX-Code /* <<<<<<<<<<<<< END >>>>>>>>>>>>>*/ \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code /* <<<< getDate_3_Macro >>>>> */ \end_layout \begin_layout LyX-Code ' --> no return value: returning date array with population \end_layout \begin_layout LyX-Code ' --> called by cells \end_layout \begin_layout LyX-Code ' --> input parameter for WebService call \end_layout \begin_layout LyX-Code ' --> named range input-parameter for result population \end_layout \begin_layout LyX-Code ' \end_layout \begin_layout LyX-Code Macro getDate_3_macro(offset, someday, day) \end_layout \begin_layout LyX-Code var format ss_cell_ cellInfo \end_layout \begin_layout LyX-Code var celladdr, cell \end_layout \begin_layout LyX-Code var date, rowStart, rowEnd, col \end_layout \begin_layout LyX-Code var k, i \end_layout \begin_layout LyX-Code /* Preparation Web Service input parameter not necessary --> provided by call */ \end_layout \begin_layout LyX-Code /* Web Service call */ \end_layout \begin_layout LyX-Code date = getDate_2(offset, someday) \end_layout \begin_layout LyX-Code /* Result Population */ \end_layout \begin_layout LyX-Code 'cellAddr = ss_extract_range_info@("day") \end_layout \begin_layout LyX-Code ' --> hardcoded if no parameter is allowed \end_layout \begin_layout LyX-Code cellAddr = ss_extract_range_info@(day) \end_layout \begin_layout LyX-Code ' --> not possible to get named range as parameter for result because \end_layout \begin_layout LyX-Code ' the content of the named range is used by APPLIX NOT THE COORDINATES \end_layout \begin_layout LyX-Code rowStart=cellAddr[1] \end_layout \begin_layout LyX-Code rowEnd=cellAddr[3] \end_layout \begin_layout LyX-Code col=cellAddr[2] \end_layout \begin_layout LyX-Code k=0 \end_layout \begin_layout LyX-Code for i=rowStart to rowEnd \end_layout \begin_layout LyX-Code cell=ss_coordinate@(col, i, 0) \end_layout \begin_layout LyX-Code new_task@("ss_put_cell@", cell, date[k]) \end_layout \begin_layout LyX-Code 'ss_put_cell@(col,i,date[k]) --> not working \end_layout \begin_layout LyX-Code k=k+1 \end_layout \begin_layout LyX-Code next i \end_layout \begin_layout LyX-Code /* \end_layout \begin_layout LyX-Code * DUMP_ARRAY@(retStruct) \end_layout \begin_layout LyX-Code */ \end_layout \begin_layout LyX-Code endmacro \end_layout \begin_layout LyX-Code /* <<<<<<<<<<<<< END >>>>>>>>>>>>>*/ \end_layout \begin_layout Section ZSI tracing hacks \begin_inset LatexCommand \label{sec:ZSI-hack} \end_inset \end_layout \begin_layout Standard Warning: These are more-or-less nifty recipees to inject some functionality to ZSI with regard to logging/debugging. Not tested beyond what you see here. \end_layout \begin_layout Subsection Detailed method call tracing \begin_inset LatexCommand \label{sub:Detailed-method-call-tracing} \end_inset \end_layout \begin_layout Standard If something goes wrong with answering a service request in a ZSI server and you do not know why this is, sometimes ZSIīs fault exception message is just not enough to get a clue. As the current ZSI versions do not allow you to output more detailed call stack information, hereīs a helper module \family typewriter inject_tracing.py \family default to inject desired functionality into ZSI: \end_layout \begin_layout LyX-Code import types \end_layout \begin_layout LyX-Code # Based on "Method enhancement" Python cookbook recipee by Ken Seehof \end_layout \begin_layout LyX-Code # (http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81982)} \end_layout \begin_layout LyX-Code def enhance_method(cls, methodname, decorate): \end_layout \begin_layout LyX-Code 'replace a method with an enhancement' \end_layout \begin_layout LyX-Code method = getattr(cls, methodname) \end_layout \begin_layout LyX-Code _f = decorate(method) \end_layout \begin_layout LyX-Code setattr(cls, methodname, types.MethodType(_f, None, cls)) \end_layout \begin_layout LyX-Code # loop over class dict and call enhance_method() function \end_layout \begin_layout LyX-Code # for all methods to modify \end_layout \begin_layout LyX-Code def enhance_all_methods(cls, decorate): \end_layout \begin_layout LyX-Code for methodname in cls.__dict__: \end_layout \begin_layout LyX-Code if not methodname.startswith("__") \backslash \end_layout \begin_layout LyX-Code or methodname in ["__call__", "__call___"]: \end_layout \begin_layout LyX-Code method = getattr(cls, methodname) \end_layout \begin_layout LyX-Code if isinstance(method, types.MethodType): \end_layout \begin_layout LyX-Code enhance_method(cls, methodname, decorate) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def traced(enter_text="-->", leave_text="<--"): \end_layout \begin_layout LyX-Code """Configurable Function decorator. \end_layout \begin_layout LyX-Code Outputs the text given as enter_text plus the function name and arguments \end_layout \begin_layout LyX-Code before entering the decorated function, and the leave_text string after \end_layout \begin_layout LyX-Code having left the decorated function. \end_layout \begin_layout LyX-Code Example: \end_layout \begin_layout LyX-Code >>> @traced("into", "out of") \end_layout \begin_layout LyX-Code ... def foo(x,y): \end_layout \begin_layout LyX-Code ... print x,y \end_layout \begin_layout LyX-Code ... return x \end_layout \begin_layout LyX-Code ... \end_layout \begin_layout LyX-Code >>> foo(1, 2) \end_layout \begin_layout LyX-Code into foo(args=(1, 2), kwargs={}) \end_layout \begin_layout LyX-Code 1 2 \end_layout \begin_layout LyX-Code out of foo() \end_layout \begin_layout LyX-Code 1 \end_layout \begin_layout LyX-Code >>> \end_layout \begin_layout LyX-Code """ \end_layout \begin_layout LyX-Code def traced(f): \end_layout \begin_layout LyX-Code _f = f \end_layout \begin_layout LyX-Code def f(*args, **kwargs): \end_layout \begin_layout LyX-Code print enter_text, "%s(args=%s, kwargs=%s)" % (_f.__name__, args, \end_layout \begin_layout LyX-Code kwargs) \end_layout \begin_layout LyX-Code try: \end_layout \begin_layout LyX-Code return _f(*args, **kwargs) \end_layout \begin_layout LyX-Code finally: \end_layout \begin_layout LyX-Code print leave_text, "%s()" % (_f.__name__) \end_layout \begin_layout LyX-Code f.__name__ = _f.__name__ \end_layout \begin_layout LyX-Code return f \end_layout \begin_layout LyX-Code return traced \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def enhance_module(module, decorator=traced, names=[]): \end_layout \begin_layout LyX-Code """Wrap module class methods and callables with given decorator. Applies \end_layout \begin_layout LyX-Code to list of names only, if given, otherwise to all detected \end_layout \begin_layout LyX-Code methods/callables. \end_layout \begin_layout LyX-Code """ \end_layout \begin_layout LyX-Code for objname, obj in module.__dict__.items(): \end_layout \begin_layout LyX-Code if not names or objname in names: \end_layout \begin_layout LyX-Code if isinstance(obj, (type, types.ClassType)): \end_layout \begin_layout LyX-Code #print obj \end_layout \begin_layout LyX-Code enhance_all_methods(obj, decorator()) \end_layout \begin_layout LyX-Code elif callable(obj): \end_layout \begin_layout LyX-Code #print obj \end_layout \begin_layout LyX-Code module.__dict__[objname] = decorator()(obj) \end_layout \begin_layout Standard The FinancialService server implementation from section \begin_inset LatexCommand \ref{sub:Python-ZSI-FinancialService-server} \end_inset can be modified like this to make use of it: \end_layout \begin_layout LyX-Code from optparse import OptionParser \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Import the ZSI stuff you'd need no matter what \end_layout \begin_layout LyX-Code from ZSI.wstools import logging \end_layout \begin_layout LyX-Code from ZSI import ServiceContainer \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code from ZSI.version import Version as zsiversion \end_layout \begin_layout LyX-Code print "*** ZSI version %s ***" % (zsiversion,) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Import the generated Server Object \end_layout \begin_layout LyX-Code try: \end_layout \begin_layout LyX-Code # 2.1alpha \end_layout \begin_layout LyX-Code from FinancialService_server import * \end_layout \begin_layout LyX-Code # Dummy implementation \end_layout \begin_layout LyX-Code def adapt(method): \end_layout \begin_layout LyX-Code return method \end_layout \begin_layout LyX-Code except ImportError, e: \end_layout \begin_layout LyX-Code # 2.0final \end_layout \begin_layout LyX-Code from FinancialService_services_server import * \end_layout \begin_layout LyX-Code from service_adaptor import ServiceAdaptor21 \end_layout \begin_layout LyX-Code adapt = ServiceAdaptor21.adapt \end_layout \begin_layout LyX-Code # Wrap generated 2.0 class to 2.1 soap_ method behaviour \end_layout \begin_layout LyX-Code FinancialService = ServiceAdaptor21(FinancialService) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Create a Server implementation by inheritance \end_layout \begin_layout LyX-Code # 2.1alpha implementation \end_layout \begin_layout LyX-Code class MyFinancialService(FinancialService): \end_layout \begin_layout LyX-Code # Make WSDL available for HTTP GET \end_layout \begin_layout LyX-Code _wsdl = "".join(open("FinancialService.wsdl").readlines()) \end_layout \begin_layout LyX-Code @adapt \end_layout \begin_layout LyX-Code def soap_getPV(self, ps, **kw): \end_layout \begin_layout LyX-Code # Call the generated base class method to get appropriate \end_layout \begin_layout LyX-Code # input/output data structures \end_layout \begin_layout LyX-Code request, response = FinancialService.soap_getPV(self, ps, **kw) \end_layout \begin_layout LyX-Code # Comment that out if need be to see what API these objects offer: \end_layout \begin_layout LyX-Code #print help(request) \end_layout \begin_layout LyX-Code #print help(response) \end_layout \begin_layout LyX-Code # Using getters/setters \end_layout \begin_layout LyX-Code irate = request.get_element_irate() \end_layout \begin_layout LyX-Code CFs = request.get_element_CFSequence().get_element_CF() \end_layout \begin_layout LyX-Code # Alternative way to access the input data \end_layout \begin_layout LyX-Code #irate = request._irate \end_layout \begin_layout LyX-Code #CFs = request._CFSequence._CF \end_layout \begin_layout LyX-Code # Invoke the service worker code \end_layout \begin_layout LyX-Code PV = self.getPV(irate, CFs) \end_layout \begin_layout LyX-Code #print "PV:", PV \end_layout \begin_layout LyX-Code # assign return value to response object \end_layout \begin_layout LyX-Code response = getPVResponse(PV) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Another way to create a serializable response object \end_layout \begin_layout LyX-Code #class SimpleTypeWrapper(float): \end_layout \begin_layout LyX-Code # typecode = response.typecode \end_layout \begin_layout LyX-Code #response = SimpleTypeWrapper(PV) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code return request, response \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def getPV(self, irate, cashFlows): \end_layout \begin_layout LyX-Code # worker code method \end_layout \begin_layout LyX-Code t = 0 \end_layout \begin_layout LyX-Code PV = 0.0 \end_layout \begin_layout LyX-Code for CF in cashFlows: \end_layout \begin_layout LyX-Code PV += (CF or 0.0) * ((irate / 100.0 + 1) ** (-t)) \end_layout \begin_layout LyX-Code t += 1 \end_layout \begin_layout LyX-Code return PV \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code op = OptionParser(usage="%prog [options]") \end_layout \begin_layout LyX-Code op.add_option("-l", "--loglevel", help="loglevel (DEBUG, WARN)", \end_layout \begin_layout LyX-Code metavar="LOGLEVEL") \end_layout \begin_layout LyX-Code op.add_option("-p", "--port", help="HTTP port", \end_layout \begin_layout LyX-Code metavar="PORT", default=8080, type="int") \end_layout \begin_layout LyX-Code op.add_option("-t", "--trace", help="trace function/method calls", \end_layout \begin_layout LyX-Code action="store_true") \end_layout \begin_layout LyX-Code op.add_option("-n", "--names", help="trace function/method names", \end_layout \begin_layout LyX-Code metavar="NAME", action="append") \end_layout \begin_layout LyX-Code options, args = op.parse_args() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if options.trace: \end_layout \begin_layout LyX-Code from ZSI import parse \end_layout \begin_layout LyX-Code from inject_tracing import enhance_module \end_layout \begin_layout LyX-Code enhance_module(ServiceContainer, names=options.names) \end_layout \begin_layout LyX-Code enhance_module(parse, names=options.names) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # set the loglevel according to cmd line arg \end_layout \begin_layout LyX-Code if options.loglevel: \end_layout \begin_layout LyX-Code loglevel = eval(options.loglevel, logging.__dict__) \end_layout \begin_layout LyX-Code logger = logging.getLogger("") \end_layout \begin_layout LyX-Code logger.setLevel(loglevel) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Run the server with a given list of services \end_layout \begin_layout LyX-Code ServiceContainer.AsServer(port=options.port, services=[MyFinancialService(),]) \end_layout \begin_layout Standard This will print way more tracing output for incoming service requests: \end_layout \begin_layout LyX-Code $ /apps/pydev/hjoukl/bin/python2.4 ./myTracedFinancialServer.py -t \end_layout \begin_layout LyX-Code *** ZSI version (2, 1, 0) *** \end_layout \begin_layout LyX-Code --> AsServer(args=(), kwargs={'services': [<__main__.MyFinancialService instance at 0x4c3558>], 'port': 8080}) \end_layout \begin_layout LyX-Code --> server_bind(args=( ,), kwargs={}) <-- server_bind() \end_layout \begin_layout LyX-Code --> setNode(args=(, <__main__.MyFinancialService instance at 0x4c3558>), kwargs={}) \end_layout \begin_layout LyX-Code --> getPost(args=(<__main__.MyFinancialService instance at 0x4c3558>,), kwargs={} ) \end_layout \begin_layout LyX-Code <-- getPost() \end_layout \begin_layout LyX-Code <-- setNode() \end_layout \begin_layout LyX-Code --> handle(args=(,), kwargs={}) \end_layout \begin_layout LyX-Code --> handle_one_request(args=(,), kwargs={}) \end_layout \begin_layout LyX-Code --> parse_request(args=(,), kwargs={}) \end_layout \begin_layout LyX-Code <-- parse_request() \end_layout \begin_layout LyX-Code --> do_POST(args=(,) , kwargs={}) \end_layout \begin_layout LyX-Code --> (args=(,), kwargs={}) \end_layout \begin_layout LyX-Code <-- () \end_layout \begin_layout LyX-Code --> _check_for_legal_children(args=(, 'Envelope', ), kwargs={}) \end_layout \begin_layout LyX-Code --> _check_for_legal_children(args=(, 'Envelope', ), kwargs={}) \end_layout \begin_layout LyX-Code --> (args=(,), kwargs={}) \end_layout \begin_layout LyX-Code <-- () \end_layout \begin_layout LyX-Code <-- _check_for_legal_children() \end_layout \begin_layout LyX-Code <-- _check_for_legal_children() \end_layout \begin_layout LyX-Code --> (args=(,), kwargs={}) \end_layout \begin_layout LyX-Code <-- () \end_layout \begin_layout LyX-Code --> _valid_encoding(args=(,), kwargs={}) \end_layout \begin_layout LyX-Code <-- _valid_encoding() \end_layout \begin_layout LyX-Code --> (args=(,), kwargs={}) \end_layout \begin_layout LyX-Code <-- () \end_layout \begin_layout LyX-Code --> _check_for_legal_children(args=(, 'Body', , 0), kwargs={}) \end_layout \begin_layout LyX-Code --> _check_for_legal_children(args=(, 'Body', , 0), kwargs={}) \end_layout \begin_layout LyX-Code --> (args=(,), kwargs={}) \end_layout \begin_layout LyX-Code <-- () \end_layout \begin_layout LyX-Code <-- _check_for_legal_children() \end_layout \begin_layout LyX-Code <-- _check_for_legal_children() \end_layout \begin_layout LyX-Code --> (args=(,), kwargs={}) \end_layout \begin_layout LyX-Code <-- () \end_layout \begin_layout LyX-Code --> _check_for_pi_nodes(args=(, [], 0), kwargs={}) \end_layout \begin_layout LyX-Code --> _check_for_pi_nodes(args=(, [], 0), kwargs={}) \end_layout \begin_layout LyX-Code --> (args=(,), kwargs={}) \end_layout \begin_layout LyX-Code <-- () \end_layout \begin_layout LyX-Code --> (args=(,), kwargs={}) \end_layout \begin_layout LyX-Code <-- () \end_layout \begin_layout LyX-Code --> (args=(,), kwargs={}) \end_layout \begin_layout LyX-Code <-- () \end_layout \begin_layout LyX-Code --> (args=(,), kwargs={}) \end_layout \begin_layout LyX-Code <-- () \end_layout \begin_layout LyX-Code --> (args=(,), kwargs={}) \end_layout \begin_layout LyX-Code <-- () \end_layout \begin_layout LyX-Code --> (args=(,), kwargs={}) \end_layout \begin_layout LyX-Code <-- () \end_layout \begin_layout LyX-Code --> (args=(,), kwargs={}) \end_layout \begin_layout LyX-Code <-- () \end_layout \begin_layout LyX-Code --> (args=(,), kwargs={}) \end_layout \begin_layout LyX-Code <-- () \end_layout \begin_layout LyX-Code <-- _check_for_pi_nodes() \end_layout \begin_layout LyX-Code <-- _check_for_pi_nodes() \end_layout \begin_layout LyX-Code --> _valid_encoding(args=(,), kwargs={}) \end_layout \begin_layout LyX-Code <-- _valid_encoding() \end_layout \begin_layout LyX-Code --> (args=(,), kwargs={}) \end_layout \begin_layout LyX-Code <-- () \end_layout \begin_layout LyX-Code --> (args=(,), kwargs={}) \end_layout \begin_layout LyX-Code <-- () \end_layout \begin_layout LyX-Code --> _valid_encoding(args=(,), kwargs={}) \end_layout \begin_layout LyX-Code <-- _valid_encoding() \end_layout \begin_layout LyX-Code --> (args=(,), kwargs={}) \end_layout \begin_layout LyX-Code <-- () \end_layout \begin_layout LyX-Code --> _check_for_pi_nodes(args=(, [], 0), kwargs={}) \end_layout \begin_layout LyX-Code --> _check_for_pi_nodes(args=(, [], 0), kwargs={}) \end_layout \begin_layout LyX-Code <-- _check_for_pi_nodes() \end_layout \begin_layout LyX-Code <-- _check_for_pi_nodes() \end_layout \begin_layout LyX-Code --> _Dispatch(args=(, , >, >), kwargs={'action': '', 'post': '/FinancialService'}) \end_layout \begin_layout LyX-Code --> getNode(args=(, '/FinancialService'), kwargs={}) \end_layout \begin_layout LyX-Code <-- getNode() \end_layout \begin_layout LyX-Code --> authorize(args=(<__main__.MyFinancialService instance at 0x4c3558>, None, '/FinancialService', ''), kwargs={}) \end_layout \begin_layout LyX-Code <-- authorize() \end_layout \begin_layout LyX-Code --> getOperation(args=(<__main__.MyFinancialService instance at 0x4c3558>, , ''), kwargs={}) \end_layout \begin_layout LyX-Code --> getOperationName(args=(<__main__.MyFinancialService instance at 0x4c3558>, , ''), kwargs={}) \end_layout \begin_layout LyX-Code --> (args=(,), kwargs={}) \end_layout \begin_layout LyX-Code <-- () \end_layout \begin_layout LyX-Code <-- getOperationName() \end_layout \begin_layout LyX-Code <-- getOperation() \end_layout \begin_layout LyX-Code --> Parse(args=(, ), kwargs={}) \end_layout \begin_layout LyX-Code --> Parse(args=(, ), kwargs={}) \end_layout \begin_layout LyX-Code <-- Parse() \end_layout \begin_layout LyX-Code <-- Parse() \end_layout \begin_layout LyX-Code --> verify(args=(<__main__.MyFinancialService instance at 0x4c3558>, ), kwargs={}) \end_layout \begin_layout LyX-Code <-- verify() \end_layout \begin_layout LyX-Code --> serialize(args=(, 0.934579439252331 67), kwargs={}) \end_layout \begin_layout LyX-Code --> writeNSdict(args=(, {}), kwargs={} ) \end_layout \begin_layout LyX-Code <-- writeNSdict() \end_layout \begin_layout LyX-Code <-- serialize() \end_layout \begin_layout LyX-Code --> sign(args=(<__main__.MyFinancialService instance at 0x4c3558>, ), kwargs={}) \end_layout \begin_layout LyX-Code <-- sign() \end_layout \begin_layout LyX-Code --> close(args=(,), kwargs={}) \end_layout \begin_layout LyX-Code <-- close() \end_layout \begin_layout LyX-Code --> send_xml(args=(, \end_layout \begin_layout LyX-Code ' \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code 0.934579 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code '), kwargs={}) \end_layout \begin_layout LyX-Code --> send_response(args=(, 200), kwargs={}) \end_layout \begin_layout LyX-Code --> log_request(args=(, 200), kwargs={}) \end_layout \begin_layout LyX-Code --> log_message(args=(, '"%s" %s %s', 'POST /FinancialService HTTP/1.1', '200', '-'), kwargs={}) \end_layout \begin_layout LyX-Code --> address_string(args=(,), kwargs={}) \end_layout \begin_layout LyX-Code <-- address_string() \end_layout \begin_layout LyX-Code --> log_date_time_string(args=(,), \end_layout \begin_layout LyX-Code kwargs={}) \end_layout \begin_layout LyX-Code <-- log_date_time_string() \end_layout \begin_layout LyX-Code adevp02 - - [11/Jan/2008 14:41:24] "POST /FinancialService HTTP/1.1" 200 - \end_layout \begin_layout LyX-Code <-- log_message() \end_layout \begin_layout LyX-Code <-- log_request() \end_layout \begin_layout LyX-Code --> version_string(args=(,), kwargs={}) \end_layout \begin_layout LyX-Code <-- version_string() \end_layout \begin_layout LyX-Code --> send_header(args=(, 'Server', 'ZSI/1.1 BaseHTTP/0.3 Python/2.4.4'), kwargs={}) \end_layout \begin_layout LyX-Code <-- send_header() \end_layout \begin_layout LyX-Code --> date_time_string(args=(,), kwargs={}) \end_layout \begin_layout LyX-Code <-- date_time_string() \end_layout \begin_layout LyX-Code --> send_header(args=(, 'Date', 'Fri, 11 Jan 2008 13:41:24 GMT'), kwargs={}) \end_layout \begin_layout LyX-Code <-- send_header() \end_layout \begin_layout LyX-Code <-- send_response() \end_layout \begin_layout LyX-Code --> send_header(args=(, 'Content-type', 'text/xml; charset="utf-8"'), kwargs={}) \end_layout \begin_layout LyX-Code <-- send_header() \end_layout \begin_layout LyX-Code --> send_header(args=(, 'Content-Length', '456'), kwargs={}) \end_layout \begin_layout LyX-Code <-- send_header() \end_layout \begin_layout LyX-Code --> end_headers(args=(,), kwargs={}) \end_layout \begin_layout LyX-Code <-- end_headers() \end_layout \begin_layout LyX-Code <-- send_xml() \end_layout \begin_layout LyX-Code <-- _Dispatch() \end_layout \begin_layout LyX-Code <-- do_POST() \end_layout \begin_layout LyX-Code <-- handle_one_request() \end_layout \begin_layout LyX-Code <-- handle() \end_layout \begin_layout Subsection Logging the client request \begin_inset LatexCommand \label{sub:Logging-the-client-request} \end_inset \end_layout \begin_layout Standard While section \begin_inset LatexCommand \ref{sub:Detailed-method-call-tracing} \end_inset presents a way to enable a rather detailed tracing of method calls and arguments, it is often desirable to log the incoming client request on the server side. Unfortunately, ZSI currently offers no standard way to do so. The straightforward way to achieve this is to implement a custom request handler by copying over the \family typewriter ServiceContainer.SOAPRequestHander.do_POST() \family default method implementation and insert debugging statements where desired. We use another approach to get at the incoming XML: \end_layout \begin_layout LyX-Code from optparse import OptionParser \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Import the ZSI stuff you'd need no matter what \end_layout \begin_layout LyX-Code from ZSI.wstools import logging \end_layout \begin_layout LyX-Code from ZSI import ServiceContainer \end_layout \begin_layout LyX-Code from ZSI import resolvers \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code from ZSI.version import Version as zsiversion \end_layout \begin_layout LyX-Code print "*** ZSI version %s ***" % (zsiversion,) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code LOGGER = logging.getLogger("") \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Some more or less nifty stuff for more extensive tracing \end_layout \begin_layout LyX-Code from inject_tracing import enhance_module, TraceLocals \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Import the generated Server Object \end_layout \begin_layout LyX-Code try: \end_layout \begin_layout LyX-Code # 2.1alpha \end_layout \begin_layout LyX-Code from FinancialService_server import * \end_layout \begin_layout LyX-Code # Dummy implementation \end_layout \begin_layout LyX-Code def adapt(method): \end_layout \begin_layout LyX-Code return method \end_layout \begin_layout LyX-Code except ImportError, e: \end_layout \begin_layout LyX-Code # 2.0final \end_layout \begin_layout LyX-Code from FinancialService_services_server import * \end_layout \begin_layout LyX-Code from service_adaptor import ServiceAdaptor21 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code adapt = ServiceAdaptor21.adapt \end_layout \begin_layout LyX-Code # Wrap generated 2.0 class to 2.1 soap_ method behaviour \end_layout \begin_layout LyX-Code FinancialService = ServiceAdaptor21(FinancialService) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Create a Server implementation by inheritance \end_layout \begin_layout LyX-Code # 2.1alpha implementation \end_layout \begin_layout LyX-Code class MyFinancialService(FinancialService): \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Make WSDL available for HTTP GET \end_layout \begin_layout LyX-Code _wsdl = "".join(open("FinancialService.wsdl").readlines()) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code @adapt \end_layout \begin_layout LyX-Code def soap_getPV(self, ps, **kw): \end_layout \begin_layout LyX-Code # Call the generated base class method to get appropriate \end_layout \begin_layout LyX-Code # input/output data structures \end_layout \begin_layout LyX-Code request, response = FinancialService.soap_getPV(self, ps, **kw) \end_layout \begin_layout LyX-Code # Comment that out if need be to see what API these objects offer: \end_layout \begin_layout LyX-Code #print help(request) \end_layout \begin_layout LyX-Code #print help(response) \end_layout \begin_layout LyX-Code # Using getters/setters \end_layout \begin_layout LyX-Code irate = request.get_element_irate() \end_layout \begin_layout LyX-Code CFs = request.get_element_CFSequence().get_element_CF() \end_layout \begin_layout LyX-Code # Alternative way to access the input data \end_layout \begin_layout LyX-Code #irate = request._irate \end_layout \begin_layout LyX-Code #CFs = request._CFSequence._CF \end_layout \begin_layout LyX-Code # Invoke the service worker code \end_layout \begin_layout LyX-Code PV = self.getPV(irate, CFs) \end_layout \begin_layout LyX-Code #print "PV:", PV \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # assign return value to response object \end_layout \begin_layout LyX-Code response = getPVResponse(PV) \end_layout \begin_layout LyX-Code # Another way to create a serializable response object \end_layout \begin_layout LyX-Code #class SimpleTypeWrapper(float): \end_layout \begin_layout LyX-Code # typecode = response.typecode \end_layout \begin_layout LyX-Code #response = SimpleTypeWrapper(PV) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code return request, response \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def getPV(self, irate, cashFlows): \end_layout \begin_layout LyX-Code # worker code method \end_layout \begin_layout LyX-Code t = 0 \end_layout \begin_layout LyX-Code PV = 0.0 \end_layout \begin_layout LyX-Code for CF in cashFlows: \end_layout \begin_layout LyX-Code PV += (CF or 0.0) * ((irate / 100.0 + 1) ** (-t)) \end_layout \begin_layout LyX-Code t += 1 \end_layout \begin_layout LyX-Code return PV \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # A traced request handler class that debug-logs the payload XML of the request \end_layout \begin_layout LyX-Code # message, as received from the client \end_layout \begin_layout LyX-Code class DebugSOAPRequestHandler(ServiceContainer.SOAPRequestHandler): \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code @TraceLocals.trace(["xml"], logger=LOGGER) \end_layout \begin_layout LyX-Code def do_POST(self): \end_layout \begin_layout LyX-Code ServiceContainer.SOAPRequestHandler.do_POST(self) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code op = OptionParser(usage="%prog [options]") \end_layout \begin_layout LyX-Code op.add_option("-l", "--loglevel", help="loglevel (DEBUG, WARN)", \end_layout \begin_layout LyX-Code metavar="LOGLEVEL") \end_layout \begin_layout LyX-Code op.add_option("-p", "--port", help="HTTP port", \end_layout \begin_layout LyX-Code metavar="PORT", default=8080, type="int") \end_layout \begin_layout LyX-Code op.add_option("-t", "--trace", help="trace function/method calls", \end_layout \begin_layout LyX-Code action="store_true") \end_layout \begin_layout LyX-Code op.add_option("-n", "--names", help="trace function/method names", \end_layout \begin_layout LyX-Code metavar="NAME", action="append") \end_layout \begin_layout LyX-Code options, args = op.parse_args() \end_layout \begin_layout LyX-Code if options.trace: \end_layout \begin_layout LyX-Code from ZSI import parse \end_layout \begin_layout LyX-Code enhance_module(ServiceContainer, names=options.names) \end_layout \begin_layout LyX-Code enhance_module(parse, names=options.names) \end_layout \begin_layout LyX-Code # set the loglevel according to cmd line arg \end_layout \begin_layout LyX-Code if options.loglevel: \end_layout \begin_layout LyX-Code loglevel = eval(options.loglevel, logging.__dict__) \end_layout \begin_layout LyX-Code LOGGER.setLevel(loglevel) \end_layout \begin_layout LyX-Code # Run the server with a given list of services \end_layout \begin_layout LyX-Code address = ('', options.port) \end_layout \begin_layout LyX-Code sc = ServiceContainer.ServiceContainer( \end_layout \begin_layout LyX-Code address, services=[MyFinancialService(),], \end_layout \begin_layout LyX-Code RequestHandlerClass=DebugSOAPRequestHandler) \end_layout \begin_layout LyX-Code sc.serve_forever() \end_layout \begin_layout Standard Comments: \end_layout \begin_layout Itemize Instead of the \family typewriter AsServer() \family default function, a ServiceContainer instance with a custom request handler class \family typewriter DebugSOAPRequestHandler \family default gets used \end_layout \begin_layout Itemize \family typewriter DebugSOAPRequestHandler \family default features a decorated \family typewriter do_POST() \family default method that calls its super-classī \family typewriter do_POST() \family default . The decorator provides debugging of local variables, at the point of leaving the method, and can be parameterized to print only certain variable names (we want the \begin_inset Quotes eld \end_inset xml \begin_inset Quotes erd \end_inset string only here). \end_layout \begin_layout Standard This is how the extended \family typewriter inject_tracing.py \family default module now looks like: \begin_inset Foot status open \begin_layout Standard Based on \begin_inset ERT status open \begin_layout Standard \backslash htmladdnormallink{Mailing list posting http://mail.python.org/pipermail/python-lis t/2005-April/319662.html}{http://mail.python.org/pipermail/python-list/2005-April/3 19662.html} \end_layout \end_inset , by Bengt Richter \end_layout \end_inset \end_layout \begin_layout LyX-Code import types \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Based on "Method enhancement" Python cookbook recipee by Ken Seehof \end_layout \begin_layout LyX-Code # (http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81982)} \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def enhance_method(cls, methodname, decorate): \end_layout \begin_layout LyX-Code 'replace a method with an enhancement' \end_layout \begin_layout LyX-Code method = getattr(cls, methodname) \end_layout \begin_layout LyX-Code _f = decorate(method) \end_layout \begin_layout LyX-Code setattr(cls, methodname, types.MethodType(_f, None, cls)) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # loop over class dict and call enhance_method() function \end_layout \begin_layout LyX-Code # for all methods to modify \end_layout \begin_layout LyX-Code def enhance_all_methods(cls, decorate): \end_layout \begin_layout LyX-Code for methodname in cls.__dict__: \end_layout \begin_layout LyX-Code if not methodname.startswith("__") \backslash \end_layout \begin_layout LyX-Code or methodname in ["__call__", "__call___"]: \end_layout \begin_layout LyX-Code method = getattr(cls, methodname) \end_layout \begin_layout LyX-Code if isinstance(method, types.MethodType): \end_layout \begin_layout LyX-Code enhance_method(cls, methodname, decorate) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def traced(enter_text="-->", leave_text="<--"): \end_layout \begin_layout LyX-Code """Configurable Function decorator. \end_layout \begin_layout LyX-Code Outputs the text given as enter_text plus the function name and arguments \end_layout \begin_layout LyX-Code before entering the decorated function, and the leave_text string after \end_layout \begin_layout LyX-Code having left the decorated function. \end_layout \begin_layout LyX-Code Example: \end_layout \begin_layout LyX-Code >>> @traced("into", "out of") \end_layout \begin_layout LyX-Code ... def foo(x,y): \end_layout \begin_layout LyX-Code ... print x,y \end_layout \begin_layout LyX-Code ... return x \end_layout \begin_layout LyX-Code ... \end_layout \begin_layout LyX-Code >>> foo(1, 2) \end_layout \begin_layout LyX-Code into foo(args=(1, 2), kwargs={}) \end_layout \begin_layout LyX-Code 1 2 \end_layout \begin_layout LyX-Code out of foo() \end_layout \begin_layout LyX-Code 1 \end_layout \begin_layout LyX-Code >>> \end_layout \begin_layout LyX-Code """ \end_layout \begin_layout LyX-Code def traced(f): \end_layout \begin_layout LyX-Code _f = f \end_layout \begin_layout LyX-Code def f(*args, **kwargs): \end_layout \begin_layout LyX-Code print enter_text, "%s(args=%s, kwargs=%s)" % (_f.__name__, args, \end_layout \begin_layout LyX-Code kwargs) \end_layout \begin_layout LyX-Code try: \end_layout \begin_layout LyX-Code return _f(*args, **kwargs) \end_layout \begin_layout LyX-Code finally: \end_layout \begin_layout LyX-Code print leave_text, "%s()" % (_f.__name__) \end_layout \begin_layout LyX-Code f.__name__ = _f.__name__ \end_layout \begin_layout LyX-Code return f \end_layout \begin_layout LyX-Code return traced \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def enhance_module(module, decorator=traced, names=[]): \end_layout \begin_layout LyX-Code """Wrap module class methods and callables with given decorator. Applies \end_layout \begin_layout LyX-Code to list of names only, if given, otherwise to all detected \end_layout \begin_layout LyX-Code methods/callables. \end_layout \begin_layout LyX-Code """ \end_layout \begin_layout LyX-Code for objname, obj in module.__dict__.items(): \end_layout \begin_layout LyX-Code if not names or objname in names: \end_layout \begin_layout LyX-Code if isinstance(obj, (type, types.ClassType)): \end_layout \begin_layout LyX-Code #print obj \end_layout \begin_layout LyX-Code enhance_all_methods(obj, decorator()) \end_layout \begin_layout LyX-Code elif callable(obj): \end_layout \begin_layout LyX-Code #print obj \end_layout \begin_layout LyX-Code module.__dict__[objname] = decorator()(obj) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Based on Python mailing list post \end_layout \begin_layout LyX-Code # http://mail.python.org/pipermail/python-list/2005-April/319662.html \end_layout \begin_layout LyX-Code # as posted by Bengt Richter \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code class TraceLocals(object): \end_layout \begin_layout LyX-Code """Tracing of local function or method variable names, as of leaving the \end_layout \begin_layout LyX-Code function. \end_layout \begin_layout LyX-Code Can be used standalone or easiest by applying the method decorator static \end_layout \begin_layout LyX-Code method "trace". \end_layout \begin_layout LyX-Code """ \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code from sys import settrace \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def __init__(self, fnames=[], vnames=[], logger=None): \end_layout \begin_layout LyX-Code """TraceLocals constructor. \end_layout \begin_layout LyX-Code Parameters: \end_layout \begin_layout LyX-Code fnames: List of function or method names to be traced. \end_layout \begin_layout LyX-Code vnames: List of local variable names to output. If list is \end_layout \begin_layout LyX-Code empty, all local variables will be printed. \end_layout \begin_layout LyX-Code """ \end_layout \begin_layout LyX-Code self.fnames = set(fnames) \end_layout \begin_layout LyX-Code self.vnames = set(vnames) \end_layout \begin_layout LyX-Code if logger: \end_layout \begin_layout LyX-Code self._print = logger.debug \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def _gwatch(self, frame, event, arg): \end_layout \begin_layout LyX-Code """ \end_layout \begin_layout LyX-Code Global scope watcher. When a new scope is entered, returns the local \end_layout \begin_layout LyX-Code scope watching method _lwatch to do the work for that. \end_layout \begin_layout LyX-Code """ \end_layout \begin_layout LyX-Code if event == 'call': \end_layout \begin_layout LyX-Code name = frame.f_code.co_name # name of executing scope \end_layout \begin_layout LyX-Code if name in self.fnames: \end_layout \begin_layout LyX-Code return self._lwatch \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def _lwatch(self, frame, event, arg): \end_layout \begin_layout LyX-Code if event == 'return': \end_layout \begin_layout LyX-Code vnames = self.vnames or frame.f_locals.keys() \end_layout \begin_layout LyX-Code for vname in vnames: \end_layout \begin_layout LyX-Code if frame.f_locals.has_key(vname): \end_layout \begin_layout LyX-Code self._print("%s: %s = %s", \end_layout \begin_layout LyX-Code frame.f_code.co_name, vname, \end_layout \begin_layout LyX-Code frame.f_locals[vname]) \end_layout \begin_layout LyX-Code else: \end_layout \begin_layout LyX-Code return self._lwatch # keep watching for return event \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on(self): \end_layout \begin_layout LyX-Code """Set the system trace hook to enable tracing. \end_layout \begin_layout LyX-Code """ \end_layout \begin_layout LyX-Code self.settrace(self._gwatch) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def off(self): \end_layout \begin_layout LyX-Code """Reset the system trace hook to disable tracing. \end_layout \begin_layout LyX-Code """ \end_layout \begin_layout LyX-Code self.settrace(None) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def _print(self, msg, *args): \end_layout \begin_layout LyX-Code print msg % args \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code @staticmethod \end_layout \begin_layout LyX-Code def trace(vnames=[], logger=None): \end_layout \begin_layout LyX-Code """Method/function decorator that enables tracing of local variables, as \end_layout \begin_layout LyX-Code of leaving the function. \end_layout \begin_layout LyX-Code Parameters: \end_layout \begin_layout LyX-Code vnames: List of variable names that shall be traced. If list is \end_layout \begin_layout LyX-Code empty, all local variables will be printed. \end_layout \begin_layout LyX-Code logger: Optional logger instance. If given, ouput will be routed \end_layout \begin_layout LyX-Code through logger's debug method. \end_layout \begin_layout LyX-Code """ \end_layout \begin_layout LyX-Code def _decorate(func): \end_layout \begin_layout LyX-Code tracer = TraceLocals([func.__name__, ], vnames, logger=logger) \end_layout \begin_layout LyX-Code def _f(*args, **kwargs): \end_layout \begin_layout LyX-Code tracer.on() \end_layout \begin_layout LyX-Code try: \end_layout \begin_layout LyX-Code return func(*args, **kwargs) \end_layout \begin_layout LyX-Code finally: \end_layout \begin_layout LyX-Code tracer.off() \end_layout \begin_layout LyX-Code _f.__name__ = func.__name__ \end_layout \begin_layout LyX-Code return _f \end_layout \begin_layout LyX-Code return _decorate \end_layout \begin_layout Standard Using this, the ZSI server now logs the incoming client message: \end_layout \begin_layout LyX-Code $ /apps/pydev/hjoukl/bin/python2.4 myTracedFinancialServer.py -l "DEBUG" \end_layout \begin_layout LyX-Code *** ZSI version (2, 1, 0) *** \end_layout \begin_layout LyX-Code ---- ZSI.TCcompound.ComplexType ---- \end_layout \begin_layout LyX-Code [DEBUG] parse \end_layout \begin_layout LyX-Code [DEBUG] ofwhat: (, ) \end_layout \begin_layout LyX-Code [DEBUG] what: (http://services.zsiserver.net/FinancialService_NS,irate) \end_layout \begin_layout LyX-Code [DEBUG] child node: (http://services.zsiserver.net/FinancialService_NS,ns1:i rate) \end_layout \begin_layout LyX-Code [DEBUG] what: (http://services.zsiserver.net/FinancialService_NS,CFSequence) \end_layout \begin_layout LyX-Code [DEBUG] child node: (http://services.zsiserver.net/FinancialService_NS,ns1:C FSequence) \end_layout \begin_layout LyX-Code [DEBUG] parse \end_layout \begin_layout LyX-Code [DEBUG] ofwhat: (,) \end_layout \begin_layout LyX-Code [DEBUG] what: (http://services.zsiserver.net/FinancialService_NS,CF) \end_layout \begin_layout LyX-Code [DEBUG] child node: (http://services.zsiserver.net/FinancialService_NS,ns1:C F) \end_layout \begin_layout LyX-Code [DEBUG] child node: (http://services.zsiserver.net/FinancialService_NS,ns1:C F) \end_layout \begin_layout LyX-Code adevp02 - - [18/Jan/2008 13:38:41] "POST /FinancialService# HTTP/1.1" 200 - \end_layout \begin_layout LyX-Code ---- ---- \end_layout \begin_layout LyX-Code [DEBUG] do_POST: xml = \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code 7.000000 \end_layout \begin_layout LyX-Code -100.000000 \end_layout \begin_layout LyX-Code 110.000000 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout Standard \newpage \end_layout \begin_layout Bibliography \bibitem {ZSIrefdoc2.0} Salz, Rich; Blunck, Christopher: ZSI: The Zolera Soap Infrastructure. , Release 2.0.0, February 01, 2007. \end_layout \begin_layout Bibliography \bibitem {ZSIuserguide2.0} Boverhof, Joshua; Moad, Charles: ZSI: The Zolera Soap Infrastructure Userīs Guide. , Release 2.0.0, February 01, 2007. \end_layout \begin_layout Bibliography \bibitem {ZSIrefdoc2.1} Salz, Rich; Blunck, Christopher: ZSI: The Zolera Soap Infrastructure. , Release 2.1.0, November 01, 2007. \end_layout \begin_layout Bibliography \bibitem {ZSIuserguide2.1} Boverhof, Joshua; Moad, Charles: ZSI: The Zolera Soap Infrastructure Userīs Guide. , Release 2.1.0, November 01, 2007. \end_layout \begin_layout Bibliography \bibitem {gSOAPdoc} van Engelen, Robert: gSOAP 2.7.3 User Guide. , June 27, 2005. \end_layout \begin_layout Bibliography \bibitem {whichWSDL} Butek, Russell: Which style of WSDL should I use? , May 24, 2005. \end_layout \begin_layout Bibliography \bibitem {InteropHOLGER} Joukl, Holger: Interoperable WSDL/SOAP web services introduction: Python ZSI, Excel XP, gSOAP C/C++ & Applix SS). . July 22, 2005. \end_layout \begin_layout Bibliography \bibitem {HobbsCookbook} Hobbs, Chris: Using ZSI. , August 1, 2007. \end_layout \begin_layout Bibliography \bibitem {LokadTutorial} Locad.com: Web Services Tutorial with Python. , January 2, 2008. \end_layout \end_body \end_document