blob: 52e38f8754d57a8268dcdd85d5d746355f884fc3 [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
Gabe Black1e1c41b2015-02-04 23:55:15 -080035from autotest_lib.client.common_lib.cros.graphite import autotest_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
Gabe Black1e1c41b2015-02-04 23:55:15 -0800106 autotest_stats.Counter('rpc').increment(methName)
Dan Shi9d33e7c2014-12-16 14:03:30 -0800107
108 metadata = request.copy()
109 metadata['_type'] = 'rpc'
Gabe Black1e1c41b2015-02-04 23:55:15 -0800110 timer = autotest_stats.Timer('rpc', metadata=metadata)
Alex Miller1b249312013-11-12 15:45:37 -0800111
showard02ed4bd2009-09-09 15:30:11 +0000112 try:
Jiaxi Luo0f5f0442014-05-23 11:36:54 -0700113 timer.start()
Dan Shicd0a01d2014-06-11 20:53:35 -0700114 meth = self.findServiceEndpoint(methName)
showard02ed4bd2009-09-09 15:30:11 +0000115 results['result'] = self.invokeServiceEndpoint(meth, args)
116 except Exception, err:
117 results['err_traceback'] = traceback.format_exc()
118 results['err'] = err
Jiaxi Luo0f5f0442014-05-23 11:36:54 -0700119 finally:
120 timer.stop(methName)
showard02ed4bd2009-09-09 15:30:11 +0000121
122 return results
123
showard3d6ae112009-05-02 00:45:48 +0000124
125 def _getRequestId(self, request):
126 try:
127 return request['id']
128 except KeyError:
129 raise BadServiceRequest(request)
130
showard02ed4bd2009-09-09 15:30:11 +0000131
showard3d6ae112009-05-02 00:45:48 +0000132 def handleRequest(self, jsonRequest):
showard3d6ae112009-05-02 00:45:48 +0000133 request = self.translateRequest(jsonRequest)
showard02ed4bd2009-09-09 15:30:11 +0000134 results = self.dispatchRequest(request)
135 return self.translateResult(results)
jadmanski0afbb632008-06-06 21:10:57 +0000136
mblighe8819cd2008-02-15 16:48:40 +0000137
showardef6fe022009-03-27 20:55:16 +0000138 @staticmethod
139 def translateRequest(data):
mblighe8819cd2008-02-15 16:48:40 +0000140 try:
141 req = json_decoder.decode(data)
142 except:
143 raise ServiceRequestNotTranslatable(data)
showard3d6ae112009-05-02 00:45:48 +0000144 req = customConvertJson(req)
mblighe8819cd2008-02-15 16:48:40 +0000145 return req
showard5da17d42008-04-28 18:07:58 +0000146
mblighe8819cd2008-02-15 16:48:40 +0000147 def findServiceEndpoint(self, name):
148 try:
149 meth = getattr(self.service, name)
mblighe8819cd2008-02-15 16:48:40 +0000150 return meth
mblighe8819cd2008-02-15 16:48:40 +0000151 except AttributeError:
152 raise ServiceMethodNotFound(name)
153
154 def invokeServiceEndpoint(self, meth, args):
155 return meth(*args)
156
showardef6fe022009-03-27 20:55:16 +0000157 @staticmethod
showard02ed4bd2009-09-09 15:30:11 +0000158 def translateResult(result_dict):
159 """
160 @param result_dict: a dictionary containing the result, error, traceback
161 and id.
162 @returns translated json result
163 """
164 if result_dict['err'] is not None:
165 error_name = result_dict['err'].__class__.__name__
166 result_dict['err'] = {'name': error_name,
167 'message': str(result_dict['err']),
168 'traceback': result_dict['err_traceback']}
169 result_dict['result'] = None
mblighe8819cd2008-02-15 16:48:40 +0000170
171 try:
showard02ed4bd2009-09-09 15:30:11 +0000172 json_dict = {'result': result_dict['result'],
173 'id': result_dict['id'],
174 'error': result_dict['err'] }
175 data = json_encoder.encode(json_dict)
mblighe8819cd2008-02-15 16:48:40 +0000176 except TypeError, e:
showard5da17d42008-04-28 18:07:58 +0000177 err_traceback = traceback.format_exc()
178 print err_traceback
179 err = {"name" : "JSONEncodeException",
180 "message" : "Result Object Not Serializable",
181 "traceback" : err_traceback}
showard02ed4bd2009-09-09 15:30:11 +0000182 data = json_encoder.encode({"result":None, "id":result_dict['id'],
183 "error":err})
showard5da17d42008-04-28 18:07:58 +0000184
mblighe8819cd2008-02-15 16:48:40 +0000185 return data