~ubuntu-branches/ubuntu/hardy/gnue-common/hardy

« back to all changes in this revision

Viewing changes to src/rpc/drivers/pyro/ServerAdapter.py

  • Committer: Bazaar Package Importer
  • Author(s): Andrew Mitchell
  • Date: 2005-03-09 11:06:31 UTC
  • Revision ID: james.westby@ubuntu.com-20050309110631-8gvvn39q7tjz1kj6
Tags: upstream-0.5.14
ImportĀ upstreamĀ versionĀ 0.5.14

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#
 
2
# This file is part of GNU Enterprise.
 
3
#
 
4
# GNU Enterprise is free software; you can redistribute it
 
5
# and/or modify it under the terms of the GNU General Public
 
6
# License as published by the Free Software Foundation; either
 
7
# version 2, or (at your option) any later version.
 
8
#
 
9
# GNU Enterprise is distributed in the hope that it will be
 
10
# useful, but WITHOUT ANY WARRANTY; without even the implied
 
11
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 
12
# PURPOSE. See the GNU General Public License for more details.
 
13
#
 
14
# You should have received a copy of the GNU General Public
 
15
# License along with program; see the file COPYING. If not,
 
16
# write to the Free Software Foundation, Inc., 59 Temple Place
 
17
# - Suite 330, Boston, MA 02111-1307, USA.
 
18
#
 
19
# Copyright 2001-2005 Free Software Foundation
 
20
#
 
21
# FILE:
 
22
# pyro/ServerAdapter.py
 
23
#
 
24
# DESCRIPTION:
 
25
# Set of classes that implement the Pyro server driver for GNUe Comm.
 
26
#
 
27
# NOTES:
 
28
# Requires pyro from pyro.sf.net
 
29
#
 
30
# Server Parameters:
 
31
#
 
32
#    port        The port that the service is located on
 
33
#
 
34
 
 
35
 
 
36
from gnue.common.apps import errors, GDebug
 
37
from gnue.common.rpc import server
 
38
from gnue.common.rpc.drivers import Base
 
39
from gnue.common.rpc.drivers._helpers import ObjectLibrarian, ObjectEnabler
 
40
 
 
41
import string, sys, os, posixpath, urllib, socket
 
42
 
 
43
try:
 
44
  import Pyro.naming
 
45
  import Pyro.core
 
46
  from Pyro.protocol import getHostname
 
47
  from Pyro.errors import PyroError,NamingError
 
48
 
 
49
except ImportError:
 
50
  tmsg = _("\nUnable to load Pyro.  To use the Pyro interface, \n"
 
51
           "please install pyro from:\n    http://pyro.sf.net/")
 
52
  raise GComm.AdapterInitializationError, tmsg
 
53
 
 
54
 
 
55
# Mapping from GRPC's datatype to XML-RPC datatypes
 
56
_datatypeMap = {
 
57
  'integer': 'int',
 
58
  'string': 'string',
 
59
  'boolean': 'boolean',
 
60
  'date': 'dateTime.iso8601',
 
61
  'number': 'double',
 
62
  'base64': 'base64',
 
63
  'binary': 'base64'
 
64
}
 
65
 
 
66
##############################################################################
 
67
#
 
68
# ServerAdapter
 
69
#
 
70
class ServerAdapter(Base.Server):
 
71
 
 
72
  def __init__(self, rpcdef, bindings, params):
 
73
    try:
 
74
      self._port = params['port']
 
75
    except KeyError:
 
76
      pass
 
77
 
 
78
    if params.has_key('bindto'):
 
79
      self._bindto = params['bindto']
 
80
    else:
 
81
      self._bindto = '' # bind to all interfaces
 
82
 
 
83
    if params.has_key('allowed_hosts'):
 
84
      # TODO: Remove spaces, etc.
 
85
      self._allowed_hosts = string.split(params['allowed_hosts'],',')
 
86
    else:
 
87
      self._allowed_hosts = [''] # allow access from all hosts
 
88
 
 
89
    if params.has_key('loglevel'):
 
90
      self._loglevel = params['loglevel']
 
91
    else:
 
92
      self._loglevel = 0
 
93
 
 
94
    self.pyro_group = ':GComm'
 
95
    # TODO: set pyro_group to other name, depending on grpc
 
96
 
 
97
    # initialize pyro server
 
98
    Pyro.core.initServer()
 
99
    Pyro.config.PYRO_NS_DEFAULTGROUP=self.pyro_group
 
100
    self.daemon = Pyro.core.Daemon()
 
101
 
 
102
    # locate the name server
 
103
    if not hasattr(self,'ns'):
 
104
      locator = Pyro.naming.NameServerLocator()
 
105
      GDebug.printMesg(9,'PYRO: searching for Naming Service...')
 
