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