class Messenger(object): """ The basic messenger interface for clients. It provides the run loop for async transports and their scheduling as well as routing behaviour if there are multiple transports. THe user can join/leave groups as well as receive and send messages. This is the interface used by the caller. Its passes most items as objects in the TxQueue as most operations take place in another thread. """ def __init__(self, name): """ Creating messaging interface here """ self.name = name self.rxqueue = Queue.Queue() self.txqueue = Queue.Queue() self.thread = None self.groupMembership = defaultdict(set) self.startDaemon() def startDaemon(self): """ Start the thread that takes care of polling and scheduling for all the transports """ from worker import WorkerThread self.thread = WorkerThread(self.name, self.txqueue, self.rxqueue) self.thread.start() def addTransport(self, transport, keepConnected=False): """ Add a new transport to this messages. This can be compared to adding a new ethernet interface on a simple node. The messenger will take care of routing if there are multiple transports. Examples: TCPServer('0.0.0.0', 17708) TCPTransport('192.168.1.50', 17708) MulticastTransport('192.168.1.40', '255.239.13.4') """ self.txqueue.put(TransportRequest(transport, keepConnected)) def join(self, group, caller = "default"): """ Join the group 'group' (a string value). Messages sent to this group by other nodes will now be received by the owner of this messaging object The call option allows one to attach an 'ID' to the join request so that someone else joining and leaving the same group doesn't cause the official leave to actually occur. """ self.txqueue.put(GroupRequest("join", group, caller)) self.groupMembership[group].add(caller) def leave(self, group, caller = "default"): """ Leave the group 'group' (a string value). This messaging object will no longer receive messages for this group. If the caller joined with a caller value, it must use the same value for the leave request """ self.txqueue.put(GroupRequest("leave", group, caller)) self.groupMembership[group].discard(caller) if not self.groupMembership[group]: del self.groupMembership[group] def nextMessage(self, block=False, timeout=None): """ Called to remove a received message from the queue. block and timeout are as specified in Queue.Queue.get() """ return self.rxqueue.get(block, timeout) def send(self, msg, **kwargs): """ Enqueue a message for transmittal, kwargs is a list of delivery request values to specify desired delivery behavior """ self.txqueue.put(TransmitRequest(msg, kwargs)) def trigger(self, **kwargs): """ Send a trigger event. Single location for a common action, ick though, this is application level stuff in messaging code """ self.send(MAGIMessage(groups="control", docks="daemon", data=yaml.dump(kwargs), contenttype=MAGIMessage.YAML)) def poisinPill(self): """ Puts a "PoisinPill" string into the rxqueue so that the first listener on nextMessage will wake up and get the message. """ self.rxqueue.put("PoisinPill") def stop(self): self.thread.stop() # stopping the worker thread
class Messenger(object): """ The basic messenger interface for clients. It provides the run loop for async transports and their scheduling as well as routing behaviour if there are multiple transports. THe user can join/leave groups as well as receive and send messages. This is the interface used by the caller. Its passes most items as objects in the TxQueue as most operations take place in another thread. """ def __init__(self, name): """ Creating messaging interface here """ self.name = name self.rxqueue = Queue.Queue() self.txqueue = Queue.Queue() self.thread = None self.groupMembership = defaultdict(set) self.startDaemon() def startDaemon(self): """ Start the thread that takes care of polling and scheduling for all the transports """ from worker import WorkerThread self.thread = WorkerThread(self.name, self.txqueue, self.rxqueue) self.thread.start() def addTransport(self, transport, keepConnected=False): """ Add a new transport to this messages. This can be compared to adding a new ethernet interface on a simple node. The messenger will take care of routing if there are multiple transports. Examples: TCPServer('0.0.0.0', 17708) TCPTransport('192.168.1.50', 17708) MulticastTransport('192.168.1.40', '255.239.13.4') """ self.txqueue.put(TransportRequest(transport, keepConnected)) def join(self, group, caller="default"): """ Join the group 'group' (a string value). Messages sent to this group by other nodes will now be received by the owner of this messaging object The call option allows one to attach an 'ID' to the join request so that someone else joining and leaving the same group doesn't cause the official leave to actually occur. """ self.txqueue.put(GroupRequest("join", group, caller)) self.groupMembership[group].add(caller) def leave(self, group, caller="default"): """ Leave the group 'group' (a string value). This messaging object will no longer receive messages for this group. If the caller joined with a caller value, it must use the same value for the leave request """ self.txqueue.put(GroupRequest("leave", group, caller)) self.groupMembership[group].discard(caller) if not self.groupMembership[group]: del self.groupMembership[group] def nextMessage(self, block=False, timeout=None): """ Called to remove a received message from the queue. block and timeout are as specified in Queue.Queue.get() """ return self.rxqueue.get(block, timeout) def send(self, msg, **kwargs): """ Enqueue a message for transmittal, kwargs is a list of delivery request values to specify desired delivery behavior """ self.txqueue.put(TransmitRequest(msg, kwargs)) def trigger(self, **kwargs): """ Send a trigger event. Single location for a common action, ick though, this is application level stuff in messaging code """ self.send( MAGIMessage(groups="control", docks="daemon", data=yaml.dump(kwargs), contenttype=MAGIMessage.YAML)) def poisinPill(self): """ Puts a "PoisinPill" string into the rxqueue so that the first listener on nextMessage will wake up and get the message. """ self.rxqueue.put("PoisinPill") def stop(self): self.thread.stop() # stopping the worker thread