blob: 2481d57d6eddde58e43302fffee8677a0fe64d65 [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 Juelich1e10d742014-09-02 16:37:46 -070024from json import decoder
25
26try:
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 ImportError:
31 from json import encoder
32 json_encoder = encoder.JSONEncoder()
33
Michael Liangda8c60a2014-06-03 13:24:51 -070034from autotest_lib.client.common_lib.cros.graphite import stats
mblighe8819cd2008-02-15 16:48:40 +000035
Jakob Juelich1e10d742014-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
Alex Miller1b249312013-11-12 15:45:37 -0800105 stats.Counter('rpc').increment(methName)
Jiaxi Luo0f5f0442014-05-23 11:36:54 -0700106 timer = stats.Timer('rpc')
Alex Miller1b249312013-11-12 15:45:37 -0800107
showard02ed4bd2009-09-09 15:30:11 +0000108 try:
Jiaxi Luo0f5f0442014-05-23 11:36:54 -0700109 timer.start()
Dan Shicd0a01d2014-06-11 20:53:35 -0700110 meth = self.findServiceEndpoint(methName)
showard02ed4bd2009-09-09 15:30:11 +0000111 results['result'] = self.invokeServiceEndpoint(meth, args)
112 except Exception, err:
113 results['err_traceback'] = traceback.format_exc()
114 results['err'] = err
Jiaxi Luo0f5f0442014-05-23 11:36:54 -0700115 finally:
116 timer.stop(methName)
showard02ed4bd2009-09-09 15:30:11 +0000117
118 return results
119
showard3d6ae112009-05-02 00:45:48 +0000120
121 def _getRequestId(self, request):
122 try:
123 return request['id']
124 except KeyError:
125 raise BadServiceRequest(request)
126
showard02ed4bd2009-09-09 15:30:11 +0000127
showard3d6ae112009-05-02 00:45:48 +0000128 def handleRequest(self, jsonRequest):
showard3d6ae112009-05-02 00:45:48 +0000129 request = self.translateRequest(jsonRequest)
showard02ed4bd2009-09-09 15:30:11 +0000130 results = self.dispatchRequest(request)
131 return self.translateResult(results)
jadmanski0afbb632008-06-06 21:10:57 +0000132
mblighe8819cd2008-02-15 16:48:40 +0000133
showardef6fe022009-03-27 20:55:16 +0000134 @staticmethod
135 def translateRequest(data):
mblighe8819cd2008-02-15 16:48:40 +0000136 try:
137 req = json_decoder.decode(data)
138 except:
139 raise ServiceRequestNotTranslatable(data)
showard3d6ae112009-05-02 00:45:48 +0000140 req = customConvertJson(req)
mblighe8819cd2008-02-15 16:48:40 +0000141 return req
showard5da17d42008-04-28 18:07:58 +0000142
mblighe8819cd2008-02-15 16:48:40 +0000143 def findServiceEndpoint(self, name):
144 try:
145 meth = getattr(self.service, name)
mblighe8819cd2008-02-15 16:48:40 +0000146 return meth
mblighe8819cd2008-02-15 16:48:40 +0000147 except AttributeError:
148 raise ServiceMethodNotFound(name)
149
150 def invokeServiceEndpoint(self, meth, args):
151 return meth(*args)
152
showardef6fe022009-03-27 20:55:16 +0000153 @staticmethod
showard02ed4bd2009-09-09 15:30:11 +0000154 def translateResult(result_dict):
155 """
156 @param result_dict: a dictionary containing the result, error, traceback
157 and id.
158 @returns translated json result
159 """
160 if result_dict['err'] is not None:
161 error_name = result_dict['err'].__class__.__name__
162 result_dict['err'] = {'name': error_name,
163 'message': str(result_dict['err']),
164 'traceback': result_dict['err_traceback']}
165 result_dict['result'] = None
mblighe8819cd2008-02-15 16:48:40 +0000166
167 try:
showard02ed4bd2009-09-09 15:30:11 +0000168 json_dict = {'result': result_dict['result'],
169 'id': result_dict['id'],
170 'error': result_dict['err'] }
171 data = json_encoder.encode(json_dict)
mblighe8819cd2008-02-15 16:48:40 +0000172 except TypeError, e:
showard5da17d42008-04-28 18:07:58 +0000173 err_traceback = traceback.format_exc()
174 print err_traceback
175 err = {"name" : "JSONEncodeException",
176 "message" : "Result Object Not Serializable",
177 "traceback" : err_traceback}
showard02ed4bd2009-09-09 15:30:11 +0000178 data = json_encoder.encode({"result":None, "id":result_dict['id'],
179 "error":err})
showard5da17d42008-04-28 18:07:58 +0000180
mblighe8819cd2008-02-15 16:48:40 +0000181 return data