106
      self.ns = locator.getNS()
 
107
      GDebug.printMesg(9,'Naming Service found at %s (%s) %s.' % \
 
108
                       (self.ns.URI.address,
 
109
                        (Pyro.protocol.getHostname(self.ns.URI.address) or '??'),
 
110
                        self.ns.URI.port))
 
111
 
 
112
    # make sure our namespace group exists
 
113
    try:
 
114
      self.ns.createGroup(self.pyro_group)
 
115
    except NamingError:
 
116
      pass
 
117
 
 
118
    self.daemon.useNameServer(self.ns)
 
119
 
 
120
    self.mapObjects(rpcdef,bindings)
 
121
    
 
122
 
 
123
  #
 
124
  # Return an exception
 
125
  #
 
126
  def raiseException(self, exception, message, event=None):
 
127
    pass
 
128
 
 
129
 
 
130
  #                                                                        #
 
131
  #                                                                        #
 
132
  ##########################################################################
 
133
  def serve(self):
 
134
 
 
135
    # enter the service loop.
 
136
    GDebug.printMesg(9,'Starting pyro daemon loop.')
 
137
    
 
138
    try:
 
139
      # daemon.setTimeout(5)
 
140
      self.daemon.requestLoop()
 
141
    except KeyboardInterrupt:
 
142
      print 'shutting down gracefully.'
 
143
      # todo: disconnect all obj.
 
144
      #daemon.disconnect(obj)
 
145
      self.daemon.shutdown()
 
146
      print 'Exiting.'
 
147
 
 
148
 
 
149
  #
 
150
  # Create an internal "service directory"
 
151
  #
 
152
  def mapObjects(self, object, bindings, parent=None):
 
153
 
 
154
    # For servicable objects, maintain a complete "path" for reference
 
155
    if object._type in ('RpService','RpMethod','RpObject'):
 
156
      if parent and hasattr(parent,'_path'):
 
157
        object._path = "%s.%s" % (parent._path, object.name)
 
158
      else:
 
159
        object._path = object.name
 
160
 
 
161
 
 
162
    ##    
 
163
    ## Add binding informations to the objects
 
164
    ##
 
165
    ## services are static objects and
 
166
    ## objects  are dynamic ones
 
167
    ##
 
168
    if hasattr(object,'binding'):      
 
169
 
 
170
      # the direct mapping
 
171
      if bindings.has_key(object.binding):
 
172
        GDebug.printMesg(9,'GNURPC Binding Information:');
 
173
        GDebug.printMesg(9,' * %s is bound to \n * %s' % \
 
174
                         (object.binding,bindings[object.binding]))
 
175
 
 
176
        # for services create an "static" object 
 
177
        if object._type == 'RpService':
 
178
          object._realbinding=bindings[object.binding]()
 
179
          # TODO: Clearup this "static" object
 
180
 
 
181
        else:
 
182
          
 
183
          # in all other cases just save the binding information
 
184
          object._realbinding=bindings[object.binding]          
 
185
                
 
186
      else:
 
187
        # RpObject normaly don't need binding information, because
 
188
        # they are bound dynamicly
 
189
        if object._type != 'RpObject':
 
190
          
 
191
          print u_("Missing Binding information. Please add binding "
 
192
                   "information for %s") % object.binding
 
193
 
 
194
        
 
195
    # care for bindings in all Services
 
196
    if object._type == 'RpService':
 
197
      if hasattr(object,'_realbinding'):
 
198
        pass  # nothing to do
 
199
      else:
 
200
        if parent._type == 'RpService':
 
201
          try:
 
202
            object._realbinding=getattr(parent._realbinding,\
 
203
                                         object.name)
 
204
 
 
205
            GDebug.printMesg(9,'* %s is bound to \n * %s' % \
 
206
                             (object._path,object._realbinding))
 
207
          except:
 
208
            tmsg = u_("GNURPC cannot bind service '%(name)s' to service "
 
209
                      "'%(destination)s'") \
 
210
                   % {'name'       : object.name,
 
211
                      'destination': parent.name}
 
212
            raise AttributeError, tmsg
 
213
        elif parent._type == 'RpGnuRpc':
 
214
          pass
 
215
        else:
 
216
          tmsg = u_("GNURPC cannot bind service '%(name)s' to service "
 
217
                    "'%(destination)s'") \
 
218
                 % {'name'       : object.name,
 
219
                    'destination': parent._type}
 
220
          raise AttributeError, tmsg
 
221
 
 
222
      # create binding for service
 
223
      try:
 
224
        self.ns.deleteGroup(object._path)
 
225
      except:
 
226
        pass
 
227
      
 
228
      self.ns.createGroup(object._path)
 
