\chapter{Examples}

This chapter contains a number of examples to show off some of \ZSI{}'s 
features.  It is broken down into client-side and server-side examples, and 
explores different implementation options \ZSI{} provides.

\section{Server Side Examples}
\subsection{Simple example}
Using the \module{ZSI.dispatch} module, it is simple to expose Python functions
as web services.  Each function is invoked with all the input parameters
specified in the client's SOAP request.  Any value returned by the function will
be serialized back to the client; multiple values can be returned by returning a
tuple.

The following code shows some simple services:

\input{dispatch-rpc-hello-array}

Each function defines a SOAP request, so if this script is installed
as a CGI script, a SOAP message can be posted to that script's URL with any of
\code{hello}, \code{echo}, or \code{average} as the request element,
and the value returned by the function will be sent back.  These functions
expect and return SOAP-ENC:arrayType instances which are marshalled into python
\class{list} instances, this script interoperates with the 
\class{client.Binding}.  For more information see \emph{Appendix A}.

The \ZSI{} CGI dispatcher catches exceptions and sends back a SOAP fault.
For example, a fault will be sent if the  \code{hello} function is given any
arguments, or if the \code{average} function is given a non-integer.

Here is another example but using SOAP-ENC:Struct instances which are marshalled
into python \class{dict} instances, this script interoperates with the
\class{client.NamedParamBinding}.  For more information see \emph{Appendix B}.

\input{dispatch-rpc-hello-struct}

\subsection{low level soap processing example}

We will now show a more complete example of a robust web service implemented at
the SOAP layer. It takes as input a player name and array of integers, and returns
the average.  It is presented in sections, following the steps detailed above. 
A complete working example of this service is available in \emph{Appendix C}.

The first section reads in a request, and parses the SOAP header.

\begin{verbatim}
from ZSI import *
import sys
IN, OUT = sys.stdin, sys.stdout
try:
    ps = ParsedSoap(IN)
except ParseException, e:
    OUT.write(FaultFromZSIException(e).AsSOAP())
    sys.exit(1)
except Exception, e:
    # Faulted while processing; we assume it's in the header.
    OUT.write(FaultFromException(e, 1).AsSOAP())
    sys.exit(1)

# We are not prepared to handle any actors or mustUnderstand elements,
# so we'll arbitrarily fault back with the first one we found.
a = ps.WhatActorsArePresent()
if len(a):
    OUT.write(FaultFromActor(a[0]).AsSOAP())
    sys.exit(1)
mu = ps.WhatMustIUnderstand()
if len(mu):
    uri, localname = mu[0]
    OUT.write(FaultFromNotUnderstood(uri, localname).AsSOAP())
    sys.exit(1)
\end{verbatim}

This section defines the mappings between Python objects and the SOAP
data being transmitted.  Recall that according to the SOAP specification, RPC
input and output are modeled as a structure.

\begin{verbatim}
class Player:
    def __init__(self, *args):
        if not len(args): return
        self.Name = args[0]
        self.Scores = args[1:]
Player.typecode = TC.Struct(Player, [
                                TC.String('Name'),
                                TC.Array('Integer', TC.Integer(), 'Scores', undeclared=True),
                                ], 'GetAverage')
class Average:
    def __init__(self, average=None):
        self.average = average
Average.typecode = TC.Struct(Average, [
                                TC.Integer('average'),
                                ], 'GetAverageResponse')
\end{verbatim}

This section parses the input, performs the application-level
activity, and serializes the response.
\begin{verbatim}
try:
    player = ps.Parse(Player.typecode)
except EvaluateException, e:
    OUT.write(FaultFromZSIException(e).AsSOAP())
    sys.exit(1)

try:
    total = 0
    for value in player.Scores: total = total + value
    result = Average(total / len(player.Scores))
    sw = SoapWriter()
    sw.serialize(result, Average.typecode)
    sw.close()
    OUT.write(str(sw))
except Exception, e:
    OUT.write(FaultFromException(e, 0, sys.exc_info()[2]).AsSOAP())
    sys.exit(1)
\end{verbatim}

In the \method{serialize()} call above, the second parameter is optional, since
\code{result} is an instance of the \class{Average} class, and the 
\code{Average.typecode} attribute is the typecode for class instances.


\subsection{A mod_python example}

