blob: a66bb0ddaf2b3d230450dacf30564870f6627445 [file] [log] [blame]
mblighe8819cd2008-02-15 16:48:40 +00001
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
Jakob Juelich59cfe542014-09-02 16:37:46 -070024from 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
Michael Liangda8c60a2014-06-03 13:24:51 -070035from autotest_lib.client.common_lib.cros.graphite import stats
mblighe8819cd2008-02-15 16:48:40 +000036
Jakob Juelich59cfe542014-09-02 16:37:46 -070037
38json_decoder = decoder.JSONDecoder()
39
40
mblighe8819cd2008-02-15 16:48:40 +000041def 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
mblighe8819cd2008-02-15 16:48:40 +000063
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
showard3d6ae112009-05-02 00:45:48 +000080
mblighe8819cd2008-02-15 16:48:40 +000081class ServiceHandler(object):
82
83 def __init__(self, service):
84 self.service=service
jadmanski0afbb632008-06-06 21:10:57 +000085
showard2df3c692009-09-11 18:41:07 +000086
87 @classmethod
88 def blank_result_dict(cls):
89 return {'id': None, 'result': None, 'err': None, 'err_traceback': None}
90
showard3d6ae112009-05-02 00:45:48 +000091 def dispatchRequest(self, request):
showard02ed4bd2009-09-09 15:30:11 +000092 """
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 """
showard2df3c692009-09-11 18:41:07 +000097 results = self.blank_result_dict()
showard02ed4bd2009-09-09 15:30:11 +000098
showard3d6ae112009-05-02 00:45:48 +000099 try:
showard02ed4bd2009-09-09 15:30:11 +0000100 results['id'] = self._getRequestId(request)
showard3d6ae112009-05-02 00:45:48 +0000101 methName = request['method']
102 args = request['params']
103 except KeyError:
104 raise BadServiceRequest(request)
mblighe8819cd2008-02-15 16:48:40 +0000105
Alex Miller1b249312013-11-12 15:45:37 -0800106 stats.Counter('rpc').increment(methName)
Jiaxi Luo0f5f0442014-05-23 11:36:54 -0700107 timer = stats.Timer('rpc')
Alex Miller1b249312013-11-12 15:45:37 -0800108
showard02ed4bd2009-09-09 15:30:11 +0000109 try:
Jiaxi Luo0f5f0442014-05-23 11:36:54 -0700110 timer.start()
Dan Shicd0a01d2014-06-11 20:53:35 -0700111 meth = self.findServiceEndpoint(methName)
showard02ed4bd2009-09-09 15:30:11 +0000112 results['result'] = self.invokeServiceEndpoint(meth, args)
113 except Exception, err:
114 results['err_traceback'] = traceback.format_exc()
115 results['err'] = err
Jiaxi Luo0f5f0442014-05-23 11:36:54 -0700116 finally:
117 timer.stop(methName)
showard02ed4bd2009-09-09 15:30:11 +0000118
119 return results
120
showard3d6ae112009-05-02 00:45:48 +0000121
122 def _getRequestId(self, request):
123 try:
124 return request['id']
125 except KeyError:
126 raise BadServiceRequest(request)
127
showard02ed4bd2009-09-09 15:30:11 +0000128
showard3d6ae112009-05-02 00:45:48 +0000129 def handleRequest(self, jsonRequest):
showard3d6ae112009-05-02 00:45:48 +0000130 request = self.translateRequest(jsonRequest)
showard02ed4bd2009-09-09 15:30:11 +0000131 results = self.dispatchRequest(request)
132 return self.translateResult(results)
jadmanski0afbb632008-06-06 21:10:57 +0000133
mblighe8819cd2008-02-15 16:48:40 +0000134
showardef6fe022009-03-27 20:55:16 +0000135 @staticmethod
136 def translateRequest(data):
mblighe8819cd2008-02-15 16:48:40 +0000137 try:
138 req = json_decoder.decode(data)
139 except:
140 raise ServiceRequestNotTranslatable(data)
showard3d6ae112009-05-02 00:45:48 +0000141 req = customConvertJson(req)
mblighe8819cd2008-02-15 16:48:40 +0000142 return req
showard5da17d42008-04-28 18:07:58 +0000143
mblighe8819cd2008-02-15 16:48:40 +0000144 def findServiceEndpoint(self, name):
145 try:
146 meth = getattr(self.service, name)
mblighe8819cd2008-02-15 16:48:40 +0000147 return meth
mblighe8819cd2008-02-15 16:48:40 +0000148 except AttributeError:
149 raise ServiceMethodNotFound(name)
150
151 def invokeServiceEndpoint(self, meth, args):
152 return meth(*args)
153
showardef6fe022009-03-27 20:55:16 +0000154 @staticmethod
showard02ed4bd2009-09-09 15:30:11 +0000155 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
mblighe8819cd2008-02-15 16:48:40 +0000167
168 try:
showard02ed4bd2009-09-09 15:30:11 +0000169 json_dict = {'result': result_dict['result'],
170 'id': result_dict['id'],
171 'error': result_dict['err'] }
172 data = json_encoder.encode(json_dict)
mblighe8819cd2008-02-15 16:48:40 +0000173 except TypeError, e:
showard5da17d42008-04-28 18:07:58 +0000174 err_traceback = traceback.format_exc()
175 print err_traceback
176 err = {"name" : "JSONEncodeException",
177 "message" : "Result Object Not Serializable",
178 "traceback" : err_traceback}
showard02ed4bd2009-09-09 15:30:11 +0000179 data = json_encoder.encode({"result":None, "id":result_dict['id'],
180 "error":err})
showard5da17d42008-04-28 18:07:58 +0000181
mblighe8819cd2008-02-15 16:48:40 +0000182 return data