229
      
 
230
      self._bindDelegateto(object._path+'.self',object._realbinding)
 
231
 
 
232
          
 
233
 
 
234
    # Compute binding for methods and for attributs
 
235
    # both are direct lins to the specific object
 
236
    # 
 
237
    # the dispatcher has to distinguish methods and
 
238
    # objects by testing if they are callable
 
239
    if (object._type == 'RpMethod') or \
 
240
       (object._type == 'RpAttribute'):
 
241
      
 
242
      # check for the binding
 
243
      if hasattr(object,'_realbinding'):
 
244
        bindto=object._realbinding
 
245
      else:
 
246
        if parent._type == 'RpService':
 
247
          try:
 
248
            bindto=getattr(parent._realbinding,object.name)
 
249
            GDebug.printMesg(9,'* %s is bound to \n * %s' % \
 
250
                             (object._path,bindto))
 
251
          except:
 
252
            tmsg = u_("GNURPC cannot bind method/attribut '%(name)s' to "
 
253
                      "service '%(service)s'") \
 
254
                   % {'name'   : object.name,
 
255
                      'service': parent.name}
 
256
            raise AttributeError, tmsg
 
257
            pass
 
258
        else:
 
259
          bindto=None
 
260
 
 
261
    if object._type == 'RpMethod':    
 
262
      self.addRpMethod(object,parent,bindto)
 
263
 
 
264
    #
 
265
    # Add all attribute methods to our directory..
 
266
    # XML-RPC doesn't support "Attributes", so an
 
267
    # attribute is exposed as a pair of get_<name>
 
268
    # and set_<name> methods.
 
269
    #
 
270
    if object._type == 'RpAttribute':
 
271
      self.addRpAttribut(object,parent,bindto)
 
272
 
 
273
 
 
274
    # Now, map our children
 
275
    for child in object._children:
 
276
      self.mapObjects(child, bindings, object)
 
277
 
 
278
  def addRpMethod(self,object,parent,binding):
 
279
    if binding!=None:
 
280
      self._bindDelegateto(object._path,binding)
 
281
    
 
282
  def _bindDelegateto(self,name,binding):
 
283
    # unregister method
 
284
    try:
 
285
      self.ns.unregister(name)
 
286
    except NamingError:
 
287
      pass
 
288
    except:
 
289
      pass
 
290
 
 
291
    # register using delegation
 
292
    proxy=Pyro.core.ObjBase()
 
293
    proxy.delegateTo(binding)
 
294
    #print 'bindto: %s (%s,%s)' % (name,binding, type(binding))
 
295
    self.daemon.connect(proxy,name)
 
296
 
 
297
  def addRpAttribut(self,object,parent,binding):
 
298
 
 
299
    return
 
300
    if not object.readonly:
 
301
      # Add the set_* directory entry
 
302
      self.daemon.connect({'%s.set_%s' % \
 
303
                              (parent._path, object.name):\
 
304
                              binding})
 
305
    if not object.writeonly:
 
306
      # Add the get_* directory entry
 
307
          self.server.addMethods({'%s.get_%s' % \
 
308
                              (parent._path, object.name):\
 
309
                              binding})
 
310
 
 
311
  #
 
312
  # Call the requested method
 
313
  #
 
314
  def call(self, method, params):
 
315
    if self._loglevel>0:
 
316
      print _("Dispatching: "), method, params
 
317
    
 
318
 
 
319
    ## Check if the Method is part of a service or a pointer to a
 
320
    ## single object
 
321
    ##
 
322
    ## Call to an object:  method="_Management:235423456_.getAttr"
 
323
    ##                     params=""
 
324
    ## Call to an method: (of a service=one object)
 
325
    ##                     method="DonutPlace.Management.Restart"
 
326
 
 
327
    if method[0]=='[':
 
328
      # call to an object
 
329
      # 1. get the object from the objectlibrarian
 
330
      # 2. check, if the object is supported by the gfd
 
331
      try:
 
332
        i=string.index(method,']',1)
 
333
        objhandle=method[1:i]
 
334
        method=method[i+2:]
 
335
      except ValueError:
 
336
        tmsg = u_("Wrong format of object handle in method call %s") % method
 
337
        raise AttributeError, tmsg
 
338
      # TODO check in service dir, if obj is supported or not
 
339
      o=ObjectLibrarian.retrieveObject(objhandle)
 
340
      try:
 
341
        server_method=getattr(o,method)
 
342
        server_attribute=None
 
343
      except AttributeError:
 
344
        server_method=None
 
345
        try:
 
346
          server_attribute=getattr(o,method[4:])
 
347
        except AttributeError:
 
348
          
 
349
          if method != "_close":
 
