Package Products :: Package ZenUtils :: Package extdirect :: Module router |
[hide private]
[frames] | no frames]

# Source Code for Module Products.ZenUtils.extdirect.router

      1  ###########################################################################
      2  #
      3  # This program is part of Zenoss Core, an open source monitoring platform.
      4  # Copyright (C) 2009, Zenoss Inc.
      5  #
      6  # This program is free software; you can redistribute it and/or modify it
      7  # under the terms of the GNU General Public License version 2 as published by
      8  # the Free Software Foundation.
      9  #
     10  # For complete information please visit: http://www.zenoss.com/oss/
     11  #
     12  ###########################################################################
     13  import inspect
     14  import logging
     15  log = logging.getLogger('extdirect')
     16
     17 -class DirectException(Exception):
     18      pass
     19
     20  from Products.ZenUtils.jsonutils import json, unjson
     21
     22
     23 -class DirectResponse(object):
     24      """
     25      Encapsulation of the simple protocol used to send results and messages to
     26      the front end.
     27      """
     28      _data = None
     29 -    def __init__(self, msg=None, success=True, **kwargs):
     30          self._data = {}
     31          self._data.update(kwargs)
     32          self._data['success'] = success
     33          if msg:
     34              self._data['msg'] = msg
     35
     36      @property
     37 -    def data(self):
     38          return self._data
     39
     40 -    def __setitem__(self, key, value):
     41          self._data[key] = value
     42
     43      @staticmethod
     44 -    def fail(msg=None, **kwargs):
     45          return DirectResponse(msg, success=False, **kwargs)
     46
     47      @staticmethod
     48 -    def succeed(msg=None, **kwargs):
     49          return DirectResponse(msg, success=True, **kwargs)
     50
     51
     52 -class DirectRouter(object):
     53      """
     54      Basic Ext.Direct router class.
     55
     56      Ext.Direct allows one to create an API that communicates with a single URL,
     57      which then routes requests to the appropriate method. The client-side API
     58      object matches the server-side API object.
     59
     60      This base class parses an Ext.Direct request, which contains the name of
     61      the method and any data that should be passed, and routes the data to the
     62      approriate method. It then receives the output of that call and puts it
     63      into the data structure expected by Ext.Direct.
     64
     65      Call an instance of this class with the JSON from an Ext.Direct request.
     66      """
     67 -    def __call__(self, body):
     68          """
     69          """
     70          # Decode the request data
     71          body = unjson(body)
     72          self._body = body
     73
     74          if isinstance(body, list):
     75              directRequests = body
     76          elif isinstance(body, dict):
     77              directRequests = [body]
     78          else:
     79              raise DirectException("Body is not a supported type: %s" % body)
     80
     81          directResponses = []
     82          for directRequest in directRequests:
     83              directResponses.append(self._processDirectRequest(directRequest))
     84
     85          if len(directResponses) == 1:
     86              directResponses = directResponses[0]
     87
     88          return json(directResponses)
     89
     90 -    def _processDirectRequest(self, directRequest):
     91
     92          # Double-check that this request is meant for this class
     93          action = directRequest.get('action')
     94          clsname = self.__class__.__name__
     95          if action != clsname:
     96              raise DirectException(("Action specified in request ('%s') is"
     97                                    " not named %s.") % (action, clsname))
     98
     99          # Pull out the method name and make sure it exists on this class
    100          method = directRequest.get('method')
    101          if not method:
    102              raise DirectException("No method specified. Is this a valid"
    103                                    " Ext.Direct request?")
    104          try:
    105              _targetfn = getattr(self, method)
    106          except AttributeError:
    107              raise DirectException("'%s' is not the name of a method on %s" % (
    108                  method, clsname
    109              ))
    110
    111          # Pull out any arguments. Sent as an array containing a hash map, so
    112          # get the first member.
    113          data = directRequest.get('data')
    114          if not data:
    115              data = {}
    116          else:
    117              data = data[0]
    118
    119          if isinstance(data, (int, basestring)):
    120              data = {'id': data}
    121
    122          # Cast all keys as strings, in case of encoding or other wrinkles
    123          data = dict((str(k), v) for k,v in data.iteritems())
    124          self._data = data
    125
    126          # Finally, call the target method, passing in the data
    127          try:
    128              result = _targetfn(**data)
    129          except Exception, e:
    130              log.exception(e)
    131              message = e.__class__.__name__ + ' ' + str(e)
    132              return {
    133                  'type':'exception',
    134                  'message':message
    135              }
    136
    137          if isinstance(result, DirectResponse):
    138              result = result.data
    139
    140          return {
    141              'type':'rpc',
    142              'tid': directRequest['tid'],
    143              'action': action,
    144              'method': method,
    145              'result': result
    146          }
    147
    148
    149 -class DirectProviderDefinition(object):
    150      """
    151      Turns a L{DirectRouter} subclass into JavaScript object representing the
    152      config of the client-side API.
    153
    154      Inspects the given subclass and retrieves the names of all public methods,
    155      then defines those as actions on the Ext.Direct provider, and creates the
    156      JS that adds the provider.
    157
    158      See http://extjs.com/products/extjs/direct.php for a full explanation of
    159      protocols and features of Ext.Direct.
    160      """
    161 -    def __init__(self, routercls, url, timeout, ns=None):
    162          """
    163          @param routercls: A L{DirectRouter} subclass
    164          @type routercls: class
    165          @param url: The url at which C{routercls} is available
    166          @type url: str
    167          @param ns: The client-side namespace in which the provider should live.
    168                     The provider will be available at [ns].[routercls.__name__].
    169                     For example, if ns is 'Zenoss.remote' and routercls is named
    170                     'EventConsole', client-side code would call
    171                     C{Zenoss.remote.EventConsole.my_method(params, callback)}.
    172          """
    173          self.routercls = routercls
    174          self.url = url
    175          self.ns = ns
    176          self.timeout = timeout
    177
    178 -    def _config(self):
    179          actions = []
    180          for name, value in inspect.getmembers(self.routercls):
    181              if name.startswith("_"):
    182                  continue
    183              if inspect.ismethod(value):
    184
    185                  ## Update this when extdirect doesn't freak out when you specify
    186                  ## actual lens (we're passing them all in as a single dict, so
    187                  ## from the perspective of Ext.Direct they are all len 1)
    188                  #args = inspect.getargspec(value)[0]
    189                  #args.remove('self')
    190                  #arglen = len(args)
    191                  arglen = 1
    192
    193                  actions.append({'name':name, 'len':arglen})
    194          config = {
    195              'id': self.routercls.__name__,
    196              'type': 'remoting',
    197              'url': self.url,
    198              'timeout': self.timeout,
    199              'enableBuffer': 100,
    200              'actions': {
    201                  self.routercls.__name__: actions
    202              }
    203          }
    204          if self.ns:
    205              config['namespace'] = self.ns
    206          return config
    207
    208 -    def render(self):
    209          """
    210          Generate and return an Ext.Direct provider definition, wrapped in a
    211          <script> tag and ready for inclusion in an HTML document.
    212          """
    213          config = self._config()
    214          source = "\nExt.Direct.addProvider(%s);\n" % json(config)
    215          return source.strip()
    216