My favorites | Sign in
Project Logo
                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is Mozilla Corporation Code.
#
# The Initial Developer of the Original Code is
# Mikeal Rogers.
# Portions created by the Initial Developer are Copyright (C) 2008
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Mikeal Rogers <mikeal.rogers@gmail.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****

import asyncore
import socket
import select
import logging
import uuid
from time import sleep
from threading import Thread

try:
import json as simplejson
except:
import simplejson


logger = logging.getLogger(__name__)

class JavaScriptException(Exception): pass

class Telnet(object, asyncore.dispatcher):
def __init__(self, host, port):
self.host, self.port = host, port
asyncore.dispatcher.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.connect((host, port))
self.buffer = ''
self.logger = logger

def __del__(self):
self.close()

def handle_close(self):
self.close()

def handle_expt(self): self.close() # connection failed, shutdown

def writable(self):
return (len(self.buffer) > 0)

def handle_write(self):
sent = self.send(self.buffer)
self.buffer = self.buffer[sent:]

def send(self, b):
asyncore.dispatcher.send(self, b)

def read_all(self):
import socket
data = ''
while 1:
try:
data += self.recv(4096)
except socket.error:
return data

def handle_read(self):
self.data = self.read_all()
self.process_read(self.data)

read_callback = lambda self, data: None

decoder = simplejson.JSONDecoder()

try:
from json.encoder import encode_basestring_ascii, encode_basestring
except:
from simplejson.encoder import encode_basestring_ascii, encode_basestring

class JSObjectEncoder(simplejson.JSONEncoder):
"""Encoder that supports jsobject references by name."""

def _iterencode(self, o, markers=None):
import jsobjects
if isinstance(o, jsobjects.JSObject):
yield o._name_
elif isinstance(o, basestring):
if self.ensure_ascii:
encoder = encode_basestring_ascii
else:
encoder = encode_basestring
_encoding = self.encoding
if (_encoding is not None and isinstance(o, str)
and not (_encoding == 'utf-8')):
o = o.decode(_encoding)
yield encoder(o)
elif o is None:
yield 'null'
elif o is True:
yield 'true'
elif o is False:
yield 'false'
elif isinstance(o, (int, long)):
yield str(o)
elif isinstance(o, float):
yield getattr(simplejson.encoder, 'floatstr', simplejson.encoder._floatstr)(o, self.allow_nan)
elif isinstance(o, (list, tuple)):
for chunk in self._iterencode_list(o, markers):
yield chunk
elif isinstance(o, dict):
for chunk in self._iterencode_dict(o, markers):
yield chunk
else:
if markers is not None:
markerid = id(o)
if markerid in markers:
raise ValueError("Circular reference detected")
markers[markerid] = o
for chunk in self._iterencode_default(o, markers):
yield chunk
if markers is not None:
del markers[markerid]

encoder = JSObjectEncoder()

class Bridge(Telnet):

trashes = []
reading = False
sbuffer = ''
events_list = []

callbacks = {}

bridge_type = "bridge"

registered = False

def __init__(self, *args, **kwargs):
Telnet.__init__(self, *args, **kwargs)
self.connect(args)

def handle_connect(self):
self.register()

def run(self, _uuid, exec_string, interval=0, raise_exeption=True):
exec_string += '\r\n'
self.send(exec_string)

while _uuid not in self.callbacks.keys():
sleep(interval)

callback = self.callbacks.pop(_uuid)
if callback['result'] is False and raise_exeption is True:
raise JavaScriptException(callback['exception'])
return callback

def register(self):
_uuid = str(uuid.uuid1())
self.send('bridge.register("'+_uuid+'", "'+self.bridge_type+'")\r\n')
self.registered = True

def execFunction(self, func_name, args, interval=.25):
_uuid = str(uuid.uuid1())
exec_args = [encoder.encode(_uuid), func_name, encoder.encode(args)]
return self.run(_uuid, 'bridge.execFunction('+ ', '.join(exec_args)+')', interval)

def setAttribute(self, obj_name, name, value):
_uuid = str(uuid.uuid1())
exec_args = [encoder.encode(_uuid), obj_name, encoder.encode(name), encoder.encode(value)]
return self.run(_uuid, 'bridge.setAttribute('+', '.join(exec_args)+')')

def set(self, obj_name):
_uuid = str(uuid.uuid1())
return self.run(_uuid, 'bridge.set('+', '.join([encoder.encode(_uuid), obj_name])+')')

def describe(self, obj_name):
_uuid = str(uuid.uuid1())
return self.run(_uuid, 'bridge.describe('+', '.join([encoder.encode(_uuid), obj_name])+')')

def fire_callbacks(self, obj):
self.callbacks[obj['uuid']] = obj

def process_read(self, data):
"""Parse out json objects and fire callbacks."""
self.sbuffer += data
self.reading = True
self.parsing = True
while self.parsing:
# Remove erroneus data in front of callback object
index = self.sbuffer.find('{')
if index is not -1 and index is not 0:
self.sbuffer = self.sbuffer[index:]
# Try to get a json object from the data stream
try:
obj, index = decoder.raw_decode(self.sbuffer)
except Exception, e:
self.parsing = False
# If we got an object fire the callback infra
if self.parsing:
self.fire_callbacks(obj)
self.sbuffer = self.sbuffer[index:]

class BackChannel(Bridge):

bridge_type = "backchannel"

def __init__(self, *args, **kwargs):
super(BackChannel, self).__init__(*args, **kwargs)
self.uuid_listener_index = {}
self.event_listener_index = {}
self.global_listeners = []

def fire_callbacks(self, obj):
"""Handle all callback fireing on json objects pulled from the data stream."""
self.fire_event(**dict([(str(key), value,) for key, value in obj.items()]))

def add_listener(self, callback, uuid=None, eventType=None):
if uuid is not None:
self.uuid_listener_index.setdefault(uuid, []).append(callback)
if eventType is not None:
self.event_listener_index.setdefault(eventType, []).append(callback)

def add_global_listener(self, callback):
self.global_listeners.append(callback)

def fire_event(self, eventType=None, uuid=None, result=None, exception=None):
event = eventType
if uuid is not None and self.uuid_listener_index.has_key(uuid):
for callback in self.uuid_listener_index[uuid]:
callback(result)
if event is not None and self.event_listener_index.has_key(event):
for callback in self.event_listener_index[event]:
callback(result)
for listener in self.global_listeners:
listener(eventType, result)

thread = None

def create_network(hostname, port):

back_channel = BackChannel(hostname, port)
bridge = Bridge(hostname, port)
global thread
if not thread or not thread.isAlive():
def do():
try: asyncore.loop(use_poll=True)
except select.error:pass

thread = Thread(target=do)
getattr(thread, 'setDaemon', lambda x : None)(True)
thread.start()

return back_channel, bridge
Show details Hide details

Change log

r114 by mikeal.rogers on Jun 02, 2009   Diff
Workarounds for asyncore bugs.
Go to: 
Project members, sign in to write a code review

Older revisions

r113 by mikeal.rogers on Jun 01, 2009   Diff
Insuring that only one active asycore
loop is running at any given time.
r109 by mikeal.rogers on Mar 25, 2009   Diff
Fixing rogue import.
r102 by mikeal.rogers on Mar 13, 2009   Diff
Fixing bugs.
All revisions of this file

File info

Size: 9582 bytes, 279 lines
Hosted by Google Code