checkpoint

This commit is contained in:
Kenneth Giusti 2013-12-23 10:35:17 -05:00
parent ac6d1c7fd3
commit 623b1af3ab
5 changed files with 314 additions and 156 deletions

4
.gitignore vendored

@ -34,3 +34,7 @@ nosetests.xml
.mr.developer.cfg
.project
.pydevproject
# emacs crap
*~

@ -36,44 +36,196 @@ to a server, and waits for a response. The method call is a map of the form:
"""
class MyConnectionEventHandler(fusion_connection.ConnectionEventHandler):
class MyConnection(fusion_connection.ConnectionEventHandler):
def __init__(self, name, socket, container, properties):
self.name = name
self.socket = socket
self.connection = container.create_connection(name, self,
properties)
self.connection.user_context = self
def process(self):
""" Do connection-based processing (I/O and timers) """
readfd = []
writefd = []
if self.connection.needs_input > 0:
readfd = [self.socket]
if self.connection.has_output > 0:
writefd = [self.socket]
timeout = None
deadline = self.connection.next_tick
if deadline:
now = time.time()
timeout = 0 if deadline <= now else deadline - now
print("select start (t=%s)" % str(timeout))
readable,writable,ignore = select.select(readfd,writefd,[],timeout)
print("select return")
if readable:
count = self.connection.needs_input
if count > 0:
try:
sock_data = self.socket.recv(count)
if sock_data:
self.connection.process_input( sock_data )
else:
# closed?
self.connection.close_input()
except socket.timeout, e:
raise # I don't expect this
except socket.error, e:
err = e.args[0]
# ignore non-fatal errors
if (err != errno.EAGAIN and
err != errno.EWOULDBLOCK and
err != errno.EINTR):
# otherwise, unrecoverable:
self.connection.close_input()
raise
except: # beats me...
self.connection.close_input()
raise
if writable:
data = self.connection.output_data()
if data:
try:
rc = self.socket.send(data)
if rc > 0:
self.connection.output_written(rc)
else:
# else socket closed
self.connection.close_output()
except socket.timeout, e:
raise # I don't expect this
except socket.error, e:
err = e.args[0]
# ignore non-fatal errors
if (err != errno.EAGAIN and
err != errno.EWOULDBLOCK and
err != errno.EINTR):
# otherwise, unrecoverable
self.connection.close_output()
raise
except: # beats me...
self.connection.close_output()
raise
self.connection.process(time.time())
def destroy(self, error=None):
self.connection.user_context = None
self.connection.destroy()
self.connection = None
self.socket.close()
self.socket = None
# Connection callbacks:
def connection_active(self, connection):
"""connection handshake completed"""
print "APP: CONN ACTIVE"
def connection_closed(self, connection, reason):
print "APP: CONN CLOSED"
def sender_requested(self, connection, link_handle,
requested_source, properties={}):
# call accept_sender to accept new link,
# reject_sender to reject it.
assert False, "Not expected"
def receiver_requested(self, connection, link_handle,
requested_target, properties={}):
# call accept_sender to accept new link,
# reject_sender to reject it.
assert False, "Not expected"
def sasl_done(self, connection, result):
print "APP: SASL DONE"
print result
def connection_closed(self, connection, reason):
print "APP: CONN CLOSED"
def link_pending(self, connection, link):
print "APP: LINK PENDING"
return True
class MySenderEventHandler(fusion_link.SenderEventHandler):
class MyCaller(fusion_link.SenderEventHandler,
fusion_link.ReceiverEventHandler):
"""
"""
def __init__(self, method_map, my_connection,
my_source, my_target,
receiver_properties, sender_properties):
conn = my_connection.connection
self._sender = conn.create_sender(my_source, target_address=None,
eventHandler=self, name=my_source,
properties=sender_properties)
self._receiver = conn.create_receiver(my_target, source_address=None,
eventHandler=self, name=my_target,
properties=receiver_properties)
self._method = method_map
self._reply_to = None
self._to = None
self._receiver_closed = False
self._sender_closed = False
def done(self):
return self._receiver_closed and self._sender_closed
def _send_request(self):
"""Send a message containing the RPC method call
"""
msg = Message()
msg.subject = "An RPC call!"
msg.address = self._to
msg.reply_to = self._reply_to
msg.body = self._method
msg.correlation_id = 5 # whatever...
self._sender.send(msg, self, None, time.time() + 10)
# SenderEventHandler callbacks:
def sender_active(self, sender_link):
print "APP: SENDER ACTIVE"
assert sender_link is self._sender
self._to = sender_link.target_address
assert self._to, "Expected a target address!!!"
if self._reply_to:
self._send_request()
def sender_closed(self, sender_link, error):
print "APP: SENDER CLOSED"
self._sender_closed = True
# send complete callback:
class MyReceiverEventHandler(fusion_link.ReceiverEventHandler):
def __call__(self, sender_link, handle, status, error):
print "APP: MESSAGE SENT CALLBACK %s" % status
# ReceiverEventHandler callbacks:
def receiver_active(self, receiver_link):
print "APP: RECEIVER ACTIVE"
assert receiver_link is self._receiver
self._reply_to = receiver_link.source_address
assert self._reply_to, "Expected a source address!!!"
if self._to:
self._send_request()
def receiver_closed(self, receiver_link, error):
print "APP: RECEIVER CLOSED"
self._receiver_closed = True
def message_received(self, receiver_link, message, handle):
print "APP: MESSAGE RECEIVED"
print "Response received: %s" % str(message)
def send_callback(sender, handle, status, error=None):
print "APP: MESSAGE SENT CALLBACK"
self._sender.destroy()
del self._sender
self._receiver.destroy()
del self._receiver
def main(argv=None):
@ -90,15 +242,7 @@ def main(argv=None):
if not method_info:
assert False, "No method info specified!"
if len(method_info) % 2 != 1:
assert False, "An even number of method arguments is required!"
method = {'method': method_info[0],
'args': dict([(method_info[i], method_info[i+1])
for i in range(1, len(method_info), 2)])}
print "method=%s" % str(method)
assert False, "An even number of method arguments are required!"
# Create a socket connection to the server
#
@ -123,109 +267,26 @@ def main(argv=None):
# create AMQP container, connection, sender and receiver
#
conn_handler = MyConnectionEventHandler()
send_handler = MySenderEventHandler()
recv_handler = MyReceiverEventHandler()
container = fusion_container.Container(uuid.uuid4().hex)
connection = container.create_connection("server",
conn_handler)
my_connection = MyConnection( "to-server", my_socket,
container, {})
# @todo: need better sasl + server
connection.sasl.mechanisms("ANONYMOUS")
connection.sasl.client()
my_connection.connection.sasl.mechanisms("ANONYMOUS")
my_connection.connection.sasl.client()
sender = connection.create_sender( "rpc-client-src",
"rpc-server-tgt",
send_handler )
# Create the RPC caller
method = {'method': method_info[0],
'args': dict([(method_info[i], method_info[i+1])
for i in range(1, len(method_info), 2)])}
my_caller = MyCaller( method,
my_connection,
"my-source-address",
"my-target-address",
receiver_properties = {"capacity": 1})
receiver = connection.create_receiver( "rpc-client-tgt",
"rpc-server-src",
recv_handler )
# send a message containing the RPC method call
#
msg = Message()
msg.address = "rpc-server-address"
msg.subject = "An RPC call!"
msg.reply_to = "rpc-client-tgt"
msg.body = method
sender.send( msg, send_callback, "my-handle", opts.timeout )
while not connection.closed:
#
# Poll for I/O & timers
#
readfd = []
writefd = []
if connection.needs_input > 0:
readfd = [my_socket]
if connection.has_output > 0:
writefd = [my_socket]
timeout = None
deadline = connection.next_tick
if deadline:
now = time.time()
timeout = 0 if deadline <= now else deadline - now
print("select start (t=%s)" % str(timeout))
readable,writable,ignore = select.select(readfd,writefd,[],timeout)
print("select return")
if readable:
count = connection.needs_input
if count > 0:
try:
sock_data = my_socket.recv(count)
if sock_data:
connection.process_input( sock_data )
else:
# closed?
connection.close_input()
except socket.timeout, e:
raise # I don't expect this
except socket.error, e:
err = e.args[0]
# ignore non-fatal errors
if (err != errno.EAGAIN and
err != errno.EWOULDBLOCK and
err != errno.EINTR):
# otherwise, unrecoverable:
connection.close_input()
raise
except: # beats me...
connection.close_input()
raise
if writable:
data = connection.output_data()
if data:
try:
rc = my_socket.send(data)
if rc > 0:
connection.output_written(rc)
else:
# else socket closed
connection.close_output()
except socket.timeout, e:
raise # I don't expect this
except socket.error, e:
err = e.args[0]
# ignore non-fatal errors
if (err != errno.EAGAIN and
err != errno.EWOULDBLOCK and
err != errno.EINTR):
# otherwise, unrecoverable
connection.close_output()
raise
except: # beats me...
connection.close_output()
raise
connection.process(time.time())
while not my_caller.done():
my_connection.process()
print "DONE"

@ -39,6 +39,11 @@ map sent in the request.
"""
# Maps of outgoing and incoming links
sender_links = {} # indexed by Source address
receiver_links = {} # indexed by Target address
class SocketConnection(fusion_connection.ConnectionEventHandler):
"""Associates a fusion Connection with a python network socket"""
@ -61,9 +66,43 @@ class SocketConnection(fusion_connection.ConnectionEventHandler):
def connection_closed(self, connection, reason):
print "APP: CONN CLOSED"
def link_pending(self, connection, link):
print "APP: LINK PENDING"
return True
def sender_requested(self, connection, link_handle,
requested_source, properties):
print "APP: SENDER LINK REQUESTED"
global sender_links
name = uuid.uuid4().hex
# allow for requested_source address if it doesn't conflict with an
# existing address
if not requested_source or requested_source in sender_links:
requested_source = "/%s/%s" % (connection.container.name,
name)
assert requested_source not in sender_links
sender = MySenderLink(connection, link_handle, requested_source,
name, {})
sender_links[requested_source] = sender
print "APP: NEW SENDER LINK CREATED, source=%s" % requested_source
def receiver_requested(self, connection, link_handle,
requested_target, properties):
print "APP: RECEIVER LINK REQUESTED"
# allow for requested_source address if it doesn't conflict with an
# existing address
global receiver_links
name = uuid.uuid4().hex
if not requested_target or requested_target in receiver_links:
requested_target = "/%s/%s" % (connection.container.name,
name)
assert requested_target not in receiver_links
receiver = MyReceiverLink(connection, link_handle,
requested_target, name,
{"capacity": 3})
receiver_links[requested_target] = receiver
print "APP: NEW RECEIVER LINK CREATED, target=%s" % requested_target
def sasl_done(self, connection, result):
print "APP: SASL DONE"
@ -74,30 +113,38 @@ class MySenderLink(fusion_link.SenderEventHandler):
"""
"""
def __init__(self, connection, link_handle, source_address,
name):
name, properties):
self.sender_link = connection.accept_sender(link_handle,
source_address,
self,
name)
name,
properties)
# SenderEventHandler callbacks:
def sender_active(self, sender_link):
print "APP: SENDER ACTIVE"
def sender_closed(self, sender_link, error):
print "APP: SENDER CLOSED"
# 'message sent' callback:
def __call__(self, sender, handle, status, error=None):
print "APP: MESSAGE SENT CALLBACK %s" % status
class MyReceiverLink(fusion_link.ReceiverEventHandler):
"""
"""
def __init__(self, connection, link_handle, target_address,
name):
self.receiver_link = connection.accept_receiver(link_handle,
target_address,
self,
name)
name, properties):
self._link = connection.accept_receiver(link_handle,
target_address,
self,
name,
properties)
# ReceiverEventHandler callbacks:
def receiver_active(self, receiver_link):
@ -109,9 +156,33 @@ class MyReceiverLink(fusion_link.ReceiverEventHandler):
def message_received(self, receiver_link, message, handle):
print "APP: MESSAGE RECEIVED"
global sender_links
def send_callback(sender, handle, status, error=None):
print "APP: MESSAGE SENT CALLBACK"
# extract to reply-to, correlation id
reply_to = message.reply_to
if not reply_to or reply_to not in sender_links:
self._link.message_rejected(handle, "Bad reply-to address")
else:
my_sender = sender_links[reply_to]
correlation_id = message.correlation_id
method_map = message.body
if (not isinstance(method_map, dict) or
'method' not in method_map):
self._link.message_rejected(handle, "Bad format")
else:
print "Echoing back message map"
response = Message()
response.address = reply_to
response.subject = message.subject
response.correlation_id = correlation_id
response.body = {"response": method_map}
link = my_sender.sender_link
link.send( response, my_sender,
message, time.time() + 5.0)
if self._link.capacity == 0:
self._link.add_capacity( 5 )
def main(argv=None):
@ -125,7 +196,7 @@ def main(argv=None):
opts, arguments = parser.parse_args(args=argv)
# Create a socket connection to the server
# Create a socket for inbound connections
#
regex = re.compile(r"^amqp://([a-zA-Z0-9.]+)(:([\d]+))?$")
print("Listening on %s" % opts.address)
@ -162,7 +233,7 @@ def main(argv=None):
writefd = []
readers,writers,timers = container.need_processing()
# map fusion Connections to my SocketConnections
# map fusion Connections back to my SocketConnections
for c in readers:
sc = c.user_context
assert sc and isinstance(sc, SocketConnection)
@ -174,7 +245,7 @@ def main(argv=None):
timeout = None
if timers:
deadline = timers[0].next_tick
deadline = timers[0].next_tick # 0 == next expiring timer
now = time.time()
timeout = 0 if deadline <= now else deadline - now
@ -251,10 +322,6 @@ def main(argv=None):
raise
w.connection.process(time.time())
print "How to close connections that are finished???"
# Send replies
return 0

