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