xref: /aosp_15_r20/external/autotest/frontend/afe/json_rpc/serviceHandler.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1*9c5db199SXin Li"""
2*9c5db199SXin Li  Copyright (c) 2007 Jan-Klaas Kollhof
3*9c5db199SXin Li
4*9c5db199SXin Li  This file is part of jsonrpc.
5*9c5db199SXin Li
6*9c5db199SXin Li  jsonrpc is free software; you can redistribute it and/or modify
7*9c5db199SXin Li  it under the terms of the GNU Lesser General Public License as published by
8*9c5db199SXin Li  the Free Software Foundation; either version 2.1 of the License, or
9*9c5db199SXin Li  (at your option) any later version.
10*9c5db199SXin Li
11*9c5db199SXin Li  This software is distributed in the hope that it will be useful,
12*9c5db199SXin Li  but WITHOUT ANY WARRANTY; without even the implied warranty of
13*9c5db199SXin Li  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14*9c5db199SXin Li  GNU Lesser General Public License for more details.
15*9c5db199SXin Li
16*9c5db199SXin Li  You should have received a copy of the GNU Lesser General Public License
17*9c5db199SXin Li  along with this software; if not, write to the Free Software
18*9c5db199SXin Li  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19*9c5db199SXin Li"""
20*9c5db199SXin Li
21*9c5db199SXin Lifrom __future__ import absolute_import
22*9c5db199SXin Lifrom __future__ import division
23*9c5db199SXin Lifrom __future__ import print_function
24*9c5db199SXin Li
25*9c5db199SXin Liimport socket
26*9c5db199SXin Liimport traceback
27*9c5db199SXin Li
28*9c5db199SXin Lifrom json import decoder
29*9c5db199SXin Li
30*9c5db199SXin Liimport six
31*9c5db199SXin Li
32*9c5db199SXin Litry:
33*9c5db199SXin Li    from django.core import exceptions as django_exceptions
34*9c5db199SXin Li    # Django JSON encoder uses the standard json encoder but can handle DateTime
35*9c5db199SXin Li    from django.core.serializers import json as django_encoder
36*9c5db199SXin Li    json_encoder = django_encoder.DjangoJSONEncoder()
37*9c5db199SXin Liexcept django_exceptions.ImproperlyConfigured:
38*9c5db199SXin Li    from json import encoder
39*9c5db199SXin Li    json_encoder = encoder.JSONEncoder()
40*9c5db199SXin Li
41*9c5db199SXin Li
42*9c5db199SXin Lijson_decoder = decoder.JSONDecoder()
43*9c5db199SXin Li
44*9c5db199SXin Li
45*9c5db199SXin Lidef customConvertJson(value):
46*9c5db199SXin Li    """\
47*9c5db199SXin Li    Recursively process JSON values and do type conversions.
48*9c5db199SXin Li    -change floats to ints
49*9c5db199SXin Li    -change unicodes to strs
50*9c5db199SXin Li    """
51*9c5db199SXin Li    if isinstance(value, float):
52*9c5db199SXin Li        return int(value)
53*9c5db199SXin Li    elif isinstance(value, six.text_type):
54*9c5db199SXin Li        return str(value)
55*9c5db199SXin Li    elif isinstance(value, list):
56*9c5db199SXin Li        return [customConvertJson(item) for item in value]
57*9c5db199SXin Li    elif isinstance(value, dict):
58*9c5db199SXin Li        new_dict = {}
59*9c5db199SXin Li        for key, val in six.iteritems(value):
60*9c5db199SXin Li            new_key = customConvertJson(key)
61*9c5db199SXin Li            new_val = customConvertJson(val)
62*9c5db199SXin Li            new_dict[new_key] = new_val
63*9c5db199SXin Li        return new_dict
64*9c5db199SXin Li    else:
65*9c5db199SXin Li        return value
66*9c5db199SXin Li
67*9c5db199SXin Li
68*9c5db199SXin Lidef ServiceMethod(fn):
69*9c5db199SXin Li    fn.IsServiceMethod = True
70*9c5db199SXin Li    return fn
71*9c5db199SXin Li
72*9c5db199SXin Liclass ServiceException(Exception):
73*9c5db199SXin Li    pass
74*9c5db199SXin Li
75*9c5db199SXin Liclass ServiceRequestNotTranslatable(ServiceException):
76*9c5db199SXin Li    pass
77*9c5db199SXin Li
78*9c5db199SXin Liclass BadServiceRequest(ServiceException):
79*9c5db199SXin Li    pass
80*9c5db199SXin Li
81*9c5db199SXin Liclass ServiceMethodNotFound(ServiceException):
82*9c5db199SXin Li    pass
83*9c5db199SXin Li
84*9c5db199SXin Li
85*9c5db199SXin Liclass ServiceHandler(object):
86*9c5db199SXin Li
87*9c5db199SXin Li    def __init__(self, service):
88*9c5db199SXin Li        self.service=service
89*9c5db199SXin Li
90*9c5db199SXin Li
91*9c5db199SXin Li    @classmethod
92*9c5db199SXin Li    def blank_result_dict(cls):
93*9c5db199SXin Li        return {'id': None, 'result': None, 'err': None, 'err_traceback': None}
94*9c5db199SXin Li
95*9c5db199SXin Li    def dispatchRequest(self, request):
96*9c5db199SXin Li        """
97*9c5db199SXin Li        Invoke a json RPC call from a decoded json request.
98*9c5db199SXin Li        @param request: a decoded json_request
99*9c5db199SXin Li        @returns a dictionary with keys id, result, err and err_traceback
100*9c5db199SXin Li        """
101*9c5db199SXin Li        results = self.blank_result_dict()
102*9c5db199SXin Li
103*9c5db199SXin Li        try:
104*9c5db199SXin Li            results['id'] = self._getRequestId(request)
105*9c5db199SXin Li            methName = request['method']
106*9c5db199SXin Li            args = request['params']
107*9c5db199SXin Li        except KeyError:
108*9c5db199SXin Li            raise BadServiceRequest(request)
109*9c5db199SXin Li
110*9c5db199SXin Li        metadata = request.copy()
111*9c5db199SXin Li        metadata['_type'] = 'rpc'
112*9c5db199SXin Li        metadata['rpc_server'] = socket.gethostname()
113*9c5db199SXin Li        try:
114*9c5db199SXin Li            meth = self.findServiceEndpoint(methName)
115*9c5db199SXin Li            results['result'] = self.invokeServiceEndpoint(meth, args)
116*9c5db199SXin Li        except Exception as err:
117*9c5db199SXin Li            results['err_traceback'] = traceback.format_exc()
118*9c5db199SXin Li            results['err'] = err
119*9c5db199SXin Li
120*9c5db199SXin Li        return results
121*9c5db199SXin Li
122*9c5db199SXin Li
123*9c5db199SXin Li    def _getRequestId(self, request):
124*9c5db199SXin Li        try:
125*9c5db199SXin Li            return request['id']
126*9c5db199SXin Li        except KeyError:
127*9c5db199SXin Li            raise BadServiceRequest(request)
128*9c5db199SXin Li
129*9c5db199SXin Li
130*9c5db199SXin Li    def handleRequest(self, jsonRequest):
131*9c5db199SXin Li        request = self.translateRequest(jsonRequest)
132*9c5db199SXin Li        results = self.dispatchRequest(request)
133*9c5db199SXin Li        return self.translateResult(results)
134*9c5db199SXin Li
135*9c5db199SXin Li
136*9c5db199SXin Li    @staticmethod
137*9c5db199SXin Li    def translateRequest(data):
138*9c5db199SXin Li        try:
139*9c5db199SXin Li            req = json_decoder.decode(data)
140*9c5db199SXin Li        except:
141*9c5db199SXin Li            raise ServiceRequestNotTranslatable(data)
142*9c5db199SXin Li        req = customConvertJson(req)
143*9c5db199SXin Li        return req
144*9c5db199SXin Li
145*9c5db199SXin Li    def findServiceEndpoint(self, name):
146*9c5db199SXin Li        try:
147*9c5db199SXin Li            meth = getattr(self.service, name)
148*9c5db199SXin Li            return meth
149*9c5db199SXin Li        except AttributeError:
150*9c5db199SXin Li            raise ServiceMethodNotFound(name)
151*9c5db199SXin Li
152*9c5db199SXin Li    def invokeServiceEndpoint(self, meth, args):
153*9c5db199SXin Li        return meth(*args)
154*9c5db199SXin Li
155*9c5db199SXin Li    @staticmethod
156*9c5db199SXin Li    def translateResult(result_dict):
157*9c5db199SXin Li        """
158*9c5db199SXin Li        @param result_dict: a dictionary containing the result, error, traceback
159*9c5db199SXin Li                            and id.
160*9c5db199SXin Li        @returns translated json result
161*9c5db199SXin Li        """
162*9c5db199SXin Li        if result_dict['err'] is not None:
163*9c5db199SXin Li            error_name = result_dict['err'].__class__.__name__
164*9c5db199SXin Li            result_dict['err'] = {'name': error_name,
165*9c5db199SXin Li                                  'message': str(result_dict['err']),
166*9c5db199SXin Li                                  'traceback': result_dict['err_traceback']}
167*9c5db199SXin Li            result_dict['result'] = None
168*9c5db199SXin Li
169*9c5db199SXin Li        try:
170*9c5db199SXin Li            json_dict = {'result': result_dict['result'],
171*9c5db199SXin Li                         'id': result_dict['id'],
172*9c5db199SXin Li                         'error': result_dict['err'] }
173*9c5db199SXin Li            data = json_encoder.encode(json_dict)
174*9c5db199SXin Li        except TypeError as e:
175*9c5db199SXin Li            err_traceback = traceback.format_exc()
176*9c5db199SXin Li            print(err_traceback)
177*9c5db199SXin Li            err = {"name" : "JSONEncodeException",
178*9c5db199SXin Li                   "message" : "Result Object Not Serializable",
179*9c5db199SXin Li                   "traceback" : err_traceback}
180*9c5db199SXin Li            data = json_encoder.encode({"result":None, "id":result_dict['id'],
181*9c5db199SXin Li                                        "error":err})
182*9c5db199SXin Li
183*9c5db199SXin Li        return data
184