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

contrib/opal/ZSI/ZSI/twisted/WSresource.py

00001 ###########################################################################
00002 # Joshua R. Boverhof, LBNL
00003 # See Copyright for copyright notice!
00004 # $Id: WSresource.py 1423 2007-11-01 20:33:33Z boverhof $
00005 ###########################################################################
00006 
00007 import sys, warnings
00008 
00009 # twisted & related imports
00010 from zope.interface import classProvides, implements, Interface
00011 from twisted.python import log, failure
00012 from twisted.web.error import NoResource
00013 from twisted.web.server import NOT_DONE_YET
00014 import twisted.web.http
00015 import twisted.web.resource
00016 
00017 # ZSI imports
00018 from ZSI import _get_element_nsuri_name, EvaluateException, ParseException
00019 from ZSI.parse import ParsedSoap
00020 from ZSI.writer import SoapWriter
00021 from ZSI import fault
00022 
00023 # WS-Address related imports
00024 from ZSI.address import Address
00025 from ZSI.ServiceContainer import WSActionException
00026 
00027 from interfaces import CheckInputArgs, HandlerChainInterface, CallbackChainInterface,\
00028     DataHandler
00029 
00030 
00031 class LoggingHandlerChain:
00032 
00033     @CheckInputArgs(CallbackChainInterface, HandlerChainInterface)
00034     def __init__(self, cb, *handlers):
00035         self.handlercb = cb
00036         self.handlers = handlers
00037         self.debug = len(log.theLogPublisher.observers) > 0
00038         
00039     def processRequest(self, arg, **kw):
00040         debug = self.debug
00041         if debug: log.msg('--->PROCESS REQUEST: %s' %arg, debug=1)
00042         
00043         for h in self.handlers:
00044             if debug: log.msg('\t%s handler: %s' %(arg, h), debug=1)
00045             arg = h.processRequest(arg, **kw)
00046             
00047         return self.handlercb.processRequest(arg, **kw)
00048             
00049     def processResponse(self, arg, **kw):
00050         debug = self.debug
00051         if debug: log.msg('===>PROCESS RESPONSE: %s' %str(arg), debug=1)
00052 
00053         if arg is None: 
00054             return
00055 
00056         for h in self.handlers:
00057             if debug: log.msg('\t%s handler: %s' %(arg, h), debug=1)
00058             arg = h.processResponse(arg, **kw)
00059             
00060         s = str(arg)
00061         if debug: log.msg(s, debug=1)
00062         
00063         return s
00064 
00065 
00066 # 
00067 # Stability: Unstable
00068 #     
00069 class DefaultCallbackHandler:
00070     classProvides(CallbackChainInterface)
00071 
00072     @classmethod
00073     def processRequest(cls, ps, **kw):
00074         """invokes callback that should return a (request,response) tuple.
00075         representing the SOAP request and response respectively.
00076         ps -- ParsedSoap instance representing HTTP Body.
00077         request -- twisted.web.server.Request
00078         """
00079         resource = kw['resource']
00080         request = kw['request']
00081         method =  getattr(resource, 'soap_%s' %
00082                            _get_element_nsuri_name(ps.body_root)[-1])
00083                                               
00084         try:
00085             req_pyobj,rsp_pyobj = method(ps, request=request)
00086         except TypeError, ex:
00087             log.err(
00088                 'ERROR: service %s is broken, method MUST return request, response'\
00089                     % cls.__name__
00090             )
00091             raise
00092         except Exception, ex:
00093             log.err('failure when calling bound method')
00094             raise
00095         
00096         return rsp_pyobj
00097     
00098 
00099 class WSAddressHandler:
00100     """General WS-Address handler.  This implementation depends on a 
00101     'wsAction' dictionary in the service stub which contains keys to 
00102     WS-Action values.  
00103 
00104     Implementation saves state on request response flow, so using this 
00105     handle is not  reliable if execution is deferred between proceesRequest 
00106     and processResponse.  
00107 
00108     TODO: sink this up with wsdl2dispatch
00109     TODO: reduce coupling with WSAddressCallbackHandler.
00110     """
00111     implements(HandlerChainInterface)
00112     
00113     def processRequest(self, ps, **kw):
00114         # TODO: Clean this up
00115         resource = kw['resource']
00116 
00117         d = getattr(resource, 'root', None)
00118         key = _get_element_nsuri_name(ps.body_root)
00119         if d is None or d.has_key(key) is False:
00120             raise RuntimeError,\
00121                 'Error looking for key(%s) in root dictionary(%s)' %(key, str(d))
00122 
00123         self.op_name = d[key]
00124         self.address = address = Address()
00125         address.parse(ps)
00126         action = address.getAction()
00127         if not action:
00128             raise WSActionException('No WS-Action specified in Request')
00129 
00130         request = kw['request']
00131         http_headers = request.getAllHeaders()
00132         soap_action = http_headers.get('soapaction')
00133         if soap_action and soap_action.strip('\'"') != action:
00134             raise WSActionException(\
00135                 'SOAP Action("%s") must match WS-Action("%s") if specified.'\
00136                 %(soap_action,action)
00137             )
00138             
00139         # Save WS-Address in ParsedSoap instance.
00140         ps.address = address
00141         return ps
00142         
00143     def processResponse(self, sw, **kw):
00144         if sw is None:
00145             self.address = None
00146             return
00147         
00148         request, resource = kw['request'], kw['resource']
00149         if isinstance(request, twisted.web.http.Request) is False:
00150             raise TypeError, '%s instance expected' %http.Request
00151                 
00152         d = getattr(resource, 'wsAction', None)
00153         key = self.op_name
00154         if d is None or d.has_key(key) is False:
00155             raise WSActionNotSpecified,\
00156                 'Error looking for key(%s) in wsAction dictionary(%s)' %(key, str(d))
00157 
00158         addressRsp = Address(action=d[key])
00159         if request.transport.TLS == 0:
00160             addressRsp.setResponseFromWSAddress(\
00161                  self.address, 'http://%s:%d%s' %(
00162                  request.host.host, request.host.port, request.path)
00163             )
00164         else:
00165             addressRsp.setResponseFromWSAddress(\
00166                  self.address, 'https://%s:%d%s' %(
00167                  request.host.host, request.host.port, request.path)
00168             )
00169             
00170         addressRsp.serialize(sw, typed=False)
00171         self.address = None
00172         return sw
00173     
00174 
00175 class WSAddressCallbackHandler:
00176     classProvides(CallbackChainInterface)
00177 
00178     @classmethod
00179     def processRequest(cls, ps, **kw):
00180         """invokes callback that should return a (request,response) tuple.
00181         representing the SOAP request and response respectively.
00182         ps -- ParsedSoap instance representing HTTP Body.
00183         request -- twisted.web.server.Request
00184         """
00185         resource = kw['resource']
00186         request = kw['request']
00187         method =  getattr(resource, 'wsa_%s' %
00188                            _get_element_nsuri_name(ps.body_root)[-1])
00189                                               
00190         # TODO: grab ps.address, clean this up.
00191         try:
00192             req_pyobj,rsp_pyobj = method(ps, ps.address, request=request)
00193         except TypeError, ex:
00194             log.err(
00195                 'ERROR: service %s is broken, method MUST return request, response'\
00196                     %self.__class__.__name__
00197             )
00198             raise
00199         except Exception, ex:
00200             log.err('failure when calling bound method')
00201             raise
00202         
00203         return rsp_pyobj
00204             
00205 
00206 class DeferHandlerChain:
00207     """Each handler is 
00208     """
00209 
00210     @CheckInputArgs(CallbackChainInterface, HandlerChainInterface)
00211     def __init__(self, cb, *handlers):
00212         self.handlercb = cb
00213         self.handlers = handlers
00214         self.debug = len(log.theLogPublisher.observers) > 0
00215         
00216     def processRequest(self, arg, **kw):
00217         from twisted.internet import reactor
00218         from twisted.internet.defer import Deferred
00219         
00220         debug = self.debug
00221         if debug: log.msg('--->DEFER PROCESS REQUEST: %s' %arg, debug=1)
00222         
00223         d = Deferred()
00224         for h in self.handlers:
00225             if debug: 
00226                 log.msg('\t%s handler: %s' %(arg, h), debug=1)
00227                 log.msg('\thandler callback: %s' %h.processRequest)
00228             d.addCallback(h.processRequest, **kw)
00229             
00230         d.addCallback(self.handlercb.processRequest, **kw)
00231         reactor.callLater(.0001, d.callback, arg)
00232 
00233         if debug: log.msg('===>DEFER PROCESS RESPONSE: %s' %str(arg), debug=1)
00234 
00235 
00236         
00237         for h in self.handlers:
00238             if debug: log.msg('\t%s handler: %s' %(arg, h), debug=1)
00239             d.addCallback(h.processResponse, **kw)
00240 
00241         d.addCallback(str)
00242         return d
00243     
00244     def processResponse(self, arg, **kw):
00245         return arg
00246 
00247 
00248 class DefaultHandlerChainFactory:
00249     protocol = LoggingHandlerChain
00250     
00251     @classmethod
00252     def newInstance(cls):
00253         return cls.protocol(DefaultCallbackHandler, DataHandler)
00254     
00255 
00256 class WSAddressHandlerChainFactory:
00257     protocol = DefaultHandlerChain
00258     
00259     @classmethod
00260     def newInstance(cls):
00261         return cls.protocol(WSAddressCallbackHandler, DataHandler, 
00262             WSAddressHandler())
00263 
00264 
00265 class WSResource(twisted.web.resource.Resource, object):
00266     """
00267     class variables:
00268         encoding  --
00269         factory -- hander chain, which has a factory method "newInstance"
00270                       that returns a 
00271     """
00272     encoding = "UTF-8"
00273     factory = DefaultHandlerChainFactory
00274 
00275     def __init__(self):
00276         """
00277         """
00278         twisted.web.resource.Resource.__init__(self)
00279 
00280     def _writeResponse(self, response, request, status=200):
00281         """
00282         request -- request message
00283         response --- response message
00284         status -- HTTP Status
00285         """
00286         request.setResponseCode(status)
00287         if self.encoding is not None:
00288             mimeType = 'text/xml; charset="%s"' % self.encoding
00289         else:
00290             mimeType = "text/xml"
00291 
00292         request.setHeader("Content-Type", mimeType)
00293         request.setHeader("Content-Length", str(len(response)))
00294         request.write(response)
00295         request.finish()
00296 
00297     def _writeFault(self, fail, request):
00298         """
00299         fail -- failure
00300         request -- request message
00301         ex -- Exception 
00302         """
00303         response = fault.FaultFromException(fail.value, False, fail.tb).AsSOAP() 
00304         self._writeResponse(response, request, status=500)
00305 
00306     def render_POST(self, request):
00307         """Dispatch Method called by twisted render, creates a 
00308         request/response handler chain.
00309         request -- twisted.web.server.Request
00310         """
00311         from twisted.internet.defer import maybeDeferred
00312         
00313         chain = self.factory.newInstance()
00314         data = request.content.read()
00315         
00316         d = maybeDeferred(chain.processRequest, data, request=request, resource=self)
00317         d.addCallback(chain.processResponse, request=request, resource=self)
00318         d.addCallback(self._writeResponse, request)
00319         d.addErrback(self._writeFault, request)
00320         
00321         return NOT_DONE_YET
00322 
00323 
00324 
00325 
00326 
00327 class DefaultHandlerChain:
00328 
00329     @CheckInputArgs(CallbackChainInterface, HandlerChainInterface)
00330     def __init__(self, cb, *handlers):
00331         self.handlercb = cb
00332         self.handlers = handlers
00333         self.debug = len(log.theLogPublisher.observers) > 0
00334         
00335     def processRequest(self, arg, **kw):
00336         debug = self.debug
00337         if debug: log.msg('--->PROCESS REQUEST: %s' %arg, debug=1)
00338         
00339         for h in self.handlers:
00340             if debug: log.msg('\t%s handler: %s' %(arg, h), debug=1)
00341             arg = h.processRequest(arg, **kw)
00342             
00343         return self.handlercb.processRequest(arg, **kw)
00344             
00345     def processResponse(self, arg, **kw):
00346         debug = self.debug
00347         if debug: log.msg('===>PROCESS RESPONSE: %s' %str(arg), debug=1)
00348 
00349         if arg is None: 
00350             return
00351 
00352         for h in self.handlers:
00353             if debug: log.msg('\t%s handler: %s' %(arg, h), debug=1)
00354             arg = h.processResponse(arg, **kw)
00355             
00356         s = str(arg)
00357         if debug: log.msg(s, debug=1)
00358         
00359         return s
00360 

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