|
From: <bov...@us...> - 2007-06-08 21:59:36
|
Revision: 1392
http://svn.sourceforge.net/pywebsvcs/?rev=1392&view=rev
Author: boverhof
Date: 2007-06-08 14:59:37 -0700 (Fri, 08 Jun 2007)
Log Message:
-----------
M test/test_callhome.py
A ZSI/twisted/wsgi.py
M ZSI/twisted/__init__.py
M ZSI/twisted/WSresource.py
-- moving some stuff around, like to use soap handler stuff for all WSGI
-- Added a "DeferHandlerChain" for twisted,
did some minor refactoring to "render_POST" in order to support it, doesn't affect existing apps.
This will add all soap "handler" callbacks into a Deferred, thus returning control to the reactor
before processing the soap request/response messages.
To use this processing model just set appropriate chain factory protocol
from ZSI.twisted.WSresource import *
DefaultHandlerChainFactory.protocol = DeferHandlerChain
WSAddressHandlerChainFactory.protocol = DeferHandlerChain
WSSecurityHandlerChainFactory
WSResource.factory = DefaultHandlerChainFactory
# or
WSResource.factory = WSAddressHandlerChainFactory
# or
WSResource.factory =
Modified Paths:
--------------
trunk/zsi/ZSI/twisted/WSresource.py
trunk/zsi/ZSI/twisted/__init__.py
trunk/zsi/test/test_callhome.py
Added Paths:
-----------
trunk/zsi/ZSI/twisted/wsgi.py
Modified: trunk/zsi/ZSI/twisted/WSresource.py
===================================================================
--- trunk/zsi/ZSI/twisted/WSresource.py 2007-06-08 21:31:45 UTC (rev 1391)
+++ trunk/zsi/ZSI/twisted/WSresource.py 2007-06-08 21:59:37 UTC (rev 1392)
@@ -24,65 +24,13 @@
from ZSI.address import Address
from ZSI.ServiceContainer import WSActionException
+from interfaces import CheckInputArgs, HandlerChainInterface, CallbackChainInterface,\
+ DataHandler, DefaultHandlerChain
+
#
# Stability: Unstable
-#
-
-class HandlerChainInterface(Interface):
-
- def processRequest(self, input, **kw):
- """returns a representation of the request, the
- last link in the chain must return a response
- pyobj with a typecode attribute.
- Parameters:
- input --
- Keyword Parameters:
- request -- HTTPRequest instance
- resource -- Resource instance
- """
- def processResponse(self, output, **kw):
- """returns a string representing the soap response.
- Parameters
- output --
- Keyword Parameters:
- request -- HTTPRequest instance
- resource -- Resource instance
- """
-
-class CallbackChainInterface(Interface):
-
- def processRequest(self, input, **kw):
- """returns a response pyobj with a typecode
- attribute.
- Parameters:
- input --
- Keyword Parameters:
- request -- HTTPRequest instance
- resource -- Resource instance
- """
-
-class DataHandler:
- """
- class variables:
- readerClass -- factory class to create reader for ParsedSoap instances.
- writerClass -- ElementProxy implementation to use for SoapWriter instances.
- """
- classProvides(HandlerChainInterface)
- readerClass = None
- writerClass = None
-
- @classmethod
- def processRequest(cls, input, **kw):
- return ParsedSoap(input, readerclass=cls.readerClass)
-
- @classmethod
- def processResponse(cls, output, **kw):
- sw = SoapWriter(outputclass=cls.writerClass)
- sw.serialize(output)
- return sw
-
-
+#
class DefaultCallbackHandler:
classProvides(CallbackChainInterface)
@@ -218,26 +166,12 @@
raise
return rsp_pyobj
+
-
-def CheckInputArgs(*interfaces):
- """Must provide at least one interface, the last one may be repeated.
+class DeferHandlerChain:
+ """Each handler is
"""
- l = len(interfaces)
- def wrapper(func):
- def check_args(self, *args, **kw):
- for i in range(len(args)):
- if (l > i and interfaces[i].providedBy(args[i])) or interfaces[-1].providedBy(args[i]):
- continue
- if l > i: raise TypeError, 'arg %s does not implement %s' %(args[i], interfaces[i])
- raise TypeError, 'arg %s does not implement %s' %(args[i], interfaces[-1])
- func(self, *args, **kw)
- return check_args
- return wrapper
-
-class DefaultHandlerChain:
-
@CheckInputArgs(CallbackChainInterface, HandlerChainInterface)
def __init__(self, cb, *handlers):
self.handlercb = cb
@@ -245,31 +179,51 @@
self.debug = len(log.theLogPublisher.observers) > 0
def processRequest(self, arg, **kw):
- if self.debug:
- log.msg('--->PROCESS REQUEST\n%s' %arg, debug=1)
-
+ from twisted.internet import reactor
+ from twisted.internet.defer import Deferred
+
+ debug = self.debug
+ if debug: log.msg('--->DEFER PROCESS REQUEST: %s' %arg, debug=1)
+
+ d = Deferred()
for h in self.handlers:
- arg = h.processRequest(arg, **kw)
+ if debug:
+ log.msg('\t%s handler: %s' %(arg, h), debug=1)
+ log.msg('\thandler callback: %s' %h.processRequest)
+ #arg = h.processRequest(arg, **kw)
+ d.addCallback(h.processRequest, **kw)
- return self.handlercb.processRequest(arg, **kw)
+ #return self.handlercb.processRequest(arg, **kw)
+ d.addCallback(self.handlercb.processRequest, **kw)
+ reactor.callLater(.0001, d.callback, arg)
+ #return d
- def processResponse(self, arg, **kw):
- if self.debug:
- log.msg('===>PROCESS RESPONSE: %s' %str(arg), debug=1)
+ #def processResponse(self, arg, **kw):
+ #from twisted.internet.defer import Deferred
+
+ #debug = self.debug
+ if debug: log.msg('===>DEFER PROCESS RESPONSE: %s' %str(arg), debug=1)
- if arg is None:
- return
-
+ #if arg is None:
+ # return d
+
for h in self.handlers:
- arg = h.processResponse(arg, **kw)
+ if debug: log.msg('\t%s handler: %s' %(arg, h), debug=1)
+ #arg = h.processResponse(arg, **kw)
+ d.addCallback(h.processResponse, **kw)
- s = str(arg)
- if self.debug:
- log.msg(s, debug=1)
+ #s = str(arg)
+ #if debug: log.msg(s, debug=1)
+ #return s
+
+ # convert SoapWriter into a string
+ d.addCallback(str)
+ return d
+
+ def processResponse(self, arg, **kw):
+ return arg
- return s
-
class DefaultHandlerChainFactory:
protocol = DefaultHandlerChain
@@ -285,8 +239,8 @@
def newInstance(cls):
return cls.protocol(WSAddressCallbackHandler, DataHandler,
WSAddressHandler())
-
+
class WSResource(twisted.web.resource.Resource, object):
"""
class variables:
@@ -302,7 +256,7 @@
"""
twisted.web.resource.Resource.__init__(self)
- def _writeResponse(self, request, response, status=200):
+ def _writeResponse(self, response, request, status=200):
"""
request -- request message
response --- response message
@@ -318,38 +272,32 @@
request.setHeader("Content-Length", str(len(response)))
request.write(response)
request.finish()
- return NOT_DONE_YET
- def _writeFault(self, request, ex):
+ def _writeFault(self, fail, request):
"""
+ fail -- failure
request -- request message
ex -- Exception
"""
- response = None
- response = fault.FaultFromException(ex, False, sys.exc_info()[2]).AsSOAP()
- log.err('SOAP FAULT: %s' % response)
- return self._writeResponse(request, response, status=500)
+ response = fault.FaultFromException(fail.value, False, fail.tb).AsSOAP()
+ self._writeResponse(response, request, status=500)
def render_POST(self, request):
"""Dispatch Method called by twisted render, creates a
request/response handler chain.
request -- twisted.web.server.Request
"""
+ from twisted.internet.defer import maybeDeferred
+
chain = self.factory.newInstance()
data = request.content.read()
- try:
- pyobj = chain.processRequest(data, request=request, resource=self)
- except Exception, ex:
- return self._writeFault(request, ex)
+
+ d = maybeDeferred(chain.processRequest, data, request=request, resource=self)
+ d.addCallback(chain.processResponse, request=request, resource=self)
+ d.addCallback(self._writeResponse, request)
+ d.addErrback(self._writeFault, request)
+
+ return NOT_DONE_YET
- try:
- soap = chain.processResponse(pyobj, request=request, resource=self)
- except Exception, ex:
- return self._writeFault(request, ex)
- if soap is not None:
- return self._writeResponse(request, soap)
- request.finish()
- return NOT_DONE_YET
-
Modified: trunk/zsi/ZSI/twisted/__init__.py
===================================================================
--- trunk/zsi/ZSI/twisted/__init__.py 2007-06-08 21:31:45 UTC (rev 1391)
+++ trunk/zsi/ZSI/twisted/__init__.py 2007-06-08 21:59:37 UTC (rev 1392)
@@ -4,4 +4,5 @@
# $Id$
###########################################################################
-__all__=['WSresource', 'WSsecurity']
+__all__=['interfaces', 'client', 'WSresource', 'WSsecurity']
+import interfaces
\ No newline at end of file
Added: trunk/zsi/ZSI/twisted/wsgi.py
===================================================================
--- trunk/zsi/ZSI/twisted/wsgi.py (rev 0)
+++ trunk/zsi/ZSI/twisted/wsgi.py 2007-06-08 21:59:37 UTC (rev 1392)
@@ -0,0 +1,186 @@
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See Copyright for copyright notice!
+# $Id: __init__.py 1132 2006-02-17 01:55:41Z boverhof $
+###########################################################################
+import os, sys, warnings
+from StringIO import StringIO
+
+# twisted & related imports
+from zope.interface import classProvides, implements, Interface
+from twisted.python import log, failure
+#from twisted.web.error import NoResource
+#from twisted.web.server import NOT_DONE_YET
+#import twisted.web.http
+#import twisted.web.resource
+
+# ZSI imports
+from ZSI import _get_element_nsuri_name, EvaluateException, ParseException
+from ZSI import fault, ParsedSoap, SoapWriter
+
+
+# WS-Address related imports
+from ZSI.address import Address
+
+
+class WSAddressException(Exception):
+ """
+ """
+
+from ZSI.twisted.interfaces import HandlerChainInterface, CallbackChainInterface,\
+ DataHandler, CheckInputArgs, DefaultHandlerChain
+
+
+
+class DefaultCallbackHandler:
+ classProvides(CallbackChainInterface)
+
+ @classmethod
+ def processRequest(cls, ps, **kw):
+ """invokes callback that should return a (request,response) tuple.
+ representing the SOAP request and response respectively.
+ ps -- ParsedSoap instance representing HTTP Body.
+ request -- twisted.web.server.Request
+ """
+ #env = kw['env']
+ #start_response = kw['start_response']
+ resource = kw['resource']
+ #request = kw['request']
+ method = getattr(resource, 'soap_%s' %
+ _get_element_nsuri_name(ps.body_root)[-1])
+
+ try:
+ req_pyobj,rsp_pyobj = method(ps)
+ except TypeError, ex:
+ log.err(
+ 'ERROR: service %s is broken, method MUST return request, response'\
+ % cls.__name__
+ )
+ raise
+ except Exception, ex:
+ log.err('failure when calling bound method')
+ raise
+
+ return rsp_pyobj
+
+
+class DefaultHandlerChainFactory:
+ protocol = DefaultHandlerChain
+
+ @classmethod
+ def newInstance(cls):
+ return cls.protocol(DefaultCallbackHandler, DataHandler)
+
+
+
+class WSGIApplication(dict):
+ encoding = "UTF-8"
+
+ def __call__(self, env, start_response):
+ """do dispatching, else process
+ """
+ script = env['SCRIPT_NAME'] # consumed
+ ipath = os.path.split(env['PATH_INFO'])[1:]
+ for i in range(1, len(ipath)+1):
+ path = os.path.join(*ipath[:i])
+ print "PATH: ", path
+ application = self.get(path)
+ if application is not None:
+ env['SCRIPT_NAME'] = script + path
+ env['PATH_INFO'] = ''
+ print "SCRIPT: ", env['SCRIPT_NAME']
+ return application(env, start_response)
+
+ return self._request_cb(env, start_response)
+
+ def _request_cb(self, env, start_response):
+ """callback method, override
+ """
+ start_response("404 ERROR", [('Content-Type','text/plain')])
+ return ['Move along people, there is nothing to see to hear']
+
+
+class SOAPApplication(WSGIApplication):
+ """
+ """
+ factory = DefaultHandlerChainFactory
+ soapAction = {}
+ root = {}
+
+ def __init__(self, **kw):
+ dict.__init__(self, **kw)
+ self.delegate = None
+
+ def _request_cb(self, env, start_response):
+ """process request,
+ """
+ if env['REQUEST_METHOD'] == 'GET':
+ return self._handle_GET(env, start_response)
+
+ if env['REQUEST_METHOD'] == 'POST':
+ return self._handle_POST(env, start_response)
+
+ start_response("500 ERROR", [('Content-Type','text/plain')])
+ s = StringIO()
+ h = env.items(); h.sort()
+ for k,v in h:
+ print >>s, k,'=',`v`
+ return [s.getvalue()]
+
+ def _handle_GET(self, env, start_response):
+ if env['QUERY_STRING'].lower() == 'wsdl':
+ start_response("200 OK", [('Content-Type','text/plain')])
+ return ['NO WSDL YET']
+
+ start_response("404 ERROR", [('Content-Type','text/plain')])
+ return ['NO RESOURCE FOR GET']
+
+ def _handle_POST(self, env, start_response):
+ """Dispatch Method called by twisted render, creates a
+ request/response handler chain.
+ request -- twisted.web.server.Request
+ """
+ input = env['wsgi.input']
+ data = input.read( int(env['CONTENT_LENGTH']) )
+ mimeType = "text/xml"
+ if self.encoding is not None:
+ mimeType = 'text/xml; charset="%s"' % self.encoding
+
+ request = None
+ resource = self.delegate or self
+ chain = self.factory.newInstance()
+ try:
+ pyobj = chain.processRequest(data, request=request, resource=resource)
+ except Exception, ex:
+ start_response("500 ERROR", [('Content-Type',mimeType)])
+ return [fault.FaultFromException(ex, False, sys.exc_info()[2]).AsSOAP()]
+
+ try:
+ soap = chain.processResponse(pyobj, request=request, resource=resource)
+ except Exception, ex:
+ start_response("500 ERROR", [('Content-Type',mimeType)])
+ return [fault.FaultFromException(ex, False, sys.exc_info()[2]).AsSOAP()]
+
+ start_response("200 OK", [('Content-Type',mimeType)])
+ return [soap]
+
+
+def test(app, port=8080, host="localhost"):
+ """
+ """
+ import sys
+ from twisted.internet import reactor
+ from twisted.python import log
+ from twisted.web2.channel import HTTPFactory
+ from twisted.web2.server import Site
+ from twisted.web2.wsgi import WSGIResource
+
+ log.startLogging(sys.stdout)
+ reactor.listenTCP(port,
+ HTTPFactory( Site(WSGIResource(app)) ),
+ interface=host,
+ )
+ reactor.run()
+
+
+
Modified: trunk/zsi/test/test_callhome.py
===================================================================
--- trunk/zsi/test/test_callhome.py 2007-06-08 21:31:45 UTC (rev 1391)
+++ trunk/zsi/test/test_callhome.py 2007-06-08 21:59:37 UTC (rev 1392)
@@ -4,7 +4,7 @@
from ZSI.wstools.logging import gridLog
os.environ['GRIDLOG_ON'] = '1'
-os.environ['GRIDLOG_DEST'] = "gridlog-udp://portnoy.lbl.gov:15100"
+os.environ['GRIDLOG_DEST'] = "gridlog-udp://netlogger.lbl.gov:15100"
class TestCase(unittest.TestCase):
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|