@ -34,20 +34,20 @@ class ConnectionEventHandler(object):
def connection_closed(self, connection, reason):
pass
def sender_pending(self, connection, link_handle,
requested_source, properties={}):
def sender_requested(self, connection, link_handle,
requested_source, properties={}):
# call accept_sender to accept new link,
# reject_sender to reject it.
pass
def receiver_pending(self, connection, link_handle,
requested_target, properties={}):
def receiver_requested(self, connection, link_handle,
requested_target, properties={}):
# call accept_sender to accept new link,
# reject_sender to reject it.
pass
# @todo cleaner sasl support, esp. server side
def sasl_done(self, connection, result):
# @todo sasl server support
pass
@ -91,6 +91,10 @@ class Connection(object):
self._pn_session = self._pn_connection.session()
self._pn_session.open()
@property
def container(self):
return self._container
@property
# @todo - hopefully remove
def transport(self):
@ -148,8 +152,8 @@ Associate an arbitrary user object with this Connection.
if not self._active:
if self._pn_connection.state == self._ACTIVE:
self._active = False
self._handler.connection_active(self, connection)
self._active = True
self._handler.connection_active(self)
ssn = self._pn_connection.session_head(self._NEED_INIT)
while ssn:
@ -328,7 +332,7 @@ Associate an arbitrary user object with this Connection.
def reject_sender(self, link_handle, reason):
pn_link = self._pending_links.pop(link_handle)
if link:
if pn_link:
# @todo support reason for close
pn_link.close()
@ -369,7 +373,7 @@ Associate an arbitrary user object with this Connection.
def reject_receiver(self, link_handle, reason):
pn_link = self._pending_links.pop(link_handle)
if link:
if pn_link:
# @todo support reason for close
pn_link.close()

