serviceHandler.py revision 59cfe5497d7f3be2bc4ade144d649a131e88448e
1 2""" 3 Copyright (c) 2007 Jan-Klaas Kollhof 4 5 This file is part of jsonrpc. 6 7 jsonrpc is free software; you can redistribute it and/or modify 8 it under the terms of the GNU Lesser General Public License as published by 9 the Free Software Foundation; either version 2.1 of the License, or 10 (at your option) any later version. 11 12 This software is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU Lesser General Public License for more details. 16 17 You should have received a copy of the GNU Lesser General Public License 18 along with this software; if not, write to the Free Software 19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20""" 21 22import traceback 23 24from json import decoder 25 26try: 27 from django.core import exceptions as django_exceptions 28 # Django JSON encoder uses the standard json encoder but can handle DateTime 29 from django.core.serializers import json as django_encoder 30 json_encoder = django_encoder.DjangoJSONEncoder() 31except django_exceptions.ImproperlyConfigured: 32 from json import encoder 33 json_encoder = encoder.JSONEncoder() 34 35from autotest_lib.client.common_lib.cros.graphite import stats 36 37 38json_decoder = decoder.JSONDecoder() 39 40 41def customConvertJson(value): 42 """\ 43 Recursively process JSON values and do type conversions. 44 -change floats to ints 45 -change unicodes to strs 46 """ 47 if isinstance(value, float): 48 return int(value) 49 elif isinstance(value, unicode): 50 return str(value) 51 elif isinstance(value, list): 52 return [customConvertJson(item) for item in value] 53 elif isinstance(value, dict): 54 new_dict = {} 55 for key, val in value.iteritems(): 56 new_key = customConvertJson(key) 57 new_val = customConvertJson(val) 58 new_dict[new_key] = new_val 59 return new_dict 60 else: 61 return value 62 63 64def ServiceMethod(fn): 65 fn.IsServiceMethod = True 66 return fn 67 68class ServiceException(Exception): 69 pass 70 71class ServiceRequestNotTranslatable(ServiceException): 72 pass 73 74class BadServiceRequest(ServiceException): 75 pass 76 77class ServiceMethodNotFound(ServiceException): 78 pass 79 80 81class ServiceHandler(object): 82 83 def __init__(self, service): 84 self.service=service 85 86 87 @classmethod 88 def blank_result_dict(cls): 89 return {'id': None, 'result': None, 'err': None, 'err_traceback': None} 90 91 def dispatchRequest(self, request): 92 """ 93 Invoke a json RPC call from a decoded json request. 94 @param request: a decoded json_request 95 @returns a dictionary with keys id, result, err and err_traceback 96 """ 97 results = self.blank_result_dict() 98 99 try: 100 results['id'] = self._getRequestId(request) 101 methName = request['method'] 102 args = request['params'] 103 except KeyError: 104 raise BadServiceRequest(request) 105 106 stats.Counter('rpc').increment(methName) 107 timer = stats.Timer('rpc') 108 109 try: 110 timer.start() 111 meth = self.findServiceEndpoint(methName) 112 results['result'] = self.invokeServiceEndpoint(meth, args) 113 except Exception, err: 114 results['err_traceback'] = traceback.format_exc() 115 results['err'] = err 116 finally: 117 timer.stop(methName) 118 119 return results 120 121 122 def _getRequestId(self, request): 123 try: 124 return request['id'] 125 except KeyError: 126 raise BadServiceRequest(request) 127 128 129 def handleRequest(self, jsonRequest): 130 request = self.translateRequest(jsonRequest) 131 results = self.dispatchRequest(request) 132 return self.translateResult(results) 133 134 135 @staticmethod 136 def translateRequest(data): 137 try: 138 req = json_decoder.decode(data) 139 except: 140 raise ServiceRequestNotTranslatable(data) 141 req = customConvertJson(req) 142 return req 143 144 def findServiceEndpoint(self, name): 145 try: 146 meth = getattr(self.service, name) 147 return meth 148 except AttributeError: 149 raise ServiceMethodNotFound(name) 150 151 def invokeServiceEndpoint(self, meth, args): 152 return meth(*args) 153 154 @staticmethod 155 def translateResult(result_dict): 156 """ 157 @param result_dict: a dictionary containing the result, error, traceback 158 and id. 159 @returns translated json result 160 """ 161 if result_dict['err'] is not None: 162 error_name = result_dict['err'].__class__.__name__ 163 result_dict['err'] = {'name': error_name, 164 'message': str(result_dict['err']), 165 'traceback': result_dict['err_traceback']} 166 result_dict['result'] = None 167 168 try: 169 json_dict = {'result': result_dict['result'], 170 'id': result_dict['id'], 171 'error': result_dict['err'] } 172 data = json_encoder.encode(json_dict) 173 except TypeError, e: 174 err_traceback = traceback.format_exc() 175 print err_traceback 176 err = {"name" : "JSONEncodeException", 177 "message" : "Result Object Not Serializable", 178 "traceback" : err_traceback} 179 data = json_encoder.encode({"result":None, "id":result_dict['id'], 180 "error":err}) 181 182 return data 183