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