@ -33,15 +33,17 @@ class _Link(object):
# @todo: raise jira to add 'context' to api
pn_link.context = self
if target_address:
self._pn_link.target.address = target_address
else:
if target_address is None:
assert pn_link.is_sender, "Dynamic target not allowed"
self._pn_link.target.dynamic = True
elif target_address:
self._pn_link.target.address = target_address
if source_address:
self._pn_link.source.address = source_address
else:
if source_address is None:
assert pn_link.is_receiver, "Dynamic source not allowed"
self._pn_link.source.dynamic = True
elif source_address:
self._pn_link.source.address = source_address
desired_mode = properties.get("distribution-mode")
if desired_mode:
@ -64,9 +66,29 @@ class _Link(object):
user_context = property(_get_user_context, _set_user_context,
doc="""
Associate an arbitrary applicaion object with this link.
Associate an arbitrary application object with this link.
""")
@property
def source_address(self):
"""If link is a sender, source is determined by the local value, else
use the remote
"""
if self._pn_link.is_sender:
return self._pn_link.source.address
else:
return self._pn_link.remote_source.address
@property
def target_address(self):
"""If link is a receiver, target is determined by the local value, else
use the remote
"""
if self._pn_link.is_receiver:
return self._pn_link.target.address
else:
return self._pn_link.remote_target.address
def _destroy(self):
self._user_context = None
self._pn_link.context = None