replaced str() calls with unicode()
[OpenSRF.git] / src / python / osrf / gateway.py
index 3a6efaf..424b094 100644 (file)
@@ -1,8 +1,9 @@
 from xml.dom import minidom
 from xml.sax import handler, make_parser, saxutils
-from json import *
-from net_obj import *
-import urllib, urllib2, sys
+from osrf.json import *
+from osrf.net_obj import *
+from osrf.log import *
+import urllib, urllib2, sys, re
 
 defaultHost = None
 
@@ -11,6 +12,10 @@ class GatewayRequest:
         self.service = service
         self.method = method
         self.params = params
+        self.path = 'gateway'
+
+    def setPath(self, path):
+        self.path = path
 
     def send(self):
         params = self.buildPOSTParams()
@@ -20,7 +25,7 @@ class GatewayRequest:
             response =urllib2.urlopen(request)
         except urllib2.HTTPError, e:
             # log this?
-            sys.stderr.write('HTTPError: code=%d : %s' % (e.code, str(e)))
+            sys.stderr.write('%s => %s?%s\n' % (unicode(e), self.buildURL(), params))
             raise e
             
         return self.handleResponse(response)
@@ -30,13 +35,12 @@ class GatewayRequest:
         params = urllib.urlencode({   
             'service': self.service,
             'method': self.method,
-            'format': self.getFormat()
+            'format': self.getFormat(),
+            'input_format': self.getInputFormat()
         })
 
         for p in self.params:
-            param = {'param': osrfObjectToJSON(p)}
-            params += '&%s' % urllib.urlencode(param)
-
+            params += '&param=%s' % urllib.quote(self.encodeParam(p), "'/")
         return params
 
     def setDefaultHost(host):
@@ -45,8 +49,33 @@ class GatewayRequest:
     setDefaultHost = staticmethod(setDefaultHost)
 
     def buildURL(self):
-        return 'http://%s/gateway' % defaultHost
+        return 'http://%s/%s' % (defaultHost, self.path)
+
+class JSONGatewayRequest(GatewayRequest):
+    def __init__(self, service, method, *params):
+        GatewayRequest.__init__(self, service, method, list(params))
+
+    def getFormat(self):
+        return 'json'
 
+    def getInputFormat(self):
+        return self.getFormat()
+
+    def handleResponse(self, response):
+        s = response.read()
+        obj = osrfJSONToObject(s)
+        if obj['status'] != 200:
+            sys.stderr.write('JSON gateway returned status %d:\n%s\n' % (obj['status'], s))
+            return None
+
+        # the gateway wraps responses in an array to handle streaming data
+        # if there is only one item in the array, it (probably) wasn't a streaming request
+        p = obj['payload']
+        if len(p) > 1: return p
+        return p[0]
+
+    def encodeParam(self, param):
+        return osrfObjectToJSON(param)
 
 class XMLGatewayRequest(GatewayRequest):
 
@@ -56,19 +85,34 @@ class XMLGatewayRequest(GatewayRequest):
     def getFormat(self):
         return 'xml'
 
+    def getInputFormat(self):
+        return self.getFormat()
+
     def handleResponse(self, response):
         handler = XMLGatewayParser()
         parser = make_parser()
         parser.setContentHandler(handler)
-        parser.parse(response)
+        try:
+            parser.parse(response)
+        except Exception, e:
+            osrfLogErr('Error parsing gateway XML: %s' % unicode(e))
+            return None
+
         return handler.getResult()
 
+    def encodeParam(self, param):
+        return osrfObjectToXML(param);
+
 class XMLGatewayParser(handler.ContentHandler):
 
     def __init__(self):
         self.result = None
         self.objStack = []
         self.keyStack = []
+        self.posStack = [] # for tracking array-based hinted object indices
+
+        # true if we are parsing an element that may have character data
+        self.charsPending = 0 
 
     def getResult(self):
         return self.result
@@ -80,17 +124,31 @@ class XMLGatewayParser(handler.ContentHandler):
         return None
 
     def startElement(self, name, attrs):
+        
+        if self.charsPending:
+            # we just read a 'string' or 'number' element that resulted
+            # in no text data.  Appaned a None object
+            self.appendChild(None)
+
+        if name == 'null':
+            self.appendChild(None)
+            return
 
-        # XXX add support for serializable objects!
+        if name == 'string' or name == 'number':
+            self.charsPending = True
+            return
 
         if name == 'element': # this is an object item wrapper
             self.keyStack.append(self.__getAttr(attrs, 'key'))
             return
 
-        if name == 'object':
-            obj = {}
+        hint = self.__getAttr(attrs, 'class_hint')
+        if hint:
+            obj = osrfNewObjectFromHint(hint)
             self.appendChild(obj)
             self.objStack.append(obj)
+            if name == 'array':
+                self.posStack.append(0)
             return
 
         if name == 'array':
@@ -99,8 +157,10 @@ class XMLGatewayParser(handler.ContentHandler):
             self.objStack.append(obj)
             return
 
-        if name == 'null':
-            self.appendChild(None)
+        if name == 'object':
+            obj = {}
+            self.appendChild(obj)
+            self.objStack.append(obj)
             return
 
         if name == 'boolean':
@@ -117,17 +177,31 @@ class XMLGatewayParser(handler.ContentHandler):
 
         parent = self.objStack[len(self.objStack)-1]
 
-        if( isinstance(parent, list) ):
+        if isinstance(parent, list):
             parent.append(child)
         else:
-            parent[self.keyStack.pop()] = child
+            if isinstance(parent, dict):
+                parent[self.keyStack.pop()] = child
+            else:
+                if isinstance(parent, osrfNetworkObject):
+                    key = None
+                    if parent.getRegistry().wireProtocol == 'array':
+                        keys = parent.getRegistry().keys
+                        i = self.posStack.pop()
+                        key = keys[i]
+                        if i+1 < len(keys):
+                            self.posStack.append(i+1)
+                    else:
+                        key = self.keyStack.pop()
+
+                    parent.setField(key, child)
 
     def endElement(self, name):
         if name == 'array' or name == 'object':
             self.objStack.pop()
 
     def characters(self, chars):
-        #self.appendChild(''.join(chars[start:leng+start]))
+        self.charsPending = False
         self.appendChild(urllib.unquote_plus(chars))