commit-gnue
[Top][All Lists]
Advanced

[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 ()





reply via email to

[Prev in Thread] Current Thread [Next in Thread]