blob: bd63cd321c924ed10da8f274c674beb8dad65da4 [file] [log] [blame]
mblighe8819cd2008-02-15 16:48:40 +00001"""
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
Jakob Juelich59cfe542014-09-02 16:37:46 -070023from 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
Gabe Black1e1c41b2015-02-04 23:55:15 -080034from autotest_lib.client.common_lib.cros.graphite import autotest_stats
mblighe8819cd2008-02-15 16:48:40 +000035
Jakob Juelich59cfe542014-09-02 16:37:46 -070036
37json_decoder = decoder.JSONDecoder()
38
39
mblighe8819cd2008-02-15 16:48:40 +000040def 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
mblighe8819cd2008-02-15 16:48:40 +000062
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
showard3d6ae112009-05-02 00:45:48 +000079
mblighe8819cd2008-02-15 16:48:40 +000080class ServiceHandler(object):
81
82 def __init__(self, service):
83 self.service=service
jadmanski0afbb632008-06-06 21:10:57 +000084
showard2df3c692009-09-11 18:41:07 +000085
86 @classmethod
87 def blank_result_dict(cls):
88 return {'id': None, 'result': None, 'err': None, 'err_traceback': None}
89
showard3d6ae112009-05-02 00:45:48 +000090 def dispatchRequest(self, request):
showard02ed4bd2009-09-09 15:30:11 +000091 """
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 """
showard2df3c692009-09-11 18:41:07 +000096 results = self.blank_result_dict()
showard02ed4bd2009-09-09 15:30:11 +000097
showard3d6ae112009-05-02 00:45:48 +000098 try:
showard02ed4bd2009-09-09 15:30:11 +000099 results['id'] = self._getRequestId(request)
showard3d6ae112009-05-02 00:45:48 +0000100 methName = request['method']
101 args = request['params']
102 except KeyError:
103 raise BadServiceRequest(request)
mblighe8819cd2008-02-15 16:48:40 +0000104
Gabe Black1e1c41b2015-02-04 23:55:15 -0800105 autotest_stats.Counter('rpc').increment(methName)
Dan Shi9d33e7c2014-12-16 14:03:30 -0800106
107 metadata = request.copy()
108 metadata['_type'] = 'rpc'
Gabe Black1e1c41b2015-02-04 23:55:15 -0800109 timer = autotest_stats.Timer('rpc', metadata=metadata)
Alex Miller1b249312013-11-12 15:45:37 -0800110
showard02ed4bd2009-09-09 15:30:11 +0000111 try:
Jiaxi Luo0f5f0442014-05-23 11:36:54 -0700112 timer.start()
Dan Shicd0a01d2014-06-11 20:53:35 -0700113 meth = self.findServiceEndpoint(methName)
showard02ed4bd2009-09-09 15:30:11 +0000114 results['result'] = self.invokeServiceEndpoint(meth, args)
115 except Exception, err:
116 results['err_traceback'] = traceback.format_exc()
117 results['err'] = err
Jiaxi Luo0f5f0442014-05-23 11:36:54 -0700118 finally:
119 timer.stop(methName)
showard02ed4bd2009-09-09 15:30:11 +0000120
121 return results
122
showard3d6ae112009-05-02 00:45:48 +0000123
124 def _getRequestId(self, request):
125 try:
126 return request['id']
127 except KeyError:
128 raise BadServiceRequest(request)
129
showard02ed4bd2009-09-09 15:30:11 +0000130
showard3d6ae112009-05-02 00:45:48 +0000131 def handleRequest(self, jsonRequest):
showard3d6ae112009-05-02 00:45:48 +0000132 request = self.translateRequest(jsonRequest)
showard02ed4bd2009-09-09 15:30:11 +0000133 results = self.dispatchRequest(request)
134 return self.translateResult(results)
jadmanski0afbb632008-06-06 21:10:57 +0000135
mblighe8819cd2008-02-15 16:48:40 +0000136
showardef6fe022009-03-27 20:55:16 +0000137 @staticmethod
138 def translateRequest(data):
mblighe8819cd2008-02-15 16:48:40 +0000139 try:
140 req = json_decoder.decode(data)
141 except:
142 raise ServiceRequestNotTranslatable(data)
showard3d6ae112009-05-02 00:45:48 +0000143 req = customConvertJson(req)
mblighe8819cd2008-02-15 16:48:40 +0000144 return req
showard5da17d42008-04-28 18:07:58 +0000145
mblighe8819cd2008-02-15 16:48:40 +0000146 def findServiceEndpoint(self, name):
147 try:
148 meth = getattr(self.service, name)
mblighe8819cd2008-02-15 16:48:40 +0000149 return meth
mblighe8819cd2008-02-15 16:48:40 +0000150 except AttributeError:
151 raise ServiceMethodNotFound(name)
152
153 def invokeServiceEndpoint(self, meth, args):
154 return meth(*args)
155
showardef6fe022009-03-27 20:55:16 +0000156 @staticmethod
showard02ed4bd2009-09-09 15:30:11 +0000157 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
mblighe8819cd2008-02-15 16:48:40 +0000169
170 try:
showard02ed4bd2009-09-09 15:30:11 +0000171 json_dict = {'result': result_dict['result'],
172 'id': result_dict['id'],
173 'error': result_dict['err'] }
174 data = json_encoder.encode(json_dict)
mblighe8819cd2008-02-15 16:48:40 +0000175 except TypeError, e:
showard5da17d42008-04-28 18:07:58 +0000176 err_traceback = traceback.format_exc()
177 print err_traceback
178 err = {"name" : "JSONEncodeException",
179 "message" : "Result Object Not Serializable",
180 "traceback" : err_traceback}
showard02ed4bd2009-09-09 15:30:11 +0000181 data = json_encoder.encode({"result":None, "id":result_dict['id'],
182 "error":err})
showard5da17d42008-04-28 18:07:58 +0000183
mblighe8819cd2008-02-15 16:48:40 +0000184 return data