350
            msg = u_("Internal XMLRPC server error: method %s can be "
 
351
                     "found in the directory (build out of a .grpc file), "
 
352
                     "but the object doesn't contain this method/attribut. "
 
353
                     "Please check you .grpc file for wrong return types.") \
 
354
                  % method
 
355
            
 
356
            raise AttributeError, msg
 
357
        
 
358
      if method!="_close":
 
359
        direntry = self.getMethodDirEntry(o._type+"."+method)
 
360
        signature=direntry['signature']
 
361
      else:
 
362
        signature= ('string',)                
 
363
 
 
364
    else:
 
365
 
 
366
      # call to a service method or a helper call (get/set) for
 
367
      # a service attribut
 
368
      try:
 
369
        direntry = self.getMethodDirEntry(method)
 
370
        server_method = direntry['binding']
 
371
        server_attribute = None
 
372
        
 
373
        # check if it is an real method (binding -> method)
 
374
        # or an get/set method for an attribut (binding-> attribut)
 
375
        if (type(server_method)!=type(self.call)):
 
376
          server_attribute = server_method
 
377
          server_method=None
 
378
          
 
379
        signature=direntry['signature']
 
380
 
 
381
        if (server_method==None) and (server_attribute==None):
 
382
          tmsg = u_("Server XML-RPC method '%s' is not bound to real method") \
 
383
                 % method
 
384
          raise AttributeError, tmsg
 
385
      except KeyError:
 
386
        tmsg = u_("Server does not have XML-RPC procedure %s") % method
 
387
        raise AttributeError, tmsg
 
388
    try:
 
389
      #
 
390
      pass
 
391
        # TODO:  Compare submitted attributs with signature
 
392
    except KeyError:
 
393
      tmsg = u_("Server XML-RPC procedure %(method)s accepts just %(attr)s "
 
394
                "as attributs") \
 
395
             % {'method': method,
 
396
                'attr'  : attr}
 
397
      raise AttributeError, tmsg
 
398
    
 
399
 
 
400
    # replace object handles in param with the real object
 
401
    counter=0
 
402
    while counter<len(params):
 
403
      p=params[counter]
 
404
      if type(p)==type(""):
 
405
        if (len(p)==42) and (p[0]=="[") and (p[41]=="]"):
 
406
          try:
 
407
            p=p[1:41]
 
408
            obj=ObjectLibrarian.retrieveObject(p)
 
409
            newp=params[0:counter-1]+(obj,)+params[counter+1:]
 
410
            params=newp
 
411
          except:
 
412
            pass
 
413
      counter=counter+1;
 
414
 
 
415
    # check if it is an real method (binding -> method)
 
416
    # or an get/set method for an attribut (binding-> attribut)
 
417
 
 
418
    if (server_method!=None):
 
419
            
 
420
      # call method with params
 
421
      result=server_method(*params)
 
422
 
 
423
    # check if it's the close object method
 
424
    elif (method=="_close"):
 
425
      
 
426
      result=self.releaseDynamicObject(o)
 
427
      
 
428
    else:
 
429
      
 
430
      ## check wether its the set or the get method for the attribut
 
431
      mparts=string.splitfields(method,'.')
 
432
      mparts.reverse()
 
433
      calltype=mparts[0]
 
434
      calltype=calltype[:4]
 
435
      GDebug.printMesg(9,'method %s has calling type %s' %\
 
436
                       (method,calltype))
 
437
      if calltype=='set_':
 
438
        # setAttribut method
 
439
        server_attribute=params[0]
 
440
      elif calltype=='get_':
 
441
        # getAttribut method
 
442
        result=server_attribute
 
443
      else:
 
444
        tmsg = u_("Internal Server XML-RPC error: method type (get/set "
 
445
                  "attribute) couldn't be detected (method %s)") % method
 
446
        raise AttributeError, tmsg
 
447
    
 
448
 
 
449
    # replace real object in param with an object handle
 
450
    if type(result)==type(self):  ## both should be instances
 
451
       ObjectLibrarian.archiveObject(result)
 
452
 
 
453
       # get the type of the result
 
454
       rtype=signature[0]
 
455
       # delete the surrounding brackets < >
 
456
       rtype=rtype[1:len(rtype)-1]
 
457
       # store typeinfo in new object
 
458
       result._type=rtype
 
459
 
 
460
       result=ObjectLibrarian.getObjectReference(result)
 
461
       self.registerDynamicObject(result,rtype)
 
462
 
 
463
    # check for empty results (not allowed for XMLRPC)
 
464
    if (result==None) or (result==[None]):
 
465
      GDebug.printMesg(9,'Transform result None into 1')
 
466
      result=1
 
467
      
 
468
    return result
 
469
 
 
470
class RpcServiceProxy (Pyro.core.ObjBase):
 
471
  pass