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 21 import socket 22 import traceback 23 24 from json import decoder 25 26 try: 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() 31 except django_exceptions.ImproperlyConfigured: 32 from json import encoder 33 json_encoder = encoder.JSONEncoder() 34 35 36 json_decoder = decoder.JSONDecoder() 37 38 39 def customConvertJson(value): 40 """\ 41 Recursively process JSON values and do type conversions. 42 -change floats to ints 43 -change unicodes to strs 44 """ 45 if isinstance(value, float): 46 return int(value) 47 elif isinstance(value, unicode): 48 return str(value) 49 elif isinstance(value, list): 50 return [customConvertJson(item) for item in value] 51 elif isinstance(value, dict): 52 new_dict = {} 53 for key, val in value.iteritems(): 54 new_key = customConvertJson(key) 55 new_val = customConvertJson(val) 56 new_dict[new_key] = new_val 57 return new_dict 58 else: 59 return value 60 61 62 def ServiceMethod(fn): 63 fn.IsServiceMethod = True 64 return fn 65 66 class ServiceException(Exception): 67 pass 68 69 class ServiceRequestNotTranslatable(ServiceException): 70 pass 71 72 class BadServiceRequest(ServiceException): 73 pass 74 75 class ServiceMethodNotFound(ServiceException): 76 pass 77 78 79 class ServiceHandler(object): 80 81 def __init__(self, service): 82 self.service=service 83 84 85 @classmethod 86 def blank_result_dict(cls): 87 return {'id': None, 'result': None, 'err': None, 'err_traceback': None} 88 89 def dispatchRequest(self, request): 90 """ 91 Invoke a json RPC call from a decoded json request. 92 @param request: a decoded json_request 93 @returns a dictionary with keys id, result, err and err_traceback 94 """ 95 results = self.blank_result_dict() 96 97 try: 98 results['id'] = self._getRequestId(request) 99 methName = request['method'] 100 args = request['params'] 101 except KeyError: 102 raise BadServiceRequest(request) 103 104 metadata = request.copy() 105 metadata['_type'] = 'rpc' 106 metadata['rpc_server'] = socket.gethostname() 107 try: 108 meth = self.findServiceEndpoint(methName) 109 results['result'] = self.invokeServiceEndpoint(meth, args) 110 except Exception, err: 111 results['err_traceback'] = traceback.format_exc() 112 results['err'] = err 113 114 return results 115 116 117 def _getRequestId(self, request): 118 try: 119 return request['id'] 120 except KeyError: 121 raise BadServiceRequest(request) 122 123 124 def handleRequest(self, jsonRequest): 125 request = self.translateRequest(jsonRequest) 126 results = self.dispatchRequest(request) 127 return self.translateResult(results) 128 129 130 @staticmethod 131 def translateRequest(data): 132 try: 133 req = json_decoder.decode(data) 134 except: 135 raise ServiceRequestNotTranslatable(data) 136 req = customConvertJson(req) 137 return req 138 139 def findServiceEndpoint(self, name): 140 try: 141 meth = getattr(self.service, name) 142 return meth 143 except AttributeError: 144 raise ServiceMethodNotFound(name) 145 146 def invokeServiceEndpoint(self, meth, args): 147 return meth(*args) 148 149 @staticmethod 150 def translateResult(result_dict): 151 """ 152 @param result_dict: a dictionary containing the result, error, traceback 153 and id. 154 @returns translated json result 155 """ 156 if result_dict['err'] is not None: 157 error_name = result_dict['err'].__class__.__name__ 158 result_dict['err'] = {'name': error_name, 159 'message': str(result_dict['err']), 160 'traceback': result_dict['err_traceback']} 161 result_dict['result'] = None 162 163 try: 164 json_dict = {'result': result_dict['result'], 165 'id': result_dict['id'], 166 'error': result_dict['err'] } 167 data = json_encoder.encode(json_dict) 168 except TypeError, e: 169 err_traceback = traceback.format_exc() 170 print err_traceback 171 err = {"name" : "JSONEncodeException", 172 "message" : "Result Object Not Serializable", 173 "traceback" : err_traceback} 174 data = json_encoder.encode({"result":None, "id":result_dict['id'], 175 "error":err}) 176 177 return data 178