• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
21import socket
22import traceback
23
24from json import decoder
25
26try:
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()
31except django_exceptions.ImproperlyConfigured:
32    from json import encoder
33    json_encoder = encoder.JSONEncoder()
34
35
36json_decoder = decoder.JSONDecoder()
37
38
39def 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
62def ServiceMethod(fn):
63    fn.IsServiceMethod = True
64    return fn
65
66class ServiceException(Exception):
67    pass
68
69class ServiceRequestNotTranslatable(ServiceException):
70    pass
71
72class BadServiceRequest(ServiceException):
73    pass
74
75class ServiceMethodNotFound(ServiceException):
76    pass
77
78
79class 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