[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[gnue] r7888 - in trunk/gnue-common/src: datasources/drivers/other rpc r
From: |
johannes |
Subject: |
[gnue] r7888 - in trunk/gnue-common/src: datasources/drivers/other rpc rpc/drivers rpc/drivers/xmlrpc rpc/drivers/xmlrpc/pw_xmlrpc |
Date: |
Tue, 6 Sep 2005 12:37:19 -0500 (CDT) |
Author: johannes
Date: 2005-09-06 12:37:17 -0500 (Tue, 06 Sep 2005)
New Revision: 7888
Added:
trunk/gnue-common/src/rpc/drivers/xmlrpc/ClientAdapter.py
trunk/gnue-common/src/rpc/drivers/xmlrpc/ServerAdapter.py
trunk/gnue-common/src/rpc/drivers/xmlrpc/typeconv.py
Removed:
trunk/gnue-common/src/rpc/drivers/xmlrpc/pw_xmlrpc/ServerAdapter.py
trunk/gnue-common/src/rpc/drivers/xmlrpc/py_xmlrpc/
Modified:
trunk/gnue-common/src/datasources/drivers/other/appserver.py
trunk/gnue-common/src/rpc/client.py
trunk/gnue-common/src/rpc/drivers/Base.py
trunk/gnue-common/src/rpc/drivers/xmlrpc/RpcDoc.py
trunk/gnue-common/src/rpc/drivers/xmlrpc/__init__.py
trunk/gnue-common/src/rpc/server.py
Log:
First step of reorganizing rpc (including some improvements)
Modified: trunk/gnue-common/src/datasources/drivers/other/appserver.py
===================================================================
--- trunk/gnue-common/src/datasources/drivers/other/appserver.py
2005-09-02 20:23:33 UTC (rev 7887)
+++ trunk/gnue-common/src/datasources/drivers/other/appserver.py
2005-09-06 17:37:17 UTC (rev 7888)
@@ -324,7 +324,6 @@
Base.Connection.__init__ (self, connections, name, parameters)
self.__filters = None
- self.__server = None
self._sm = None
@@ -501,17 +500,14 @@
def __getSessionManager (self):
- if self.__server is None:
+ if self._sm is None:
params = {'host' : self.parameters.get ('host'),
'port' : self.parameters.get ('port'),
'transport': self.parameters.get ('transport')}
+ if 'timeout' in self.parameters:
+ params ['timeout'] = float (self.parameters ['timeout'])
+
rpcType = self.parameters.get ('rpctype')
- self.__server = client.attach (rpcType, params)
+ self._sm = client.attach (rpcType, params)
- if self.parameters.has_key ('timeout'):
- self.__server.setTimeout (float (self.parameters ['timeout']))
-
- if self._sm is None:
- self._sm = self.__server.request ('Session')
-
return self._sm
Modified: trunk/gnue-common/src/rpc/client.py
===================================================================
--- trunk/gnue-common/src/rpc/client.py 2005-09-02 20:23:33 UTC (rev 7887)
+++ trunk/gnue-common/src/rpc/client.py 2005-09-06 17:37:17 UTC (rev 7888)
@@ -1,4 +1,4 @@
-# GNU Enterprise RPC interface - Client adapter
+# GNU Enterprise Common Library - RPC interface - Client adapter
#
# Copyright 2001-2005 Free Software Foundation
#
@@ -21,8 +21,6 @@
#
# $Id$
-import string
-
from gnue.common.apps import errors, plugin
# =============================================================================
@@ -34,9 +32,23 @@
# -----------------------------------------------------------------------------
def attach (interface, params):
+ """
+ Create a new ClientAdapter of a given rpc driver.
+
+ @param interface: name of the driver to create a L{ClientAdapter} for
+ @param params: dictionary of parameters to pass to the ClientAdapter
+
+ @return: a L{drivers.Base.ServerProxy} instance ready for accessing the
+ remote RPC server.
+ """
+
+ checktype (interface, basestring)
+ checktype (params, dict)
+
driver = plugin.find (interface, 'gnue.common.rpc.drivers', 'ClientAdapter')
- return driver.ClientAdapter (params)
+ return driver.ClientAdapter (params).getServerProxy ()
+
# =============================================================================
# Exceptions
# =============================================================================
@@ -87,7 +99,12 @@
class InvalidParameter (ProgrammingError):
pass
+class AccessDeniedError (errors.AdminError):
+ def __init__ (self, host):
+ msg = u_("Access to services at '%s' denied") % host
+ errors.AdminError.__init__ (self, msg)
+
# =============================================================================
# Self test code - requires server.py running
# =============================================================================
@@ -97,34 +114,34 @@
import traceback
import sys
from gnue.common.apps import errors
+ import datetime
+ import mx.DateTime
- if (len(sys.argv)==2) and \
- (sys.argv[1] in
['pyro','xmlrpc','socket','soap','xmlrpc.pw_xmlrpc','xmlrpc.py_xmlrpc']):
- connection = attach (sys.argv[1], {})
+ if len (sys.argv) == 2 and \
+ sys.argv [1] in ['pyro', 'xmlrpc', 'socket', 'soap']:
+ obj = attach (sys.argv [1], {'port': 8765})
else:
- print """
-GNUe RPC test client
+ print "GNUe RPC test client\n\nUsage: gcvs client.py <transport>"
+ sys.exit (1)
-Usage: gcvs client.py <transport>
-"""
- raise SystemExit
+ print 'stringtest:', repr (obj.stringtest ('This'))
- obj = connection.request ('test')
-
- print 'stringtest:', repr (obj.stringtest ('This'))
try:
- print 'ustringtest:', (obj.ustringtest (u'\xe0\xe2\xe1\xe4')).encode
('utf-8')
+ print 'ustringtest:', \
+ (obj.ustringtest (u'\xe0\xe2\xe1\xe4')).encode ('utf-8')
except:
print "UnicodeStringtest failed"
+
print 'inttest: 21 * 2 =', repr (obj.inttest (21))
print 'floattest: 123.45 * 2 =', repr (obj.floattest (123.45))
print 'datetimetest:', repr (obj.datetimetest ())
print 'booltest:', repr (obj.booltest ())
+
#subobj = obj.objtest ()
#print 'objtest:', repr (subobj)
-# print 'subobj.test'
-# subobj.test ()
+ # print 'subobj.test'
+ # subobj.test ()
#del subobj
print 'testing exception ...'
@@ -150,6 +167,24 @@
print "remote exception detail:", e.getDetail ()
print "-" * 70
+ print "-" * 70
+ o = None
+ print "Sending %r (%s) to a roundtrip ..." % (o, type (o))
+ v = obj.roundtrip (o)
+ print "Result:", repr (v), type (v)
+
+ o = {None: 'foobar', u'Unicode-Key': 2,
+ 'int': 2, 'long': 3L, 'float': 2.34, 'False': False,
+ 'True': True, 'None': None, 'tuple': ('a', 2), 'list': ['b', 3],
+ 'date': datetime.date.today (),
+ 'time': datetime.datetime.today ().time (),
+ 'datetime': datetime.datetime.today (),
+ 'mx.DateTime': mx.DateTime.now (),
+ 'mx.DateTimeDelta': mx.DateTime.DateTimeDelta (0, 1, 2, 3.456)}
+ print "Sending %r (%s) to a roundtrip ..." % (o, type (o))
+ v = obj.roundtrip (o)
+ print "Result:", repr (v), type (v)
+
print 'shutting down server ...'
try:
# This will raise an exception because the server will not even answer any
Modified: trunk/gnue-common/src/rpc/drivers/Base.py
===================================================================
--- trunk/gnue-common/src/rpc/drivers/Base.py 2005-09-02 20:23:33 UTC (rev
7887)
+++ trunk/gnue-common/src/rpc/drivers/Base.py 2005-09-06 17:37:17 UTC (rev
7888)
@@ -1,4 +1,4 @@
-# GNU Enterprise RPC interface - Base for all drivers
+# GNU Enterprise Common Library - RPC interface - Base for all drivers
#
# Copyright 2001-2005 Free Software Foundation
#
@@ -21,20 +21,72 @@
#
# $Id$
-from types import *
-
-import string
import sys
-import thread
import traceback
import urlparse
+import types
from gnue.common.apps import i18n
+from gnue.common.rpc import server
# Indicate that this is not a valid plugin
__noplugin__ = True
+
# =============================================================================
+# Server Proxy
+# =============================================================================
+
+class ServerProxy:
+ """
+ A ServerProxy provides access to the RPC server, where each attribute is
+ encapsulated by a L{ProxyMethod} instance. Such an instance executes the
+ encapsulated method (the former attribute) on the RPC server.
+ """
+
+ # ---------------------------------------------------------------------------
+ # Constructor
+ # ---------------------------------------------------------------------------
+
+ def __init__ (self, adapter):
+ """
+ Create a new proxy for the given adapter
+
+ @param adapter: L{Server} instance to send all requests to
+ """
+
+ self._adapter = adapter
+
+
+ # ---------------------------------------------------------------------------
+ # attribute access
+ # ---------------------------------------------------------------------------
+
+ def __getattr__ (self, name):
+ """
+ Wrap a L{ProxyMethod} around the requested attribute name.
+
+ @param name: (method)-name to be wrapped within a ProxyMethod
+ """
+
+ result = ProxyMethod (self._adapter, name)
+ self.__dict__ [name] = result
+
+ return result
+
+
+ # ---------------------------------------------------------------------------
+ # Nice string represenation
+ # ---------------------------------------------------------------------------
+
+ def __repr__ (self):
+ return "<%s>" % self
+
+ def __str__ (self):
+ return "ServerProxy for %s" % self._adapter._url
+
+
+# =============================================================================
# Client adapter
# =============================================================================
@@ -42,18 +94,22 @@
"""
Basic client adapter
"""
- _default_transport = None
- _default_host = 'localhost'
- _default_port = None
- _default_path = '/'
+ _default_transport_ = None
+ _default_host_ = 'localhost'
+ _default_port_ = None
+ _default_path_ = '/'
+
+ _serverProxyClass_ = ServerProxy
+
+
# ---------------------------------------------------------------------------
- # Initialize object
+ # Constructor
# ---------------------------------------------------------------------------
def __init__ (self, params):
- checktype (params, DictionaryType)
+ checktype (params, dict)
self._url = None
self._transport = None
@@ -66,21 +122,20 @@
# Connection defined as URL
(self._transport, netloc, self._path, params, query, fragment) \
= urlparse.urlparse (params ['url'])
- (self._host, self._port) = split (netloc, ':', 1)
+ (self._host, self._port) = netloc.split (':', 1)
else:
-
# Connection defined as transport/host/port/path info
- if params.has_key ('transport'): self._transport = params ['transport']
- if params.has_key ('host' ): self._host = params ['host' ]
- if params.has_key ('port' ): self._port = params ['port' ]
- if params.has_key ('path' ): self._path = params ['path' ]
+ self._transport = params.get ('transport')
+ self._host = params.get ('host')
+ self._port = params.get ('port')
+ self._path = params.get ('path')
# If some info isn't given, fall back to default values
- if not self._transport: self._transport = self._default_transport
- if not self._host: self._host = self._default_host
- if not self._port: self._port = self._default_port
- if not self._path: self._path = self._default_path
+ if not self._transport: self._transport = self._default_transport_
+ if not self._host: self._host = self._default_host_
+ if not self._port: self._port = self._default_port_
+ if not self._path: self._path = self._default_path_
# Make sure port is an integer
self._port = int (self._port)
@@ -88,14 +143,20 @@
# Now build the full URL
self._url = '%s://%s:%d%s' % (self._transport, self._host, self._port,
self._path)
+ self._timeout = params.get ('timeout', 1.0)
- if params.has_key ('timeout'):
- self._timeout = params ['timeout']
- else:
- self._timeout = 1.0
- self.__open = True
+ # ---------------------------------------------------------------------------
+ # Nice string representation
+ # ---------------------------------------------------------------------------
+ def __repr__ (self):
+ return "<%s>" % self
+
+ def __str__ (self):
+ return "RPC-CLientAdapter for %s" % self._url
+
+
# ---------------------------------------------------------------------------
# Set timeout
# ---------------------------------------------------------------------------
@@ -104,190 +165,192 @@
self._timeout = timeout
- # ---------------------------------------------------------------------------
- # Request a (static) proxy object
- # ---------------------------------------------------------------------------
- def request (self, service):
-
- return ProxyObject (self, service, False)
-
# ---------------------------------------------------------------------------
- # Close the connection
+ # Request a proxy object
# ---------------------------------------------------------------------------
- def close ():
+ def getServerProxy (self):
"""
- Close the connection to the server
+ Create a new proxy object to the remote server.
+
+ @returns: L{ServerProxy} instance handling attribute access and method
calls
"""
- if self.__open:
- self._close ()
- # ---------------------------------------------------------------------------
- # Clean up
- # ---------------------------------------------------------------------------
+ gEnter (8)
+ return gLeave (8, self._getServerProxy_ ())
- def __del__ (self):
- if self.__open:
- self._close ()
+ # ===========================================================================
+ # Virtual functions
+ # ===========================================================================
# ---------------------------------------------------------------------------
- # Run a procedure on the server (abstract)
+ # Create a new server proxy object
# ---------------------------------------------------------------------------
- def _runMethod (self, method, *args, **params):
+ def _getServerProxy_ (self):
"""
- execute a remote method
+ Create a new proxy object to the remote server.
+
+ @returns: server proxy object, handling attribute access and method calls
"""
- pass
+ return self._serverProxyClass_ (self)
+
+
# ---------------------------------------------------------------------------
- # Close the server (virtual)
+ # perform a remote procedure call
# ---------------------------------------------------------------------------
- def _close (self):
+ def _callMethod_ (self, method, *args, **kwargs):
+ """
+ Call a method with the given arguments on the remote side. Descendants
+ override this function to do the actual remote procedure call.
- pass
+ @param method: name of the method to call
+ @param args: tuple with all positional arguments
+ @param kwargs: dictionary with all keyword arguments
- # ---------------------------------------------------------------------------
- # Create a dynamic proxy object
- # ---------------------------------------------------------------------------
+ @return: result of the remote procedure call
+ """
- def _createproxy (self, service):
+ raise NotImplementedError
- return ProxyObject (self, service, True)
# =============================================================================
-# Proxy object for clients
+# Proxy method for clients
# =============================================================================
-class ProxyObject:
+class ProxyMethod:
+ """
+ A ProxyMethod provides a callable for a method in a RPC server. This method
+ will be executed by the given L{Client} adapter.
+ """
# ---------------------------------------------------------------------------
- # Initialize proxy object
+ # Initialize proxy method
# ---------------------------------------------------------------------------
- def __init__ (self, adapter, service, dynamic):
+ def __init__ (self, adapter, methodname):
+ """
+ @param adapter: L{Client} instance implementing a L{_callMethod_} function
+ @param methodname: name of the method be called
+ """
- self.__adapter = adapter
- self.__service = service
- self.__dynamic = dynamic
+ self._adapter = adapter
+ self._methodname = methodname
+
# ---------------------------------------------------------------------------
- # Get a (proxy) method
+ # Run the method
# ---------------------------------------------------------------------------
- def __getattr__ (self, attr):
+ def __call__ (self, *args, **params):
+ """
+ Execute the wrapped RPC-Method via L{Client} adapter using the given
+ positional and keyword-arguments.
+
+ Note: not all RPC adapter can handle keyword arguments, so take care.
+ """
- if attr[0] == '_':
- raise AttributeError, attr
+ gEnter (8)
+ return gLeave (8, self._adapter._callMethod_ (self._methodname, *args,
+ **params))
- method = ProxyMethod (self.__adapter, self.__service + '.' + attr)
- self.__dict__ [attr] = method
- return method
# ---------------------------------------------------------------------------
- # Set an object attribute (must start with '_')
+ # Nice string representation
# ---------------------------------------------------------------------------
- def __setattr__ (self, attr, value):
+ def __repr__ (self):
+ return "<%s>" % self
- # FIXME: what do we need this for?
+ def __str__ (self):
+ return "ProxyMethod '%s' of %s" % (self._methodname, self._adapter)
- if attr[0] == '_':
- self.__dict__[attr] = value
- else:
- raise AttributeError, attr
- # ---------------------------------------------------------------------------
- # Clean up
- # ---------------------------------------------------------------------------
- def __del__ (self):
-
- if self.__dynamic:
- self.__adapter._runMethod (self.__service + '._close')
-
# =============================================================================
-# Proxy method for clients
+# Base class for server adapters
# =============================================================================
-class ProxyMethod:
+class Server:
+ """
+ Basic server adapter.
- # ---------------------------------------------------------------------------
- # Initialize proxy method
- # ---------------------------------------------------------------------------
+ @cvar _default_transport_: default transport to use if no transport parameter
+ is given.
+ @cvar _default_port_: default port to use if no port parameter is given
- def __init__ (self, adapter, methodname):
+ @ivar instance: the python instance to be served by this server
+ @ivar allowed_hosts: a sequence of hosts (name/ip-addresses) which are
+ allowed to connect to this server. An empty sequence means 'no restriction'
+ """
- self._adapter = adapter
- self._methodname = methodname
+ _default_transport_ = None
+ _default_port_ = None
+
# ---------------------------------------------------------------------------
- # Run the method
+ # Initialize server object
# ---------------------------------------------------------------------------
- def __call__ (self, *args, **params):
+ def __init__ (self, service, parameters):
+ """
+ Create a new server adapter serving a given python object.
- gDebug (3, "%s (%s)" \
- % (self._methodname,
- string.join ([repr (x) for x in args] + \
- ["%s = %s" % (x [0], repr (x [1])) \
- for x in params.items ()], ', ')))
- return self._adapter._runMethod (self._methodname, *args, **params)
+ @param service: python object to be served
+ @param parameters: dictionary of parameters for the server adapter
+ """
-# =============================================================================
-# Server adapter
-# =============================================================================
+ checktype (service, types.InstanceType)
+ checktype (parameters, dict)
-class Server:
- """
- Basic server adapter
- """
- _default_transport = None
- _default_port = None
+ self.instance = service
- # ---------------------------------------------------------------------------
- # Initialize server object
- # ---------------------------------------------------------------------------
+ self._transport = parameters.get ('transport', self._default_transport_)
+ self._port = parameters.get ('port', self._default_port_)
- def __init__ (self, rpcdef, bindings, params):
- self._bindings = bindings
- self._rpcdef = rpcdef
+ if not self._port:
+ raise server.AdapterConfigurationError, \
+ u_("Required parameter 'port' not supplied")
- checktype (params, DictionaryType)
+ self._port = int (self._port)
+ self._bindto = parameters.get ('bindto', '')
- self._transport = None
- self._port = None
+ self.allowed_hosts = parameters.get ('allowed_hosts', '').split (',')
- if params.has_key ('transport'): self._transport = params ['transport']
- if params.has_key ('port' ): self._port = params ['port' ]
- # If some info isn't given, fall back to default values
- if not self._transport: self._transport = self._default_transport
- if not self._port: self._port = self._default_port
+ # ---------------------------------------------------------------------------
+ # Nice string representation
+ # ---------------------------------------------------------------------------
- # Make sure port is an integer
- self._port = int (self._port)
+ def __repr__ (self):
+ return "<RPC-Server serving '%r' at %x>" % (self.instance, id (self))
+
+
# ---------------------------------------------------------------------------
# Start server
# ---------------------------------------------------------------------------
def serve (self):
- pass
+ """
+ Start the server.
+ """
- # ---------------------------------------------------------------------------
- # Start server as new thread
- # ---------------------------------------------------------------------------
+ gEnter (8)
+ return gLeave (8, self._serve_ ())
- def serveAsNewThread (self):
- thread.start_new_thread (self.serve, ())
# ---------------------------------------------------------------------------
- # Return an exception
+ # Virtual methods to be implemented by descendants
# ---------------------------------------------------------------------------
- def raiseException (self, exception, message, event=None):
- raise exception, message
+ def _serve_ (self):
+ """
+ Start serving the instance.
+ """
+
+ pass
Added: trunk/gnue-common/src/rpc/drivers/xmlrpc/ClientAdapter.py
===================================================================
--- trunk/gnue-common/src/rpc/drivers/xmlrpc/ClientAdapter.py 2005-09-02
20:23:33 UTC (rev 7887)
+++ trunk/gnue-common/src/rpc/drivers/xmlrpc/ClientAdapter.py 2005-09-06
17:37:17 UTC (rev 7888)
@@ -0,0 +1,194 @@
+# GNU Enterprise Common Library - RPC Interface - XML-RPC client
+#
+# Copyright 2001-2005 Free Software Foundation
+#
+# This file is part of GNU Enterprise.
+#
+# GNU Enterprise is free software; you can redistribute it
+# and/or modify it under the terms of the GNU General Public
+# License as published by the Free Software Foundation; either
+# version 2, or (at your option) any later version.
+#
+# GNU Enterprise is distributed in the hope that it will be
+# useful, but WITHOUT ANY WARRANTY; without even the implied
+# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+# PURPOSE. See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public
+# License along with program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# $Id$
+
+import xmlrpclib
+import socket
+
+from gnue.common.apps import errors
+from gnue.common.rpc import client
+from gnue.common.rpc.drivers import Base
+from gnue.common.utils import http
+
+import typeconv
+
+# =============================================================================
+# xml-rpc transport class using a persistent connection
+# =============================================================================
+
+class PersistentTransport (xmlrpclib.Transport):
+ """
+ Handles a persistent HTTP connection to an XML-RPC server. The connection
+ will be established on the first request, and reused by all later requests.
+ The XML-RPC server is supposed to grant persistent connections via HTTP.
+ """
+
+ user_agent = "GNUe XML-RPC"
+
+ # ---------------------------------------------------------------------------
+ # Constructor
+ # ---------------------------------------------------------------------------
+
+ def __init__ (self):
+
+ self.__connection = None
+
+
+ # ---------------------------------------------------------------------------
+ # Send a request to the given host and return it's response
+ # ---------------------------------------------------------------------------
+
+ def request (self, host, handler, request_body, verbose = 0):
+ """
+ Send an xml-request to the given host and return the server's response. If
+ there's no persistent HTTP/1.1 connection available already it will be
+ established.
+
+ @param host: target host to be contacted. This host must include a
+ port-number, e.g. foobar.com:4711
+ @param handler: target RPC handler, usually '/RPC2'
+ @param request_body: XML-RPC request body as created by a xmlrpclib.dumps
()
+ @param verbose: if set to 1 the underlying L{http.HTTPConnection} will
+ print additional debug messages
+
+ @returns: tuple with the server's response to the request (as returned by
+ the L{xmlrpclib.Unmarshaller}
+
+ @raises AccessDeniedError: if this client is not allowed to access the
+ RPC services at the given host
+ @raises ProtocolError: if the server response was another one than 200 (OK)
+ """
+
+ if not self.__connection:
+ self.__connection = http.HTTPConnection (host)
+
+ self.__connection.set_debuglevel (verbose)
+ self.__connection.request ('POST', handler, request_body,
+ {'Content-Type': 'text/xml'})
+
+ response = self.__connection.getresponse ()
+ if response:
+ if response.status == 403:
+ raise client.AccessDeniedError, host
+
+ elif response.status != 200:
+ raise xmlrpclib.ProtocolError (host + handler, response.status,
+ response.reason, response.msg)
+
+ data = response.read ()
+
+ p, u = self.getparser ()
+ p.feed (data)
+ p.close ()
+
+ result = u.close ()
+ else:
+ result = None
+
+ return result
+
+
+
+# =============================================================================
+# XML-RPC client adapter
+# =============================================================================
+
+class ClientAdapter (Base.Client):
+ """
+ Implements an XML-RPC client adapter using persistent HTTP connections as
+ transport.
+ """
+
+ _default_transport = 'http'
+ _default_port = 8765
+
+ # ---------------------------------------------------------------------------
+ # Initialize object
+ # ---------------------------------------------------------------------------
+
+ def __init__ (self, params):
+ """
+ @param params: parameter dictionary for initialization of the adapter
+ """
+
+ Base.Client.__init__ (self, params)
+
+ self._transport = PersistentTransport ()
+ self._verbose = params.get ('loglevel', 0)
+ self.__remote = "%s:%s" % (self._host, self._port)
+
+
+ # ---------------------------------------------------------------------------
+ # Run a procedure on the server
+ # ---------------------------------------------------------------------------
+
+ def _callMethod_ (self, method, *args, **params):
+ """
+ Execute a method on the XML-RPC server and return it's result.
+
+ @param method: name of the method to be executed
+ @param args: tuple with all positional arguments
+ @param kwargs: dictionary with all keyword arguments - XML-RPC does B{NOT}
+ support keyword arguments
+
+ @return: result of the remote procedure call
+
+ @raises RemoteError: if an exception occurred while executing the method on
+ the server, this exception will carry that exception
+ @raises AdminError: if an exception occurs in the socket-layer
+ @raises InvalidParameter: if an argument cannot be converted into a RPC
+ data-type, or a result cannot be converted into a native python type
+ """
+
+ gEnter (9)
+
+ checktype (method, basestring)
+
+ __args = tuple ([typeconv.python_to_rpc (arg, client.InvalidParameter)
+ for arg in args])
+ try:
+ request = xmlrpclib.dumps (__args, method, allow_none = True)
+ result = self._transport.request (self.__remote, "/RPC2", request,
+ self._verbose)
+
+ # If the result is a tuple with only one item, we're only interessted in
+ # that single item
+ if len (result) == 1:
+ result = result [0]
+
+ except xmlrpclib.Fault, e:
+ # If we got a Fault object, transform it into a RemoteError
+ (exType, exName, exMessage, exDetail) = e.faultString.split (u'\x91')
+ raise errors.RemoteError, (exType, exName, exMessage, exDetail)
+
+ except socket.error:
+ raise errors.AdminError, errors.getException () [2]
+
+ return gLeave (9, typeconv.rpc_to_python (result, client.InvalidParameter))
+
+
+ # ---------------------------------------------------------------------------
+ # Nice string representation
+ # ---------------------------------------------------------------------------
+
+ def __str__ (self):
+ return "XML-RPC client adapter for %s" % self.__remote
Property changes on: trunk/gnue-common/src/rpc/drivers/xmlrpc/ClientAdapter.py
___________________________________________________________________
Name: svn:keyword
+ Id
Name: svn:keywords
+ Id
Property changes on: trunk/gnue-common/src/rpc/drivers/xmlrpc/RpcDoc.py
___________________________________________________________________
Name: svn:keyword
+ Id
Added: trunk/gnue-common/src/rpc/drivers/xmlrpc/ServerAdapter.py
===================================================================
--- trunk/gnue-common/src/rpc/drivers/xmlrpc/ServerAdapter.py 2005-09-02
20:23:33 UTC (rev 7887)
+++ trunk/gnue-common/src/rpc/drivers/xmlrpc/ServerAdapter.py 2005-09-06
17:37:17 UTC (rev 7888)
@@ -0,0 +1,282 @@
+# GNU Enterprise Common Library - RPC Interface - xmlrpc ServerAdpater
+#
+# Copyright 2001-2005 Free Software Foundation
+#
+# This file is part of GNU Enterprise
+#
+# GNU Enterprise is free software; you can redistribute it
+# and/or modify it under the terms of the GNU General Public
+# License as published by the Free Software Foundation; either
+# version 2, or (at your option) any later version.
+#
+# GNU Enterprise is distributed in the hope that it will be
+# useful, but WITHOUT ANY WARRANTY; without even the implied
+# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+# PURPOSE. See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public
+# License along with program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# $Id$
+
+import os
+import xmlrpclib
+import SocketServer
+import typeconv
+import datetime
+
+from SimpleXMLRPCServer import SimpleXMLRPCServer
+from gnue.common.rpc import server
+from gnue.common.rpc.drivers import Base
+from gnue.common.apps import errors
+from gnue.common.utils import http
+
+
+
+# =============================================================================
+# Class implementing an XML-RPC server adapter
+# =============================================================================
+
+class ServerAdapter (Base.Server):
+ """
+ Implementation of a XML-RPC server supporting HTTP/1.1 persistent
+ connections. It supports both forking and threading per connection and one
+ can use the 'servertype' parameter set to 'forking' or 'threading' to select
+ this behavior. NOTE: 'forking' is not supported by all platforms; in this
+ case a threading server will be selected automatically.
+
+ @ivar _tpcServer: the TCPServer of the XML-RPC-Server
+ """
+
+ # ---------------------------------------------------------------------------
+ # Constructor
+ # ---------------------------------------------------------------------------
+
+ def __init__ (self, service, parameters):
+ """
+ @param service: python object to be served
+ @param parameters: dictionary of server specific parameters
+ """
+
+ Base.Server.__init__ (self, service, parameters)
+
+ stype = parameters.get ('servertype', 'forking')
+
+ # In order to use a forking server make sure it's supported by the OS
+ if stype == 'forking' and hasattr (os, 'fork'):
+ serverClass = ForkingXMLRPCServer
+ self.__type = 'forking'
+ else:
+ serverClass = ThreadingXMLRPCServer
+ self.__type = 'threading'
+
+ self._tcpServer = serverClass ((self._bindto, self._port),
+ logRequests = parameters.get ('loglevel', 0), adapter = self)
+
+ # Register the python object to be served as well as the introspection
+ # functions (system.list_methods,system.list_help, system.list_signatures)
+ self._tcpServer.register_instance (self.instance)
+ self._tcpServer.register_introspection_functions ()
+
+
+ # ---------------------------------------------------------------------------
+ # Dispatch a method with the given parameters
+ # ---------------------------------------------------------------------------
+
+ def call (self, method, parameters):
+ """
+ Dispatch a method with a given set of parameters
+
+ @param method: method to be dispatched
+ @param parameters: tuple of parameters to call method with. These
+ parameters are given in RPC types.
+
+ @returns: result of the method given in RPC types
+ """
+
+ gEnter (9)
+
+ checktype (method, basestring)
+ checktype (parameters, tuple)
+
+ params = typeconv.rpc_to_python (parameters, server.InvalidParameter)
+ result = self._tcpServer._dispatch (method, params)
+
+ return gLeave (9, typeconv.python_to_rpc (result, server.InvalidParameter))
+
+
+ # ---------------------------------------------------------------------------
+ # Nice string representation
+ # ---------------------------------------------------------------------------
+
+ def __repr__ (self):
+ return "<%s XML-RPC server serving '%s' at %d>" % \
+ (self.__type, self.instance, id (self))
+
+
+ # ---------------------------------------------------------------------------
+ # Start the server
+ # ---------------------------------------------------------------------------
+
+ def _serve_ (self):
+
+ self._tcpServer.serve_forever ()
+
+
+
+# =============================================================================
+# XML-RPC Request handler
+# =============================================================================
+
+class XMLRPCRequestHandler (http.HTTPRequestHandler):
+ """
+ Handle XML-RPC requests sent via HTTP connections.
+
+ @cvar protocol_version: Set to 'HTTP/1.1' so we do have persistent
+ connections.
+ """
+
+ # Make sure to support persistent connections
+ protocol_version = "HTTP/1.1"
+
+
+ # ---------------------------------------------------------------------------
+ # log all requests at debug level 9
+ # ---------------------------------------------------------------------------
+
+ def log_request (self, code = '-', size = '-'):
+ """
+ Log all requests at debug level 9.
+ """
+
+ gDebug (9, '"%s" %s %s' % (self.requestline, code, size))
+
+
+ # ---------------------------------------------------------------------------
+ # Process a POST request
+ # ---------------------------------------------------------------------------
+
+ def do_POST (self):
+ """
+ Process a XML-RPC request. Exceptions are reported by L{xmlrpclib.Fault}
+ instances. Such an instance carries a string consisting of the group, name,
+ message and traceback of the exception, separated by the unicode character
+ u'0x91' (see L{errors.getException}). The underlying connection will be
+ closed only if stated by the headers (e.g. 'Connection: close')
+ """
+
+ try:
+ data = self.rfile.read (int (self.headers ["content-length"]))
+
+ params, method = xmlrpclib.loads (data)
+
+ response = self.server.serverAdapter.call (method, params)
+ response = (response,)
+
+ response = xmlrpclib.dumps (response, methodresponse = 1, allow_none = 1)
+
+ except:
+ stack = u'\x91'.join (errors.getException ())
+ response = xmlrpclib.Fault (1, stack)
+ response = xmlrpclib.dumps (response, methodresponse = 1)
+
+ # Add the following data to the send-queue, but don't write it to the
+ # socket
+ self.send_response (200, flush = False)
+ self.send_header ("Content-type", "text/xml", flush = False)
+ self.send_header ("Content-length", str (len (response)), flush = False)
+ self.end_headers (flush = False)
+
+ # Add the response to the send-queue and finally flush everything to the
+ # socket.
+ self.write (response, flush = True)
+
+ # If a shutdown of the connection is requested do so, although we assume to
+ # have a persistent connection.
+ if self.close_connection:
+ self.connection.shutdown (1)
+
+
+
+# =============================================================================
+# XML-RPC TCP server
+# =============================================================================
+
+class XMLRPCServer (SimpleXMLRPCServer):
+ """
+ A TCP server implementing a XML-RPC server. This class verifies each
+ connection against a list of 'allowed hosts' and if it is not listed, an
+ error 403 (Forbidden) is returned to the client. If the owning
+ L{ServerAdapter} does not provide such a list, all clients are accepted.
+ These verification takes place *before* a new process for the connection is
+ forked or a new thread is started.
+
+ @ivar allow_reuse_address: if True, the port can be reused even if it's in
+ TIME_WAIT state
+ """
+
+ allow_reuse_address = True
+
+ # ---------------------------------------------------------------------------
+ # Constructor
+ # ---------------------------------------------------------------------------
+
+ def __init__ (self, addr, requestHandler = XMLRPCRequestHandler,
+ logRequests = False, allow_none = 1, adapter = None):
+
+ SimpleXMLRPCServer.__init__ (self, addr, requestHandler, logRequests)
+ self.allow_none = allow_none
+ self.serverAdapter = adapter
+
+
+ # ---------------------------------------------------------------------------
+ # Verify a given request
+ # ---------------------------------------------------------------------------
+
+ def verify_request (self, request, client_address):
+ """
+ Verify if the given client connection is allowed to use the services.
+
+ @param request: the already opened socket object
+ @param client_address: tuple with the address and port of the client
+
+ @returns: True, if the client is accepted, False if it is not allowed to
+ use the services. In the latter case a '403' error response will be sent
+ to the socket.
+ """
+
+ allowed = self.serverAdapter and self.serverAdapter.allowed_hosts
+ for host in allowed:
+ if client_address [0][:len (host)] == host:
+ return True
+
+ request.send ('HTTP/1.1 403 Forbidden\r\n\r\n')
+ self.close_request (request)
+
+ return False
+
+
+
+
+# =============================================================================
+# A forking XML-RPC server
+# =============================================================================
+
+class ForkingXMLRPCServer (SocketServer.ForkingMixIn, XMLRPCServer):
+ """
+ A XML-RPC server which forks a new process per connection.
+ """
+ pass
+
+
+# =============================================================================
+# A threading XML-RPC server
+# =============================================================================
+
+class ThreadingXMLRPCServer (SocketServer.ThreadingMixIn, XMLRPCServer):
+ """
+ A XML-RPC server which starts a new thread per connection.
+ """
+ pass
Property changes on: trunk/gnue-common/src/rpc/drivers/xmlrpc/ServerAdapter.py
___________________________________________________________________
Name: svn:keyword
+ Id
Name: svn:keywords
+ Id
Property changes on: trunk/gnue-common/src/rpc/drivers/xmlrpc/__init__.py
___________________________________________________________________
Name: svn:keyword
+ Id
Deleted: trunk/gnue-common/src/rpc/drivers/xmlrpc/pw_xmlrpc/ServerAdapter.py
===================================================================
--- trunk/gnue-common/src/rpc/drivers/xmlrpc/pw_xmlrpc/ServerAdapter.py
2005-09-02 20:23:33 UTC (rev 7887)
+++ trunk/gnue-common/src/rpc/drivers/xmlrpc/pw_xmlrpc/ServerAdapter.py
2005-09-06 17:37:17 UTC (rev 7888)
@@ -1,567 +0,0 @@
-#
-# This file is part of GNU Enterprise.
-#
-# GNU Enterprise is free software; you can redistribute it
-# and/or modify it under the terms of the GNU General Public
-# License as published by the Free Software Foundation; either
-# version 2, or (at your option) any later version.
-#
-# GNU Enterprise is distributed in the hope that it will be
-# useful, but WITHOUT ANY WARRANTY; without even the implied
-# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
-# PURPOSE. See the GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public
-# License along with program; see the file COPYING. If not,
-# write to the Free Software Foundation, Inc., 59 Temple Place
-# - Suite 330, Boston, MA 02111-1307, USA.
-#
-# Copyright 2001-2005 Free Software Foundation
-#
-# FILE:
-# xmlrpc/ServerAdapter.py
-#
-# DESCRIPTION:
-# Set of classes that implement the XML-RPC server driver for GNUe Comm.
-#
-# NOTES:
-# Requires xmlrpclib from http://www.pythonware.com/products/xmlrpc/
-# or Python 2.2+
-#
-# Server Parameters:
-#
-# port The port that the service is located on
-#
-
-from gnue.common.rpc import server
-from gnue.common.apps import errors
-from gnue.common.rpc.drivers._helpers import ObjectLibrarian, DirectoryServer
-
-from BaseHTTPServer import BaseHTTPRequestHandler;
-from SimpleHTTPServer import SimpleHTTPRequestHandler;
-import SocketServer
-
-import string, sys, os, posixpath, urllib, socket, types
-
-try:
- import xmlrpclib
-except ImportError:
- tmsg = _("\nUnable to load xmlrpclib. To use the XML-RPC interface, \n"
- "please install xmlrpc from:\n"
- " http://www.pythonware.com/products/xmlrpc/")
- raise server.AdapterInitializationError, tmsg
-
-import typeconv
-
-# Mapping from GRPC's datatype to XML-RPC datatypes
-_datatypeMap = {
- 'integer': 'int',
- 'string': 'string',
- 'boolean': 'boolean',
- 'date': 'dateTime.iso8601',
- 'number': 'double',
- 'base64': 'base64',
- 'binary': 'base64'
-}
-
-class MyThreadingHTTPServer(SocketServer.ThreadingTCPServer):
- def setParent(self,parent):
- self._parent=parent
-
- def server_bind(self):
- self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- self.socket.bind(self.server_address)
-
-class MyForkingHTTPServer(SocketServer.ForkingTCPServer):
- def setParent(self,parent):
- self._parent=parent
-
- def server_bind(self):
- self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- self.socket.bind(self.server_address)
-
-
-# TO BE CHECKED:
-# Python 2.1: SocketServer.ThreadingMixIn
-#SocketServer.ThreadingMixIn does not work properly
-#since it tries to close the socket of a request two
-#times.
-
-#Workaround for using SocketServer.ThreadingMixIn under
-#Python 2.1:
-
-#class MyThreadingHTTPServer(
-#SocketServer.ThreadingMixIn,
-#MyHTTPServer
-#):
-#def close_request(self, request):
-#pass
-
-# an medusa like server
-# should also be implemented: import asyncore, overwrite asyncore.dispatcher
-
-
-##############################################################################
-#
-# ServerAdapter
-#
-class ServerAdapter(DirectoryServer.DirectoryServer):
-
- def __init__(self, rpcdef, bindings, params):
- try:
- self._port = params['port']
- except KeyError:
- tmsg = _('Required parameter "port" not supplied')
- raise server.AdapterConfigurationError, tmsg
-
- if params.has_key('bindto'):
- self._bindto = params['bindto']
- else:
- self._bindto = '' # bind to all interfaces
-
- if params.has_key('allowed_hosts'):
- # TODO: Remove spaces, etc.
- self._allowed_hosts = string.split(params['allowed_hosts'],',')
- else:
- self._allowed_hosts = [''] # allow access from all hosts
-
- if params.has_key('loglevel'):
- self._loglevel = params['loglevel']
- else:
- self._loglevel = 0
-
- if params.has_key('httpbind'):
- self._httpbind = params['httpbind']
-
- self._httpserver=MyThreadingHTTPServer
-
- if params.has_key('servertype'):
- if params['servertype']=='threading':
- self._httpserver=MyThreadingHTTPServer
- elif params['servertype']=='forking':
- self._httpserver=MyForkingHTTPServer
-# elif params['servertype']=='asyncore':
-# self._httpserver=MyAsyncoreDispatcher
- else:
- raise server.AdapterConfigurationError, \
- u_("Value %(value)s for parameter 'servertype' is not supported.
"
- "Valid values are: %(valid)s") \
- % {'value': params ['servertype'],
- 'valid': 'threading, forking'}
-
- DirectoryServer.DirectoryServer.__init__(self, rpcdef, bindings, params)
-
- # To make "Object" definitions with w/non-object RPC,
- # the first parameters to any "Object" methods are
- # a "passed reference"
- # if parent._type == 'RpObject':
- # signature.append(_datatypeMap['string'])
-
- # To simulate "Object" definitions with w/this
- # non-object RPC, the first parameters to any
- # "Object" methods are a "passed reference"
-
- #
- # Return an exception
- #
- def raiseException(self, exception, message, event=None):
- xmlrpclib.dumps(xmlrpclib.Fault(34543, '%s: %s' % (exception, message)))
-
-
-
- # #
- # #
- ##########################################################################
- def serve(self):
- if hasattr(self,'_httpbind'):
- self._requestHandler = EnhancedRequestHandler
- else:
- self._requestHandler = RequestHandler
-
- self._tcpserver = self._httpserver((self._bindto,self._port),
- self._requestHandler)
- self._tcpserver.setParent(self)
- try:
- self._tcpserver.serve_forever()
- except KeyboardInterrupt:
- pass
-
- #
- # Call the requested method
- #
-
- # ---------------------------------------------------------------------------
- # Call the requested method
- # ---------------------------------------------------------------------------
-
- def call (self, method, params):
- if self._loglevel > 0:
- print _("Dispatching: "), method, params
-
-
- ## Check if the Method is part of a service or a pointer to a
- ## single object
- ##
- ## Call to an object: method="_Management:235423456_.getAttr"
- ## params=""
- ## Call to an method: (of a service=one object)
- ## method="DonutPlace.Management.Restart"
- if method [0] == '[':
- # call to an object
- # 1. get the object from the objectlibrarian
- # 2. check, if the object is supported by the gfd
-
- try:
- i = string.index (method, ']', 1)
- objhandle = method [1:i]
- method = method [i+2:]
-
- except ValueError:
- raise errors.ApplicationError, \
- u_("Wrong format of object handle in method call %s") % method
-
- # TODO check in service dir, if obj is supported or not
- o = ObjectLibrarian.retrieveObject (objhandle)
-
- try:
- server_method = getattr (o, method)
- server_attribute = None
-
- except AttributeError:
- server_method = None
-
- try:
- server_attribute = getattr (o, method [4:])
-
- except AttributeError:
- if method != "_close":
- raise errors.ApplicationError, \
- u_("Internal XMLRPC server error: method %s can be "
- "found in the directory (build out of a .grpc file), "
- "but the object doesn't contain this method/attribut. "
- "Please check you .grpc file for wrong return types.") \
- % method
-
- if method != "_close":
- direntry = self.getMethodDirEntry (o._type + "." + method)
- signature = direntry ['signature']
- else:
- signature = ('string',)
-
- else:
- # call to a service method or a helper call (get/set) for
- # a service attribut
- direntry = self.getMethodDirEntry (method)
- server_method = direntry ['binding']
- server_attribute = None
-
- # check if it is an real method (binding -> method)
- # or an get/set method for an attribut (binding-> attribut)
-
- if (type (server_method) != type (self.call)):
- server_attribute = server_method
- server_method = None
-
- signature = direntry ['signature']
-
- if (server_method is None) and (server_attribute is None):
- raise errors.SystemError, \
- u_("Server XML-RPC method '%s' is not bound to real method") \
- % method
-
- try:
- # TODO: Compare submitted attributs with signature
- pass
-
- except KeyError:
- raise errors.ApplicationError, \
- u_("Server XML-RPC procedure %(method)s accepts just %(attr)s "
- "as attributes") \
- % {'method': method,
- 'attr' : attr}
-
-
- # replace object handles in param with the real object
- counter = 0
- while counter < len (params):
- p = params [counter]
- if type (p) == type (""):
- if (len (p) == 42) and (p [0] == "[") and (p [41] == "]"):
- try:
- p = p [1:41]
- obj = ObjectLibrarian.retrieveObject (p)
- newp = params [0:counter-1] + (obj,) + params [counter+1:]
- params = newp
-
- except:
- pass
-
- counter += 1
-
- # check if it is an real method (binding -> method)
- # or an get/set method for an attribut (binding-> attribut)
-
- __params = typeconv.rpc_to_python (params, server.InvalidParameter)
-
- if (server_method is not None):
-
- # call method with params
- try:
- result = server_method (*__params)
- except:
- # Conserve traceback
- (group, name, message, detail) = errors.getException (1)
- raise errors.RemoteError (group, name, message, detail)
-
- result = typeconv.python_to_rpc (result, server.InvalidParameter)
-
- # check if it's the close object method
- elif (method == "_close"):
- result = self.releaseDynamicObject (o)
-
- else:
-
- ## check wether it's the set or the get method for the attribute
- mparts = string.splitfields (method, '.')
- mparts.reverse ()
- calltype = mparts [0]
- calltype = calltype [:4]
- gDebug (9, 'method %s has calling type %s' % (method, calltype))
-
- if calltype == 'set_':
- # setAttribut method
- server_attribute = params [0]
-
- elif calltype == 'get_':
- # getAttribut method
- result = server_attribute
- else:
- raise errors.SystemError, \
- u_("Internal Server XML-RPC error: method type (get/set "
- "attribute) couldn't be detected (method %s)") % method
-
-
- # replace real object in param with an object handle
- if type (result) == type (self): ## both should be instances
- ObjectLibrarian.archiveObject (result)
-
- # get the type of the result
- rtype = signature [0]
- # delete the surrounding brackets < >
- rtype = rtype [1:len(rtype)-1]
- # store typeinfo in new object
- result._type = rtype
-
- result = ObjectLibrarian.getObjectReference (result)
- self.registerDynamicObject (result, rtype)
-
- # check for empty results (not allowed for XMLRPC)
- if (result is None) or (result == [None]):
- gDebug (9, 'Transform result None into 1')
- result = 1
-
- return result
-
-
- def registerDynamicObject(self,objhandle,type):
- #
- # add new methods to the xmlrpc server
- #
- tl=len(type)
- for i in self.directory.keys():
- if i[0:tl]==type:
- method="["+objhandle+"]"+i[tl:]
- gDebug (9, 'Method %s registered to py-xmlrpc internal directory.' \
- % method)
- # self.server.addMethods({method:None})
-
- # add a method to close this object
- # self.server.addMethods({"["+objhandle+"]._close":None})
-
- def deregisterDynamicObject(self,object):
- #
- # remove the new methods from the xmlrpc server
- #
- objhandle=ObjectLibrarian.getObjectReference(object)
- type=object._type
- tl=len(type)
- for i in self.directory.keys():
- if i[0:tl]==type:
- method="["+objhandle+"]"+i[tl:]
- gDebug (9, 'Method %s is deleted from py-xmlrpc ' \
- % method +\
- ' internal directory.')
-# self.server.removeMethods({method:None})
-
- # remove the method to close this object
-# self.server.removeMethods({"["+objhandle+"]._close":None})
-
- def releaseDynamicObject(self,object):
- # delete bindings for this object in the xmlrpc method directory
- # self.deregisterDynamicObject(object)
- # release object for Object Librarian
- ObjectLibrarian.deferenceObject(object)
-
-
-
-
-##############################################################################
-#
-# Class to Handle the HTTP Post Request for XMLRPC only
-#
-
-class RequestHandler(BaseHTTPRequestHandler):
-
- def log_message (self, format, *args):
- gDebug (9, "%s - - [%s] %s" % \
- (self.address_string(), self.log_date_time_string(), format%args))
-
- # override basic handle to test allowed host condition
- #
- def handle(self):
- for i in self.server._parent._allowed_hosts:
- # get ip address of client and compare its first characters with i
- if self.client_address[0][:len(i)]==i:
-
- # continue with normal processing
- return BaseHTTPRequestHandler.handle(self)
-
- self.requestline="UNKNOWN"
- self.request_version="UNKNOWN"
- self.send_error(403, "Access to this server is forbidden!")
- return
-
-
- #
- # Handle the HTTP POST command
- #
- def do_POST(self):
- try:
- # Read the request
- data = self.rfile.read (int (self.headers ["content-length"]))
- params, method = xmlrpclib.loads (data)
-
- # Execute the function
- response = self.server._parent.call (method, params)
-
- # Generate response as a tuple
- if response is None:
- response = ()
- elif not isinstance (response, types.TupleType):
- response = (response,)
- response = xmlrpclib.dumps (response, methodresponse = 1)
- except:
- # Send back exceptions as Fault responses
- stack = string.join (errors.getException (), u'\x91')
- response = xmlrpclib.Fault (1, stack)
- response = xmlrpclib.dumps (response, methodresponse = 1)
-
- self.send_response (200)
- self.send_header ("Content-type", "text/xml")
- self.send_header ("Content-length", str (len (response)))
- self.end_headers ()
- self.wfile.write (response)
-
- # shut down the connection
- self.wfile.flush ()
- self.connection.shutdown (1)
-
-
-##############################################################################
-#
-# Enhanced HTTP Request Handler serving XMLRPC Post requests and allows to
-# access appserver data directory
-#
-
-class EnhancedRequestHandler(RequestHandler,SimpleHTTPRequestHandler):
-
- def send_head(self):
- # check for handler providing file content in string form
- try:
- # TODO: rewrite, add a hook instead of just filtering single pages
- content=self.server._parent._httpbind[self.path]()
- self.send_response(200)
- self.send_header("Content-type", "text/html")
- self.send_header("Content-Length", str(len(content)))
- self.end_headers()
- if self.command=="GET":
- self.wfile.write(content)
- self.wfile.flush()
- return None
- except:
- return SimpleHTTPRequestHandler.send_head(self)
-
-
- def translate_path(self, path):
- path = posixpath.normpath(urllib.unquote(path))
- words = path.split('/')
- words = filter(None, words)
- httpbind = self.server._parent._httpbind
-
- try:
- path=httpbind["/"]
- except:
- path=httpbind
-
- ## security check
- if (type(path)!=type("") or (len(path)<4)):
- return "/dev/null"
-
- for word in words:
- drive, word = os.path.splitdrive(word)
- head, word = os.path.split(word)
- if word in (os.curdir, os.pardir): continue
- path = os.path.join(path, word)
-
- return path
-
-
-###########################
-# The following part is not used at the moment
-##############################################################################
-#
-# Used internally by the XML-RPC server to proxy
-# between Object-by-Ref and actual objects.
-#
-class _ObjectByReferenceHandler:
-
- def __init__(self, oclass):
- self._class = oclass
-
- # If this makes absolutely no sense, move on and trust the code :)
- def requestHandler(self, method, *params, **args):
-
- if len(params):
-
- # If parameters were passed, the first one is always the handle
- refid = params[0]
-
- # The object's real method doesn't expect to receive the object handle
- params = params[1:]
-
- else:
-
- try:
-
- # If only named parameters are present, then
- # obj_handle should be the object handle...
- refid = args['obj_handle']
-
- # The object's real method doesn't expect to receive the object handle
- del args['obj_handle']
-
- except KeyError:
- tmsg = _('Object handle not returned')
- raise StandardError, tmsg # TODO
-
- try:
- return ObjectLibrarian. \
- retrieveObject(refid).__dict__[method](*params, **args)
- except KeyError:
-
- # Attempt to use an invalid objecthandle
- tmsg = _('Invalid object handle')
- raise StandardError, tmsg # TODO
-
-
-
Added: trunk/gnue-common/src/rpc/drivers/xmlrpc/typeconv.py
===================================================================
--- trunk/gnue-common/src/rpc/drivers/xmlrpc/typeconv.py 2005-09-02
20:23:33 UTC (rev 7887)
+++ trunk/gnue-common/src/rpc/drivers/xmlrpc/typeconv.py 2005-09-06
17:37:17 UTC (rev 7888)
@@ -0,0 +1,182 @@
+# GNU Enterprise Common Library - RPC interface - Un-/Marshalling
+#
+# Copyright 2001-2005 Free Software Foundation
+#
+# This file is part of GNU Enterprise.
+#
+# GNU Enterprise is free software; you can redistribute it
+# and/or modify it under the terms of the GNU General Public
+# License as published by the Free Software Foundation; either
+# version 2, or (at your option) any later version.
+#
+# GNU Enterprise is distributed in the hope that it will be
+# useful, but WITHOUT ANY WARRANTY; without even the implied
+# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+# PURPOSE. See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public
+# License along with program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# $Id$
+
+import xmlrpclib
+import datetime
+import mx.DateTime
+
+from gnue.common.utils import GDateTime
+
+# -----------------------------------------------------------------------------
+# Convert native Python type to xmlrpc's type
+# -----------------------------------------------------------------------------
+
+def python_to_rpc (value, exception):
+ """
+ Convert a value from native python type into a type acceptable to xmlrpc.
+
+ @param value: the native python value to be converted
+ @param exception: exception to be raised if the value couldn't get converted
+
+ The following type conversions are performed.
+
+ * None -> None
+ * str -> unicode
+ * unicode -> unicode
+ * bool -> xmlrpclib.boolean
+ * int/long/float -> int/long/float
+ * mx.DateTime/Delta -> xmlrpclib.DateTime
+ * datetime.datetime/.date/.time -> xmlrpclib.DateTime
+
+ For lists and tuples the conversion will be applied to each element. For
+ dictionaries the conversion will be applied to the key as well as the value.
+ If the value is none of the above datatypes the given exception will be
+ raised.
+ """
+
+ if value is None:
+ return value
+
+ # None or String
+ if isinstance (value, str):
+ # if xmlrpclib returns an 8-bit string, it is always in default encoding
+ return unicode (value)
+
+ elif isinstance (value, unicode):
+ return value
+
+ # Boolean needs to be checked *before* <int>
+ elif isinstance (value, bool):
+ return xmlrpclib.boolean (value)
+
+ # Number
+ elif isinstance (value, (int, long, float)):
+ return value
+
+ # Date/Time
+ elif isinstance (value, mx.DateTime.DateTimeType):
+ return xmlrpclib.DateTime ("%04d-%02d-%02d %02d:%02d:%09.6f" % (value.year,
+ value.month, value.day, value.hour, value.minute, value.second))
+
+ elif isinstance (value, mx.DateTime.DateTimeDeltaType):
+ return xmlrpclib.DateTime ("%02d:%02d:%09.6f" % (value.hour, value.minute,
+ value.second))
+
+ elif isinstance (value, (datetime.datetime, datetime.date, datetime.time)):
+ return xmlrpclib.DateTime (value.isoformat ())
+
+ # List
+ elif isinstance (value, list):
+ return [python_to_rpc (element, exception) for element in value]
+
+ # Tuple
+ elif isinstance (value, tuple):
+ return tuple ([python_to_rpc (element, exception) for element in value])
+
+ # Dictionary
+ elif isinstance (value, dict):
+ result = {}
+
+ for (key, val) in value.items ():
+ # Workaround for a bug xmlrpclib <= Python 2.3.4: No Unicode strings
+ # possible as dictionary keys.
+ if isinstance (key, unicode):
+ key = key.encode ('utf-8')
+
+ # Another deficiency in xmlrpclib: No <None> values as dictionary keys
+ elif key is None:
+ key = ''
+
+ result [python_to_rpc (key, exception)] = python_to_rpc (val, exception)
+
+ return result
+
+ else:
+ raise exception, repr (value)
+
+
+# -----------------------------------------------------------------------------
+# Convert xmlrpc's type to native Python type
+# -----------------------------------------------------------------------------
+
+def rpc_to_python (value, exception):
+ """
+ Convert a value from xmlrpc types into native python types.
+
+ @param value: xmlrpc value
+ @param exception: exception to be raised if no conversion is available
+
+ The following conversions are performed:
+
+ None -> None
+ str -> unicode or None (for empty strings)
+ unicode -> unicode or None (for empty strings)
+ xmlrpclib.boolean -> bool
+ int/long/float -> int/long/float
+ xmlrpclib.DateTime -> datetime.date, time or datetime (dep. on ISO-string)
+
+ For lists and tuples the conversion will be applied to each element. For
+ dictionaries the conversion will be applied to the key as well as the value.
+ """
+
+ if value is None:
+ return None
+
+ # String (converts to None for empty strings, which will be the case for
+ # None-values used in dictionary keys)
+ elif isinstance (value, str):
+ # if xmlrpclib returns an 8-bit string, it is always in default encoding.
+ return value and unicode (value) or None
+
+ elif isinstance (value, unicode):
+ return value and value or None
+
+ # Boolean (has to be checked before IntType)
+ elif isinstance (value, xmlrpclib.boolean):
+ return value and True or False
+
+ # Number
+ elif isinstance (value, (int,long,float)):
+ return value
+
+ # Date/Time/DateTime
+ elif isinstance (value, xmlrpclib.DateTime):
+ return GDateTime.parseISO (value.value)
+
+ # List
+ elif isinstance (value, list):
+ return [rpc_to_python (element, exception) for element in value]
+
+ # Tuple
+ elif isinstance (value, tuple):
+ return tuple ([rpc_to_python (element, exception) for element in value])
+
+ # Dictionary
+ elif isinstance (value, dict):
+ result = {}
+ for (key, val) in value.items ():
+ result [rpc_to_python (key, exception)] = rpc_to_python (val, exception)
+ return result
+
+ else:
+ raise exception, repr (value)
Property changes on: trunk/gnue-common/src/rpc/drivers/xmlrpc/typeconv.py
___________________________________________________________________
Name: svn:keyword
+ Id
Name: svn:keywords
+ Id
Modified: trunk/gnue-common/src/rpc/server.py
===================================================================
--- trunk/gnue-common/src/rpc/server.py 2005-09-02 20:23:33 UTC (rev 7887)
+++ trunk/gnue-common/src/rpc/server.py 2005-09-06 17:37:17 UTC (rev 7888)
@@ -1,8 +1,8 @@
-# GNU Enterprise RPC interface - Server adapter
+# GNU Enterprise Common - RPC - Server Interface
#
# Copyright 2001-2005 Free Software Foundation
#
-# This file is part of GNU Enterprise.
+# This file is part of GNU Enterprise
#
# GNU Enterprise is free software; you can redistribute it
# and/or modify it under the terms of the GNU General Public
@@ -21,96 +21,50 @@
#
# $Id$
-from types import *
-
-import string
-
from gnue.common.apps import errors, plugin
-from gnue.common.utils.FileUtils import openResource
+
# =============================================================================
# Public functions
# =============================================================================
# -----------------------------------------------------------------------------
-# Bind/export our services
+# Create ServerAdpaters for the given transports
# -----------------------------------------------------------------------------
-# Attributes:
-#
-# rpcdef The RPC definition loaded from an XML file. This can be
-# 1) a string that contains the location of the XML file
-# (can be a URL or pathname)
-# 2) a file-handle (or file-like handle; e.g. StringBuffer)
-# referencing an XML definition
-# 3) a GnuRpc object created from GComm.loadDefinition()
-#
-# drivers A dictionary that defines all the transports to be used. The
-# dictionary is of the form: {driver:params} where:
-# 1) driver: A string containing the name of the commdriver
-# (e.g., 'xmlrpc' or 'corba')
-# 2) params: A dictionary containing parameters specific to
-# the commdriver (e.g., {'port':'8888'} )
-#
-# bindings A dictionary containing binding definitions. The dictionary
-# is of the form: {server:handler} where:
-# 1) service: Name of the service being bound/exposed
-# 2) handler: Method that when called returns a class
-# instance i.e. {'DonutPlace',getDonutPlaceObj}
+def bind (transports, service):
+ """
+ Build server adapters for the given set of transports publishing the given
+ service instance.
-def bind (rpcdef, drivers, bindings):
+ @param transports: A dictionary of server adpaters to create. The dictionary
+ keys are the name of the drivers and the values are dictionaries with
+ parameters passed to the driver. Example: {'xmlrpc': {'loglevel':
False,..}}
+ @param service: python instance which should be served by the requested
+ adapters
- if isinstance (rpcdef, StringType):
+ @returns: a dictionary with the name of the service provider as key and the
+ ServerAdapter instance as value
+ """
- # Application supplied the location of their rpcdef.. load it
- fin = openResource (rpcdef)
- mapping = loadDefinition (fin, 1)
- fin.close ()
+ checktype (transports, dict)
- elif hasattr (rpcdef, 'read'):
+ result = {}
- # Application provided us a file-like object
- mapping = loadDefinition (rpcdef)
-
- else:
-
- # Otherwise, they must have specified a GnuRpc object
- mapping = rpcdef
-
-
- servers = {}
-
- for interface in drivers.keys ():
- params = drivers [interface]
-
+ for interface in transports:
+ params = transports [interface]
driver = plugin.find (interface, 'gnue.common.rpc.drivers',
'ServerAdapter')
- servers [interface] = driver.ServerAdapter (mapping, bindings, params)
- return servers
+ result [interface] = driver.ServerAdapter (service, params)
-# To keep everything that a client/server app will need in this
-# file, any convenience functions from the commdrivers directory.
-# (i.e., a user app should never need to access the commdrivers
-# directory.)
+ return result
-# -----------------------------------------------------------------------------
-# Load an XML-based interface definition
-# -----------------------------------------------------------------------------
-from gnue.common.rpc.parser.Parser import loadDefinition
-
# =============================================================================
# Exceptions
# =============================================================================
# -----------------------------------------------------------------------------
-# The requested adapter does not exist...
-# -----------------------------------------------------------------------------
-
-class InvalidAdapter (errors.AdminError):
- pass
-
-# -----------------------------------------------------------------------------
# The requested adapter could not initialize. Perhaps the
# supplied parameters do not point to a valid server, etc.
# -----------------------------------------------------------------------------
@@ -150,7 +104,6 @@
import sys
import time
- import StringIO
import mx.DateTime
class TestError (Exception):
@@ -189,45 +142,28 @@
def objtest (self):
return subobject ()
+ def roundtrip (self, value):
+ print "SERVER:", repr (value), type (value)
+ return value
+
def exceptiontest (self):
raise TestError, 'Message string'
def shutdown (self):
+ # Depending on wether the server is forking or threading a sys.exit (0)
+ # will affect either a child-process or a child-thread.
sys.exit (0)
- rpcdef = StringIO.StringIO ('<gnurpc>'
- '<service name="test" binding="test">'
- '<method name="stringtest"/>'
- '<method name="ustringtest"/>'
- '<method name="inttest"/>'
- '<method name="floattest"/>'
- '<method name="datetimetest"/>'
- '<method name="booltest"/>'
- '<method name="objtest"/>'
- '<method name="exceptiontest"/>'
- '<method name="shutdown"/>'
- '</service>'
-# '<service name="subobject"
binding="subobject">'
-# '<method name="test"/>'
-# '</service>'
- '</gnurpc>')
-
- if (len(sys.argv)==2) and \
- (sys.argv[1] in
['pyro','xmlrpc','socket','soap','xmlrpc.pw_xmlrpc','xmlrpc.py_xmlrpc']):
- drivers = {sys.argv[1]: {'port': 8765}}
+ if len (sys.argv) == 2 and \
+ sys.argv [1] in ['pyro', 'xmlrpc', 'socket', 'soap']:
+ drivers = {sys.argv [1]: {'port': 8765, 'servertype': 'forking'}}
else:
- print """
-GNUe RPC test server
+ print "GNUe RPC test server\n\nUsage: gcvs server.py <transport>"
+ sys.exit (1)
-Usage: gcvs server.py <transport>
+ print "Building adapter(s) for %s ..." % drivers
+ servers = bind (drivers, servertest ())
-"""
- raise SystemExit
-
- bindings = {'test': servertest}
-
- servers = bind (rpcdef, drivers, bindings)
-
print 'Starting server ...'
-
- (servers.values () [0]).serve ()
+ for server in servers.values ():
+ server.serve ()
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [gnue] r7888 - in trunk/gnue-common/src: datasources/drivers/other rpc rpc/drivers rpc/drivers/xmlrpc rpc/drivers/xmlrpc/pw_xmlrpc,
johannes <=