• Main Page
  • Related Pages
  • Modules
  • Namespaces
  • Data Structures
  • Files
  • File List
  • Globals

contrib/opal/ZSI/build/lib/ZSI/twisted/wsgi.py

00001 ############################################################################
00002 # Joshua R. Boverhof, LBNL
00003 # See Copyright for copyright notice!
00004 # $Id: $
00005 ###########################################################################
00006 import os, sys, types, inspect
00007 from StringIO import StringIO
00008 
00009 # twisted & related imports
00010 from zope.interface import classProvides, implements, Interface
00011 
00012 # ZSI imports
00013 from ZSI import _get_element_nsuri_name, EvaluateException, ParseException,\
00014     fault, ParsedSoap, SoapWriter
00015 from ZSI.twisted.reverse import DataHandler, ReverseHandlerChain,\
00016     HandlerChainInterface
00017 
00018 """
00019 EXAMPLES:
00020 
00021      See zsi/samples/WSGI
00022 
00023 
00024 """
00025 
00026 def soapmethod(requesttypecode, responsetypecode, soapaction='', 
00027                operation=None, **kw):
00028     """@soapmethod
00029     decorator function for soap methods
00030     """
00031     def _closure(func_cb):
00032         func_cb.root = (requesttypecode.nspname,requesttypecode.pname)
00033         func_cb.action = soapaction
00034         func_cb.requesttypecode = requesttypecode
00035         func_cb.responsetypecode = responsetypecode
00036         func_cb.soapmethod = True
00037         func_cb.operation = None
00038         return func_cb
00039 
00040     return _closure
00041 
00042 
00043 class SOAPCallbackHandler:
00044     """ ps --> pyobj, pyobj --> sw
00045     class variables:
00046         writerClass -- ElementProxy implementation to use for SoapWriter instances.
00047     """
00048     classProvides(HandlerChainInterface)
00049     writerClass = None
00050 
00051     @classmethod
00052     def processRequest(cls, ps, **kw):
00053         """invokes callback that should return a (request,response) tuple.
00054         representing the SOAP request and response respectively.
00055         ps -- ParsedSoap instance representing HTTP Body.
00056         request -- twisted.web.server.Request
00057         """
00058         resource = kw['resource']
00059         request = kw['request']
00060 
00061         root = _get_element_nsuri_name(ps.body_root)
00062         for key,method in inspect.getmembers(resource, inspect.ismethod):
00063             if (getattr(method, 'soapmethod', False) and method.root == root):
00064                 break
00065         else:
00066             raise RuntimeError, 'Missing soap callback method for root "%s"' %root
00067 
00068         try:
00069             req = ps.Parse(method.requesttypecode)
00070         except Exception, ex:
00071             raise
00072         try:
00073             rsp = method.responsetypecode.pyclass()
00074         except Exception, ex:
00075             raise
00076         
00077         try:
00078             req,rsp = method(req, rsp)
00079         except Exception, ex:
00080             raise
00081 
00082         return rsp
00083 
00084     @classmethod
00085     def processResponse(cls, output, **kw):
00086         sw = SoapWriter(outputclass=cls.writerClass)
00087         sw.serialize(output)
00088         return sw
00089     
00090     
00091 class SOAPHandlerChainFactory:
00092     protocol = ReverseHandlerChain
00093 
00094     @classmethod
00095     def newInstance(cls):
00096         return cls.protocol(DataHandler, SOAPCallbackHandler)
00097 
00098 
00099 class WSGIApplication(dict):
00100     encoding = "UTF-8"
00101     
00102     def __call__(self, env, start_response):
00103         """do dispatching, else process
00104         """
00105         script = env['SCRIPT_NAME'] # consumed
00106         ipath = os.path.split(env['PATH_INFO'])[1:]
00107         for i in range(1, len(ipath)+1):
00108             path = os.path.join(*ipath[:i])
00109             print "PATH: ", path
00110             application = self.get(path)
00111             if application is not None:
00112                 env['SCRIPT_NAME'] = script + path
00113                 env['PATH_INFO'] =  ''
00114                 print "SCRIPT: ", env['SCRIPT_NAME']
00115                 return application(env, start_response)
00116             
00117         return self._request_cb(env, start_response)
00118 
00119     def _request_cb(self, env, start_response):
00120         """callback method, override
00121         """
00122         start_response("404 ERROR", [('Content-Type','text/plain')])
00123         return ['Move along people, there is nothing to see to hear']
00124     
00125     def putChild(self, path, resource):
00126         """
00127         """
00128         path = path.split('/')
00129         lp = len(path)
00130         if lp == 0:
00131             raise RuntimeError, 'bad path "%s"' %path
00132         
00133         if lp == 1:
00134             self[path[0]] = resource
00135         
00136         for i in range(len(path)):
00137             if not path[i]: continue
00138             break
00139         
00140         next = self.get(path[i], None)
00141         if next is None:
00142             next = self[path[i]] = WSGIApplication()
00143             
00144         next.putChild('/'.join(path[-1:]), resource)
00145         
00146         
00147 
00148 
00149 class SOAPApplication(WSGIApplication):
00150     """
00151     """
00152     factory = SOAPHandlerChainFactory
00153     
00154     def __init__(self, **kw):
00155         dict.__init__(self, **kw)
00156         self.delegate = None
00157         
00158     def _request_cb(self, env, start_response):
00159         """process request, 
00160         """
00161         if env['REQUEST_METHOD'] == 'GET':
00162             return self._handle_GET(env, start_response)
00163 
00164         if env['REQUEST_METHOD'] == 'POST':
00165             return self._handle_POST(env, start_response)
00166             
00167         start_response("500 ERROR", [('Content-Type','text/plain')])
00168         s = StringIO()
00169         h = env.items(); h.sort()
00170         for k,v in h:
00171             print >>s, k,'=',`v`
00172         return [s.getvalue()]
00173 
00174     def _handle_GET(self, env, start_response):
00175         if env['QUERY_STRING'].lower() == 'wsdl':
00176             start_response("200 OK", [('Content-Type','text/plain')])
00177             r = self.delegate or self
00178             return _resourceToWSDL(r)
00179 
00180         start_response("404 ERROR", [('Content-Type','text/plain')])
00181         return ['NO RESOURCE FOR GET']
00182     
00183     def _handle_POST(self, env, start_response):
00184         """Dispatch Method called by twisted render, creates a
00185         request/response handler chain.
00186         request -- twisted.web.server.Request
00187         """
00188         input = env['wsgi.input']
00189         data = input.read( int(env['CONTENT_LENGTH']) )
00190         mimeType = "text/xml"
00191         if self.encoding is not None:
00192             mimeType = 'text/xml; charset="%s"' % self.encoding
00193 
00194         request = None
00195         resource = self.delegate or self
00196         chain = self.factory.newInstance()
00197         try:
00198             pyobj = chain.processRequest(data, request=request, resource=resource)
00199         except Exception, ex:
00200             start_response("500 ERROR", [('Content-Type',mimeType)])
00201             return [fault.FaultFromException(ex, False, sys.exc_info()[2]).AsSOAP()]
00202 
00203         try:
00204             soap = chain.processResponse(pyobj, request=request, resource=resource)
00205         except Exception, ex:
00206             start_response("500 ERROR", [('Content-Type',mimeType)])
00207             return [fault.FaultFromException(ex, False, sys.exc_info()[2]).AsSOAP()]
00208         
00209         start_response("200 OK", [('Content-Type',mimeType)])
00210         return [soap]
00211     
00212     
00213 def test(app, port=8080, host="localhost"):
00214     """
00215     """
00216     from twisted.internet import reactor
00217     from twisted.python import log
00218     from twisted.web2.channel import HTTPFactory
00219     from twisted.web2.server import Site
00220     from twisted.web2.wsgi import WSGIResource
00221     
00222     log.startLogging(sys.stdout)
00223     reactor.listenTCP(port, 
00224         HTTPFactory( Site(WSGIResource(app)) ),
00225         interface=host,
00226     )
00227     reactor.run()
00228 
00229 
00230 def _issoapmethod(f):
00231     return type(f) is types.MethodType and getattr(f, 'soapmethod', False)
00232 
00233 def _resourceToWSDL(resource):
00234     from xml.etree import ElementTree
00235     from xml.etree.ElementTree import Element, QName
00236     from ZSI.wstools.Namespaces import WSDL
00237     
00238     r = resource
00239     methods = filter(_issoapmethod, map(lambda i: getattr(r, i), dir(r)))
00240     tns = ''
00241     
00242     #tree = ElementTree()
00243     defs = Element("{%s}definitions" %WSDL.BASE)
00244     defs.attrib['name'] = 'SampleDefs'
00245     defs.attrib['targetNamespace'] = tns
00246     #tree.append(defs)
00247     
00248     porttype = Element("{%s}portType" %WSDL)
00249     porttype.attrib['name'] = QName("{%s}SamplePortType" %tns)
00250     
00251     binding = Element("{%s}binding" %WSDL)
00252     defs.append(binding)
00253     binding.attrib['name'] = QName("{%s}SampleBinding" %tns)
00254     binding.attrib['type'] = porttype.get('name')
00255     
00256     for m in methods:
00257         m.action
00258         
00259     service = Element("{%s}service" %WSDL.BASE)
00260     defs.append(service)
00261     service.attrib['name'] = 'SampleService'
00262     
00263     port = Element("{%s}port" %WSDL.BASE)
00264     service.append(port)
00265     port.attrib['name'] = "SamplePort"
00266     port.attrib['binding'] = binding.get('name')
00267     
00268     soapaddress = Element("{%s}address" %WSDL.BIND_SOAP)
00269     soapaddress.attrib['location'] = 'http://localhost/bla'
00270     port.append(soapaddress)
00271     
00272     return [ElementTree.tostring(defs)]
00273     
00274    
00275     
00276 """
00277 <?xml version="1.0" encoding="UTF-8"?>
00278 <wsdl:definitions name="Counter" targetNamespace="http://counter.com/bindings" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:porttype="http://counter.com" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
00279   <wsdl:import namespace="http://counter.com" location="counter_flattened.wsdl"/>
00280   <wsdl:binding name="CounterPortTypeSOAPBinding" type="porttype:CounterPortType">
00281     <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
00282     <wsdl:operation name="createCounter">
00283       <soap:operation soapAction="http://counter.com/CounterPortType/createCounterRequest"/>
00284       <wsdl:input>
00285         <soap:body use="literal"/>
00286       </wsdl:input>
00287       <wsdl:output>
00288         <soap:body use="literal"/>
00289       </wsdl:output>
00290     </wsdl:operation>
00291 
00292 
00293 <wsdl:definitions name="Counter" targetNamespace="http://counter.com/service" 
00294 xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:binding="http://counter.com/bindings">
00295   <wsdl:import namespace="http://counter.com/bindings" location="counter_bindings.wsdl"/>
00296   <wsdl:service name="CounterService">
00297     <wsdl:port name="CounterPortTypePort" binding="binding:CounterPortTypeSOAPBinding">
00298       <soap:address location="http://localhost:8080/wsrf/services/"/>
00299     </wsdl:port>
00300   </wsdl:service>
00301 </wsdl:definitions>
00302 """
00303     

Generated on Wed Oct 20 2010 11:12:16 for APBS by  doxygen 1.7.2