Home | History | Annotate | Download | only in json_rpc
      1 """
      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 
     21 import traceback
     22 
     23 from json import decoder
     24 
     25 try:
     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()
     30 except django_exceptions.ImproperlyConfigured:
     31     from json import encoder
     32     json_encoder = encoder.JSONEncoder()
     33 
     34 from autotest_lib.client.common_lib.cros.graphite import autotest_stats
     35 
     36 
     37 json_decoder = decoder.JSONDecoder()
     38 
     39 
     40 def 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 
     62 
     63 def ServiceMethod(fn):
     64     fn.IsServiceMethod = True
     65     return fn
     66 
     67 class ServiceException(Exception):
     68     pass
     69 
     70 class ServiceRequestNotTranslatable(ServiceException):
     71     pass
     72 
     73 class BadServiceRequest(ServiceException):
     74     pass
     75 
     76 class ServiceMethodNotFound(ServiceException):
     77     pass
     78 
     79 
     80 class ServiceHandler(object):
     81 
     82     def __init__(self, service):
     83         self.service=service
     84 
     85 
     86     @classmethod
     87     def blank_result_dict(cls):
     88         return {'id': None, 'result': None, 'err': None, 'err_traceback': None}
     89 
     90     def dispatchRequest(self, request):
     91         """
     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         """
     96         results = self.blank_result_dict()
     97 
     98         try:
     99             results['id'] = self._getRequestId(request)
    100             methName = request['method']
    101             args = request['params']
    102         except KeyError:
    103             raise BadServiceRequest(request)
    104 
    105         autotest_stats.Counter('rpc').increment(methName)
    106 
    107         metadata = request.copy()
    108         metadata['_type'] = 'rpc'
    109         timer = autotest_stats.Timer('rpc', metadata=metadata)
    110 
    111         try:
    112             timer.start()
    113             meth = self.findServiceEndpoint(methName)
    114             results['result'] = self.invokeServiceEndpoint(meth, args)
    115         except Exception, err:
    116             results['err_traceback'] = traceback.format_exc()
    117             results['err'] = err
    118         finally:
    119             timer.stop(methName)
    120 
    121         return results
    122 
    123 
    124     def _getRequestId(self, request):
    125         try:
    126             return request['id']
    127         except KeyError:
    128             raise BadServiceRequest(request)
    129 
    130 
    131     def handleRequest(self, jsonRequest):
    132         request = self.translateRequest(jsonRequest)
    133         results = self.dispatchRequest(request)
    134         return self.translateResult(results)
    135 
    136 
    137     @staticmethod
    138     def translateRequest(data):
    139         try:
    140             req = json_decoder.decode(data)
    141         except:
    142             raise ServiceRequestNotTranslatable(data)
    143         req = customConvertJson(req)
    144         return req
    145 
    146     def findServiceEndpoint(self, name):
    147         try:
    148             meth = getattr(self.service, name)
    149             return meth
    150         except AttributeError:
    151             raise ServiceMethodNotFound(name)
    152 
    153     def invokeServiceEndpoint(self, meth, args):
    154         return meth(*args)
    155 
    156     @staticmethod
    157     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
    169 
    170         try:
    171             json_dict = {'result': result_dict['result'],
    172                          'id': result_dict['id'],
    173                          'error': result_dict['err'] }
    174             data = json_encoder.encode(json_dict)
    175         except TypeError, e:
    176             err_traceback = traceback.format_exc()
    177             print err_traceback
    178             err = {"name" : "JSONEncodeException",
    179                    "message" : "Result Object Not Serializable",
    180                    "traceback" : err_traceback}
    181             data = json_encoder.encode({"result":None, "id":result_dict['id'],
    182                                         "error":err})
    183 
    184         return data
    185