#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
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
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:
\en