PageRenderTime 48ms CodeModel.GetById 30ms app.highlight 14ms RepoModel.GetById 1ms app.codeStats 0ms

/examples/jsonrpc/public/services/jsonrpc/__init__.py

http://pyjamas.googlecode.com/
Python | 241 lines | 203 code | 13 blank | 25 comment | 4 complexity | d8401018c999557644a64108fa041064 MD5 | raw file
  1"""
  2  Copyright (c) 2006 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
 21from threading import  Event,  Lock
 22
 23from errors import *
 24
 25from simplejson import JSONDecoder, JSONEncoder
 26    
 27    
 28class JSONRPCEncoder(JSONEncoder):
 29    def default(self, obj):
 30        if isinstance(obj, JSONRPCError):
 31            return obj.__class__.__name__
 32        else:
 33            return JSONEncoder.default(self, obj)
 34
 35
 36class Timeout(Exception):
 37    pass
 38    
 39class ResponseEvent:
 40    """Event which is fired when the response is returned for a request.
 41    
 42        For each request sent this event is created. 
 43        An application can wait for the event to create a blocking request.
 44    """
 45    def __init__(self):
 46        self.__evt = Event()
 47        
 48    def waiting(self):
 49        return not self.__evt.isSet()
 50        
 51    def waitForResponse(self, timeOut=None):
 52        """blocks until the response arrived or timeout is reached."""
 53        self.__evt.wait(timeOut)
 54        if self.waiting():
 55            raise Timeout()
 56        else:
 57            if self.response["error"]:
 58                raise Exception(self.response["error"])
 59            else:
 60                return self.response["result"]
 61        
 62    def handleResponse(self, resp):
 63        self.response = resp
 64        self.__evt.set()
 65        
 66        
 67class SimpleMessageHandler:
 68    def __init__(self,  DecoderClass=JSONDecoder, EncoderClass=JSONRPCEncoder, messageDelimiter=""):
 69        self.decoder = DecoderClass()
 70        self.encoder = EncoderClass()
 71        self.partialData = ""
 72        self.respEvents={}
 73        self.respLock = Lock()
 74        self.messageDelimiter=messageDelimiter
 75        
 76    def close(self):
 77        pass
 78        
 79    def send(self, data):
 80        pass
 81        
 82    def sendMessage(self, msg):
 83        self.send(self.encoder.encode(msg) + self.messageDelimiter)
 84    
 85    def handlePartialData(self, data):
 86        data = self.partialData + data.replace("\r","").replace("\n", "")
 87        msgs=[]
 88        while data != "":
 89            pos = data.find("{")
 90            if(pos > -1):
 91                data=data[pos:]
 92                try:
 93                    (obj, pos) = self.decoder.raw_decode(data)
 94                    data = data[pos:]
 95                    msgs.append(obj)    
 96                except:
 97                    break
 98            else:
 99                break
100        self.partialData = data
101        
102        self.handleMessages(msgs)
103
104    def sendNotify(self, name, args):
105        """sends a notification object to the peer"""
106        self.sendMessage({"method":name, "params": args})
107        
108    def sendRequest(self, name, args):
109        """sends a request to the peer"""
110        (respEvt, id) = self.newResponseEvent()
111        self.sendMessage({"id":id, "method":name, "params": args})
112        return respEvt
113        
114    def sendResponse(self, id, result, error):
115        """sends a response to the peer"""
116        self.sendMessage({"result":result, "error": error, "id":id})
117    
118    def newResponseEvent(self):
119        """creates a response event and adds it to a waiting list
120           When the reponse arrives it will be removed from the list. 
121        """
122        respEvt = ResponseEvent()
123        self.respLock.acquire()
124        eid = id(respEvt)
125        self.respEvents[eid] = respEvt
126        self.respLock.release()
127        return (respEvt,eid)
128        
129    def handleMessages(self, msgs):
130        for msg in msgs:
131            if msg.has_key("method") and msg.has_key("params"):
132                if msg.has_key("id"):
133                    if msg["id"]:
134                        self.handleRequest(msg)    
135                    else:
136                        self.handleNotification(msg)
137                else:
138                    self.handleNotification(msg)
139            elif msg.has_key("result") and msg.has_key("error"):
140                self.handleResponse(msg)
141            else:#unknown object 
142                self.sendResponse(None, InvalidJSONMessage())
143                self.close()
144                
145    def handleResponse(self, resp):
146        """handles a response by fireing the response event for the response coming in"""
147        id=resp["id"]
148        evt = self.respEvents[id]
149        del(self.respEvents[id])
150        evt.handleResponse(resp)
151    
152    def handleRequest(self, request):
153        pass
154    def handleNotification(self, notification):
155        pass
156        
157        
158import re
159NameAllowedRegExp=re.compile("^[a-zA-Z]\w*$")
160def nameAllowed(name):
161    """checks if a name is allowed.
162    """
163    if NameAllowedRegExp.match(name):
164        return 1
165    else:
166        return 0
167    
168def getMethodByName(obj, name):
169
170    """searches for an object with the name given inside the object given.
171       "obj.child.meth" will return the meth obj.
172    """
173    try:#to get a method by asking the service
174        obj = obj._getMethodByName(name)
175    except:
176        #assumed a childObject is ment 
177        #split the name from objName.childObjName... -> [objName, childObjName, ...]
178        #and get all objects up to the last in list with name checking from the service object
179        names = name.split(".")
180        for name in names:
181            if nameAllowed(name):
182                obj = getattr(obj, name)
183            else:
184                raise MethodNameNotAllowed()
185        
186    return obj  
187    
188
189
190class SimpleServiceHandler(SimpleMessageHandler):
191    def __init__(self, service, DecoderClass=JSONDecoder, EncoderClass=JSONRPCEncoder, messageDelimiter=""):
192        self.service = service
193        SimpleMessageHandler.__init__(self, DecoderClass, EncoderClass, messageDelimiter)
194        try:
195            service._newConnection(self)
196        except:
197            pass
198        
199    def close(self):
200        try:
201            self.service._closingConnection(self)
202        except:
203            pass
204        
205    def handleRequest(self, req):
206        """handles a request by calling the appropriete method the service exposes"""
207        name = req["method"]
208        params = req["params"]
209        id=req["id"]
210        obj=None
211        try: #to get a callable obj 
212            obj = getMethodByName(self.service, name)
213        except MethodNameNotAllowed,e:
214            self.sendResponse(id, None, e)
215        except:
216            self.sendResponse(id, None, MethodNotFound())
217        if obj:
218            try: #to call the object with parameters
219                rslt = obj(*params)
220                self.sendResponse(id, rslt, None)
221            except TypeError: # wrong arguments
222                #todo what if the TypeError was not thrown directly by the callable obj
223                s=getTracebackStr()
224                self.sendResponse(id, None, InvalidMethodParameters())
225            except: #error inside the callable object
226                s=getTracebackStr()
227                self.sendResponse(id, None, s)
228                
229    def handleNotification(self, req):
230        """handles a notification request by calling the appropriete method the service exposes"""
231        name = req["method"]
232        params = req["params"]
233        try: #to get a callable obj 
234            obj = getMethodByName(self.service, name)
235            rslt = obj(*params)
236        except:
237            pass
238                
239    
240    
241