The Apache module \code{mod_python} (see
\url{http://www.modpython.org}) embeds Python within the Apache server.
In order to expose operations within a module via mod_python, use the 
\method{dispatch.AsHandler()} function.  The \method{dispatch.AsHandler()}
function will dispatch requests to any operation defined in the module you
pass it, which allows for multiple operations to be defined in a module.
The only trick is to use __import__ to load the XML encodings your service 
expects.  This is a required workaround to avoid the pitfalls of restricted
execution with respect to XML parsing.

The following is a complete example of a simple handler.  The soap operations
are implemented in the MyHandler module:

\begin{verbatim}
def hello():
    return {"value":"Hello, world"}

def echo(**kw):
    return kw

def sum(**kw):
    sum = 0
    for i in kw.values(): sum += i
	return {"value":sum}

def average(**kw):
	d = sum(**kw)
    d["value"] = d["value"]/len(kw)
	return d
\end{verbatim}

Dispatching from within mod_python is achieved by passing the aforementined
MyHandler module to \code{dispatch.AsHandler()}.  The following code exposes
the operations defined in MyHandler via SOAP:

\begin{verbatim}
from ZSI import dispatch
from mod_python import apache

import MyHandler
mod = __import__('encodings.utf_8', globals(), locals(), '*')
mod = __import__('encodings.utf_16_be', globals(), locals(), '*')

def handler(req):
    dispatch.AsHandler(modules=(MyHandler,), request=req)
    return apache.OK
\end{verbatim}


\section{Client Side Examples}

\subsection{Simple Example}
\ZSI{} provides two ways for a client to interactive with a server:
the \class{Binding} or \class{NamedParamBinding} class and the
\class{ServiceProxy} class.  The first is useful when the operations to be
invoked are not defined in WSDL or when only simple Python datatypes are used;
the \class{ServiceProxy} class can be used to parse WSDL definitions in order
to determine how to serialize and parse the SOAP messages.

During development, it is often useful to record ``packet traces'' of
the SOAP messages being exchanged.  Both the \class{Binding} and
\class{ServiceProxy} classes provide a \code{tracefile} parameter to specify an
output stream (such as a file) to capture messages.  It can be particularly
useful when debugging unexpected SOAP faults.

The first example provided below demonstrates how to use the \class{NamedParamBinding}
class to connect to a remote service and perform an operation.

\input{client-hello-struct}

\subsection{Complex Example: pickler.py}
If the operation invoked returns a ComplexType, typecode information must
be provided in order to tell \ZSI{} how to deserialize the response.
Here is a sample server-side implementation (for the complete example see
\emph{Appendix D}):

\begin{verbatim}
class Person:
  def __init__(self, name=None, age=0):
    self.name = name
    self.age = age

Person.typecode = TC.Struct(Person,
			    [TC.String('name'),
			     TC.InonNegativeInteger('age')],
			    'myApp:Person')

# my web service that returns a complex structure
def getPerson(name):
  fp = open('%s.person.pickle', % name, 'r')
  return pickle.load(fp)

# my web service that accepts a complex structure
def savePerson(person):
  fp = open('%s.person.pickle' % person.name, 'w')
  pickle(person, fp)
  fp.close()
\end{verbatim}

In order for \ZSI{} to transparently deserialize the returned complex type into
a \class{Person} instance, a module defining the class and its typecode can be
passed into the \class{Binding}. It is also possible to explicitly tell \ZSI{} 
what typecode to use by passing it as a parameter to the \method{Binding.Receive()} 
method.  

The following fragment shows both styles:

\begin{verbatim}
import sys
from ZSI.client import Binding
from MyComplexTypes import Person

b = Binding(url='http://localhost/test3/pickler.py', tracefile=sys.stdout)
person = Person('christopher', 26)
rsp = b.savePerson(person)

\end{verbatim}

Because the returned complex type is defined in a class present in 
\var{typesmodule}, transparent deserialization is possible.  When sending
complex types to the server, it is not necessary to list the module
in \var{typesmodule}:

\begin{verbatim}
import sys
import MyComplexTypes
from ZSI.client import NamedParamBinding as NPBinding, Binding
from ZSI import TC

kw = {'url':'http://localhost/test3/pickler.py', 'tracefile':sys.stdout}
b = NPBinding(**kw)
rsp = b.getPerson(name='christopher')
assert type(rsp) is dict, 'expecting a dict'
assert rsp['Person']['name'] == 'christopher', 'wrong person'

b = NPBinding(typesmodule=MyComplexTypes, **kw)
rsp = b.getPerson(name='christopher')
assert isinstance(rsp['Person'], MyComplexTypes.Person), (
    'expecting instance of %s' %MyComplexTypes.Person)

b = Binding(typesmodule=MyComplexTypes, **kw)
class Name(str):
    typecode = TC.String("name")

rsp = b.getPerson(Name('christopher'))
assert isinstance(rsp['Person'], MyComplexTypes.Person), (
    'expecting instance of %s' %MyComplexTypes.Person)

\end{verbatim}



