added JSON gateway support
[OpenSRF.git] / src / python / osrf / gateway.py
1 from xml.dom import minidom
2 from xml.sax import handler, make_parser, saxutils
3 from json import *
4 from net_obj import *
5 import urllib, urllib2, sys, re
6
7 defaultHost = None
8
9 class GatewayRequest:
10     def __init__(self, service, method, params=[]):
11         self.service = service
12         self.method = method
13         self.params = params
14         self.path = 'gateway'
15
16     def setPath(self, path):
17         self.path = path
18
19     def send(self):
20         params = self.buildPOSTParams()
21         request = urllib2.Request(self.buildURL(), data=params)
22         response = None
23         try:
24             response =urllib2.urlopen(request)
25         except urllib2.HTTPError, e:
26             # log this?
27             sys.stderr.write('%s => %s?%s\n' % (str(e), self.buildURL(), params))
28             raise e
29             
30         return self.handleResponse(response)
31
32     def buildPOSTParams(self):
33
34         params = urllib.urlencode({   
35             'service': self.service,
36             'method': self.method,
37             'format': self.getFormat(),
38             'input_format': self.getInputFormat()
39         })
40
41         for p in self.params:
42             params += '&param=%s' % urllib.quote(self.encodeParam(p), "'/")
43         return params
44
45     def setDefaultHost(host):
46         global defaultHost
47         defaultHost = host
48     setDefaultHost = staticmethod(setDefaultHost)
49
50     def buildURL(self):
51         return 'http://%s/%s' % (defaultHost, self.path)
52
53 class JSONGatewayRequest(GatewayRequest):
54     def __init__(self, service, method, *params):
55         GatewayRequest.__init__(self, service, method, list(params))
56
57     def getFormat(self):
58         return 'json'
59
60     def getInputFormat(self):
61         return self.getFormat()
62
63     def handleResponse(self, response):
64         s = response.read()
65         obj = osrfJSONToObject(s)
66         if obj['status'] != 200:
67             sys.stderr.write('JSON gateway returned status %d:\n%s\n' % (obj['status'], s))
68             return None
69
70         # the gateway wraps responses in an array to handle streaming data
71         # if there is only one item in the array, it (probably) wasn't a streaming request
72         p = obj['payload']
73         if len(p) > 1: return p
74         return p[0]
75
76     def encodeParam(self, param):
77         return osrfObjectToJSON(param)
78
79 class XMLGatewayRequest(GatewayRequest):
80
81     def __init__(self, service, method, *params):
82         GatewayRequest.__init__(self, service, method, list(params))
83
84     def getFormat(self):
85         return 'xml'
86
87     def getInputFormat(self):
88         return self.getFormat()
89
90     def handleResponse(self, response):
91         handler = XMLGatewayParser()
92         parser = make_parser()
93         parser.setContentHandler(handler)
94         parser.parse(response)
95         return handler.getResult()
96
97     def encodeParam(self, param):
98         return osrfObjectToXML(param);
99
100 class XMLGatewayParser(handler.ContentHandler):
101
102     def __init__(self):
103         self.result = None
104         self.objStack = []
105         self.keyStack = []
106         self.posStack = [] # for tracking array-based hinted object indices
107
108         # true if we are parsing an element that may have character data
109         self.charsPending = 0 
110
111     def getResult(self):
112         return self.result
113
114     def __getAttr(self, attrs, name):
115         for (k, v) in attrs.items():
116             if k == name:
117                 return v
118         return None
119
120     def startElement(self, name, attrs):
121         
122         if self.charsPending:
123             # we just read a 'string' or 'number' element that resulted
124             # in no text data.  Appaned a None object
125             self.appendChild(None)
126
127         if name == 'null':
128             self.appendChild(None)
129             return
130
131         if name == 'string' or name == 'number':
132             self.charsPending = True
133             return
134
135         if name == 'element': # this is an object item wrapper
136             self.keyStack.append(self.__getAttr(attrs, 'key'))
137             return
138
139         hint = self.__getAttr(attrs, 'class_hint')
140         if hint:
141             obj = osrfNewObjectFromHint(hint)
142             self.appendChild(obj)
143             self.objStack.append(obj)
144             if name == 'array':
145                 self.posStack.append(0)
146             return
147
148         if name == 'array':
149             obj = []
150             self.appendChild(obj)
151             self.objStack.append(obj)
152             return
153
154         if name == 'object':
155             obj = {}
156             self.appendChild(obj)
157             self.objStack.append(obj)
158             return
159
160         if name == 'boolean':
161             self.appendChild((self.__getAttr(attrs, 'value') == 'true'))
162             return
163
164
165     def appendChild(self, child):
166
167         if self.result == None:
168             self.result = child
169
170         if not self.objStack: return;
171
172         parent = self.objStack[len(self.objStack)-1]
173
174         if isinstance(parent, list):
175             parent.append(child)
176         else:
177             if isinstance(parent, dict):
178                 parent[self.keyStack.pop()] = child
179             else:
180                 if isinstance(parent, osrfNetworkObject):
181                     key = None
182                     if parent.getRegistry().wireProtocol == 'array':
183                         keys = parent.getRegistry().keys
184                         i = self.posStack.pop()
185                         key = keys[i]
186                         if i+1 < len(keys):
187                             self.posStack.append(i+1)
188                     else:
189                         key = self.keyStack.pop()
190
191                     parent.setField(key, child)
192
193     def endElement(self, name):
194         if name == 'array' or name == 'object':
195             self.objStack.pop()
196
197     def characters(self, chars):
198         self.charsPending = False
199         self.appendChild(urllib.unquote_plus(chars))
200
201
202
203     
204