serviceHandler.py revision 0f5f0443e9286483365d6978ff432d8e4029d599
12be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi
22be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi"""
32be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi  Copyright (c) 2007 Jan-Klaas Kollhof
42be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi
52be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi  This file is part of jsonrpc.
62be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi
74ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn  jsonrpc is free software; you can redistribute it and/or modify
84ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn  it under the terms of the GNU Lesser General Public License as published by
94ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn  the Free Software Foundation; either version 2.1 of the License, or
104ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn  (at your option) any later version.
114ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn
124ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn  This software is distributed in the hope that it will be useful,
132be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi  but WITHOUT ANY WARRANTY; without even the implied warranty of
142be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
152be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi  GNU Lesser General Public License for more details.
16e7eb2bf306af6e8408cd77125861542d19e5ec6dUtkarsh Sanghi
172be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi  You should have received a copy of the GNU Lesser General Public License
186ea135676cf391fac45b0051242ccac935c8bc62Utkarsh Sanghi  along with this software; if not, write to the Free Software
192be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
202be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi"""
214ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn
222be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghiimport traceback
232be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi
242be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghifrom json import decoder, encoder
252be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghifrom autotest_lib.site_utils.graphite import stats
262be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi
272be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghidef customConvertJson(value):
282be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi    """\
292be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi    Recursively process JSON values and do type conversions.
302be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi    -change floats to ints
312be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi    -change unicodes to strs
322be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi    """
332be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi    if isinstance(value, float):
342be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi        return int(value)
352be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi    elif isinstance(value, unicode):
362be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi        return str(value)
372be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi    elif isinstance(value, list):
382be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi        return [customConvertJson(item) for item in value]
392be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi    elif isinstance(value, dict):
402be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi        new_dict = {}
412be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi        for key, val in value.iteritems():
422be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi            new_key = customConvertJson(key)
432be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi            new_val = customConvertJson(val)
442be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi            new_dict[new_key] = new_val
452be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi        return new_dict
462be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi    else:
472be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi        return value
482be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi
492be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghijson_encoder = encoder.JSONEncoder()
502be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghijson_decoder = decoder.JSONDecoder()
512be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi
522be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi
532be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghidef ServiceMethod(fn):
542be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi    fn.IsServiceMethod = True
552be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi    return fn
562be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi
572be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghiclass ServiceException(Exception):
582be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi    pass
592be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi
602be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghiclass ServiceRequestNotTranslatable(ServiceException):
612be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi    pass
622be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi
632be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghiclass BadServiceRequest(ServiceException):
642be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi    pass
652be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi
662be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghiclass ServiceMethodNotFound(ServiceException):
672be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi    pass
682be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi
692be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi
702be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghiclass ServiceHandler(object):
712be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi
722be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi    def __init__(self, service):
732be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi        self.service=service
742be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi
752be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi
762be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi    @classmethod
772be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi    def blank_result_dict(cls):
782be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi        return {'id': None, 'result': None, 'err': None, 'err_traceback': None}
792be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi
802be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi    def dispatchRequest(self, request):
816f68562edf5f66006c08de24d558732d6a389631Utkarsh Sanghi        """
822be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi        Invoke a json RPC call from a decoded json request.
83a5a2f2ea49e0085bf8d7f6f2b6e7cd624d710c01Utkarsh Sanghi        @param request: a decoded json_request
84a5a2f2ea49e0085bf8d7f6f2b6e7cd624d710c01Utkarsh Sanghi        @returns a dictionary with keys id, result, err and err_traceback
85a5a2f2ea49e0085bf8d7f6f2b6e7cd624d710c01Utkarsh Sanghi        """
860ebbc58fe6d45378a5b502c33eb1c4289fd8b05bUtkarsh Sanghi        results = self.blank_result_dict()
872be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi
882be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi        try:
892be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi            results['id'] = self._getRequestId(request)
902be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi            methName = request['method']
916f68562edf5f66006c08de24d558732d6a389631Utkarsh Sanghi            args = request['params']
926f68562edf5f66006c08de24d558732d6a389631Utkarsh Sanghi        except KeyError:
936f68562edf5f66006c08de24d558732d6a389631Utkarsh Sanghi            raise BadServiceRequest(request)
946f68562edf5f66006c08de24d558732d6a389631Utkarsh Sanghi
956f68562edf5f66006c08de24d558732d6a389631Utkarsh Sanghi        stats.Counter('rpc').increment(methName)
962be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi        timer = stats.Timer('rpc')
974ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn
984ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn        try:
994ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn            meth = self.findServiceEndpoint(methName)
1004ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn            timer.start()
1014ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn            results['result'] = self.invokeServiceEndpoint(meth, args)
1024ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn        except Exception, err:
1034ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn            results['err_traceback'] = traceback.format_exc()
1044ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn            results['err'] = err
1054ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn        finally:
1064ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn            timer.stop(methName)
1074ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn
1084ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn        return results
1094ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn
1104ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn
1114ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn    def _getRequestId(self, request):
1124ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn        try:
1132be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi            return request['id']
1142be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi        except KeyError:
1152be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi            raise BadServiceRequest(request)
1162be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi
1172be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi
1182be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi    def handleRequest(self, jsonRequest):
1192be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi        request = self.translateRequest(jsonRequest)
1202be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi        results = self.dispatchRequest(request)
1212be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi        return self.translateResult(results)
1222be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi
1232be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi
1246f68562edf5f66006c08de24d558732d6a389631Utkarsh Sanghi    @staticmethod
1252be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi    def translateRequest(data):
1266f68562edf5f66006c08de24d558732d6a389631Utkarsh Sanghi        try:
1276f68562edf5f66006c08de24d558732d6a389631Utkarsh Sanghi            req = json_decoder.decode(data)
1286ea135676cf391fac45b0051242ccac935c8bc62Utkarsh Sanghi        except:
1290ebbc58fe6d45378a5b502c33eb1c4289fd8b05bUtkarsh Sanghi            raise ServiceRequestNotTranslatable(data)
1302be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi        req = customConvertJson(req)
1316f68562edf5f66006c08de24d558732d6a389631Utkarsh Sanghi        return req
1322be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi
1332be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi    def findServiceEndpoint(self, name):
1346f68562edf5f66006c08de24d558732d6a389631Utkarsh Sanghi        try:
1356f68562edf5f66006c08de24d558732d6a389631Utkarsh Sanghi            meth = getattr(self.service, name)
1366f68562edf5f66006c08de24d558732d6a389631Utkarsh Sanghi            return meth
1376f68562edf5f66006c08de24d558732d6a389631Utkarsh Sanghi        except AttributeError:
1386f68562edf5f66006c08de24d558732d6a389631Utkarsh Sanghi            raise ServiceMethodNotFound(name)
1392be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi
1402be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi    def invokeServiceEndpoint(self, meth, args):
1412be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi        return meth(*args)
1422be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi
1432be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi    @staticmethod
1442be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi    def translateResult(result_dict):
1452be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi        """
1462be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi        @param result_dict: a dictionary containing the result, error, traceback
1472be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi                            and id.
1482be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi        @returns translated json result
1492be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi        """
1502be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi        if result_dict['err'] is not None:
1512be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi            error_name = result_dict['err'].__class__.__name__
1522be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi            result_dict['err'] = {'name': error_name,
1534ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn                                  'message': str(result_dict['err']),
1544ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn                                  'traceback': result_dict['err_traceback']}
1554ede7fcc1571b23867536b2506900fc3987c2dd5Darren Krahn            result_dict['result'] = None
1562be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi
1572be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi        try:
1582be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi            json_dict = {'result': result_dict['result'],
1592be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi                         'id': result_dict['id'],
1602be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi                         'error': result_dict['err'] }
1612be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi            data = json_encoder.encode(json_dict)
1622be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi        except TypeError, e:
1632be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi            err_traceback = traceback.format_exc()
1642be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi            print err_traceback
1652be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi            err = {"name" : "JSONEncodeException",
1662be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi                   "message" : "Result Object Not Serializable",
1672be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi                   "traceback" : err_traceback}
1682be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi            data = json_encoder.encode({"result":None, "id":result_dict['id'],
1692be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi                                        "error":err})
1702be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi
1712be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi        return data
1722be346182387c502f65c65ea4da49707026ce8f9Utkarsh Sanghi