blob: cd6a0f345350288a051d90a88abbb94b91cb7830 [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
Alex Miller4f341702013-03-25 12:39:12 -070024from json import decoder, encoder
Alex Miller1b249312013-11-12 15:45:37 -080025from autotest_lib.site_utils.graphite import stats
mblighe8819cd2008-02-15 16:48:40 +000026
27def customConvertJson(value):
28 """\
29 Recursively process JSON values and do type conversions.
30 -change floats to ints
31 -change unicodes to strs
32 """
33 if isinstance(value, float):
34 return int(value)
35 elif isinstance(value, unicode):
36 return str(value)
37 elif isinstance(value, list):
38 return [customConvertJson(item) for item in value]
39 elif isinstance(value, dict):
40 new_dict = {}
41 for key, val in value.iteritems():
42 new_key = customConvertJson(key)
43 new_val = customConvertJson(val)
44 new_dict[new_key] = new_val
45 return new_dict
46 else:
47 return value
48
49json_encoder = encoder.JSONEncoder()
50json_decoder = decoder.JSONDecoder()
51
52
53def ServiceMethod(fn):
54 fn.IsServiceMethod = True
55 return fn
56
57class ServiceException(Exception):
58 pass
59
60class ServiceRequestNotTranslatable(ServiceException):
61 pass
62
63class BadServiceRequest(ServiceException):
64 pass
65
66class ServiceMethodNotFound(ServiceException):
67 pass
68
showard3d6ae112009-05-02 00:45:48 +000069
mblighe8819cd2008-02-15 16:48:40 +000070class ServiceHandler(object):
71
72 def __init__(self, service):
73 self.service=service
jadmanski0afbb632008-06-06 21:10:57 +000074
showard2df3c692009-09-11 18:41:07 +000075
76 @classmethod
77 def blank_result_dict(cls):
78 return {'id': None, 'result': None, 'err': None, 'err_traceback': None}
79
showard3d6ae112009-05-02 00:45:48 +000080 def dispatchRequest(self, request):
showard02ed4bd2009-09-09 15:30:11 +000081 """
82 Invoke a json RPC call from a decoded json request.
83 @param request: a decoded json_request
84 @returns a dictionary with keys id, result, err and err_traceback
85 """
showard2df3c692009-09-11 18:41:07 +000086 results = self.blank_result_dict()
showard02ed4bd2009-09-09 15:30:11 +000087
showard3d6ae112009-05-02 00:45:48 +000088 try:
showard02ed4bd2009-09-09 15:30:11 +000089 results['id'] = self._getRequestId(request)
showard3d6ae112009-05-02 00:45:48 +000090 methName = request['method']
91 args = request['params']
92 except KeyError:
93 raise BadServiceRequest(request)
mblighe8819cd2008-02-15 16:48:40 +000094
Alex Miller1b249312013-11-12 15:45:37 -080095 stats.Counter('rpc').increment(methName)
96
showard02ed4bd2009-09-09 15:30:11 +000097 try:
98 meth = self.findServiceEndpoint(methName)
99 results['result'] = self.invokeServiceEndpoint(meth, args)
100 except Exception, err:
101 results['err_traceback'] = traceback.format_exc()
102 results['err'] = err
103
104 return results
105
showard3d6ae112009-05-02 00:45:48 +0000106
107 def _getRequestId(self, request):
108 try:
109 return request['id']
110 except KeyError:
111 raise BadServiceRequest(request)
112
showard02ed4bd2009-09-09 15:30:11 +0000113
showard3d6ae112009-05-02 00:45:48 +0000114 def handleRequest(self, jsonRequest):
showard3d6ae112009-05-02 00:45:48 +0000115 request = self.translateRequest(jsonRequest)
showard02ed4bd2009-09-09 15:30:11 +0000116 results = self.dispatchRequest(request)
117 return self.translateResult(results)
jadmanski0afbb632008-06-06 21:10:57 +0000118
mblighe8819cd2008-02-15 16:48:40 +0000119
showardef6fe022009-03-27 20:55:16 +0000120 @staticmethod
121 def translateRequest(data):
mblighe8819cd2008-02-15 16:48:40 +0000122 try:
123 req = json_decoder.decode(data)
124 except:
125 raise ServiceRequestNotTranslatable(data)
showard3d6ae112009-05-02 00:45:48 +0000126 req = customConvertJson(req)
mblighe8819cd2008-02-15 16:48:40 +0000127 return req
showard5da17d42008-04-28 18:07:58 +0000128
mblighe8819cd2008-02-15 16:48:40 +0000129 def findServiceEndpoint(self, name):
130 try:
131 meth = getattr(self.service, name)
mblighe8819cd2008-02-15 16:48:40 +0000132 return meth
mblighe8819cd2008-02-15 16:48:40 +0000133 except AttributeError:
134 raise ServiceMethodNotFound(name)
135
136 def invokeServiceEndpoint(self, meth, args):
137 return meth(*args)
138
showardef6fe022009-03-27 20:55:16 +0000139 @staticmethod
showard02ed4bd2009-09-09 15:30:11 +0000140 def translateResult(result_dict):
141 """
142 @param result_dict: a dictionary containing the result, error, traceback
143 and id.
144 @returns translated json result
145 """
146 if result_dict['err'] is not None:
147 error_name = result_dict['err'].__class__.__name__
148 result_dict['err'] = {'name': error_name,
149 'message': str(result_dict['err']),
150 'traceback': result_dict['err_traceback']}
151 result_dict['result'] = None
mblighe8819cd2008-02-15 16:48:40 +0000152
153 try:
showard02ed4bd2009-09-09 15:30:11 +0000154 json_dict = {'result': result_dict['result'],
155 'id': result_dict['id'],
156 'error': result_dict['err'] }
157 data = json_encoder.encode(json_dict)
mblighe8819cd2008-02-15 16:48:40 +0000158 except TypeError, e:
showard5da17d42008-04-28 18:07:58 +0000159 err_traceback = traceback.format_exc()
160 print err_traceback
161 err = {"name" : "JSONEncodeException",
162 "message" : "Result Object Not Serializable",
163 "traceback" : err_traceback}
showard02ed4bd2009-09-09 15:30:11 +0000164 data = json_encoder.encode({"result":None, "id":result_dict['id'],
165 "error":err})
showard5da17d42008-04-28 18:07:58 +0000166
mblighe8819cd2008-02-15 16:48:40 +0000167 return data