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 socket
     22 import traceback
     23 
     24 from json import decoder
     25 
     26 try:
     27     from django.core import exceptions as django_exceptions
     28     # Django JSON encoder uses the standard json encoder but can handle DateTime
     29     from django.core.serializers import json as django_encoder
     30     json_encoder = django_encoder.DjangoJSONEncoder()
     31 except django_exceptions.ImproperlyConfigured:
     32     from json import encoder
     33     json_encoder = encoder.JSONEncoder()
     34 
     35 
     36 json_decoder = decoder.JSONDecoder()
     37 
     38 
     39 def customConvertJson(value):
     40     """\
     41     Recursively process JSON values and do type conversions.
     42     -change floats to ints
     43     -change unicodes to strs
     44     """
     45     if isinstance(value, float):
     46         return int(value)
     47     elif isinstance(value, unicode):
     48         return str(value)
     49     elif isinstance(value, list):
     50         return [customConvertJson(item) for item in value]
     51     elif isinstance(value, dict):
     52         new_dict = {}
     53         for key, val in value.iteritems():
     54             new_key = customConvertJson(key)
     55             new_val = customConvertJson(val)
     56             new_dict[new_key] = new_val
     57         return new_dict
     58     else:
     59         return value
     60 
     61 
     62 def ServiceMethod(fn):
     63     fn.IsServiceMethod = True
     64     return fn
     65 
     66 class ServiceException(Exception):
     67     pass
     68 
     69 class ServiceRequestNotTranslatable(ServiceException):
     70     pass
     71 
     72 class BadServiceRequest(ServiceException):
     73     pass
     74 
     75 class ServiceMethodNotFound(ServiceException):
     76     pass
     77 
     78 
     79 class ServiceHandler(object):
     80 
     81     def __init__(self, service):
     82         self.service=service
     83 
     84 
     85     @classmethod
     86     def blank_result_dict(cls):
     87         return {'id': None, 'result': None, 'err': None, 'err_traceback': None}
     88 
     89     def dispatchRequest(self, request):
     90         """
     91         Invoke a json RPC call from a decoded json request.
     92         @param request: a decoded json_request
     93         @returns a dictionary with keys id, result, err and err_traceback
     94         """
     95         results = self.blank_result_dict()
     96 
     97         try:
     98             results['id'] = self._getRequestId(request)
     99             methName = request['method']
    100             args = request['params']
    101         except KeyError:
    102             raise BadServiceRequest(request)
    103 
    104         metadata = request.copy()
    105         metadata['_type'] = 'rpc'
    106         metadata['rpc_server'] = socket.gethostname()
    107         try:
    108             meth = self.findServiceEndpoint(methName)
    109             results['result'] = self.invokeServiceEndpoint(meth, args)
    110         except Exception, err:
    111             results['err_traceback'] = traceback.format_exc()
    112             results['err'] = err
    113 
    114         return results
    115 
    116 
    117     def _getRequestId(self, request):
    118         try:
    119             return request['id']
    120         except KeyError:
    121             raise BadServiceRequest(request)
    122 
    123 
    124     def handleRequest(self, jsonRequest):
    125         request = self.translateRequest(jsonRequest)
    126         results = self.dispatchRequest(request)
    127         return self.translateResult(results)
    128 
    129 
    130     @staticmethod
    131     def translateRequest(data):
    132         try:
    133             req = json_decoder.decode(data)
    134         except:
    135             raise ServiceRequestNotTranslatable(data)
    136         req = customConvertJson(req)
    137         return req
    138 
    139     def findServiceEndpoint(self, name):
    140         try:
    141             meth = getattr(self.service, name)
    142             return meth
    143         except AttributeError:
    144             raise ServiceMethodNotFound(name)
    145 
    146     def invokeServiceEndpoint(self, meth, args):
    147         return meth(*args)
    148 
    149     @staticmethod
    150     def translateResult(result_dict):
    151         """
    152         @param result_dict: a dictionary containing the result, error, traceback
    153                             and id.
    154         @returns translated json result
    155         """
    156         if result_dict['err'] is not None:
    157             error_name = result_dict['err'].__class__.__name__
    158             result_dict['err'] = {'name': error_name,
    159                                   'message': str(result_dict['err']),
    160                                   'traceback': result_dict['err_traceback']}
    161             result_dict['result'] = None
    162 
    163         try:
    164             json_dict = {'result': result_dict['result'],
    165                          'id': result_dict['id'],
    166                          'error': result_dict['err'] }
    167             data = json_encoder.encode(json_dict)
    168         except TypeError, e:
    169             err_traceback = traceback.format_exc()
    170             print err_traceback
    171             err = {"name" : "JSONEncodeException",
    172                    "message" : "Result Object Not Serializable",
    173                    "traceback" : err_traceback}
    174             data = json_encoder.encode({"result":None, "id":result_dict['id'],
    175                                         "error":err})
    176 
    177         return data
    178