def doSubscribeRequest(self, fromrouter, interval, evtype, source): """ Helper function to register an event forwarding subscription for the designated event type and source with the routers from which such events may be received. TODO: logic to keep maximum subscription interval """ ### Trace("%s doSubscribeRequest %us, (%s,%s) from %s"%(self.getUri(),interval,evtype,source,fromrouter), "EventLib.EventRouter") if interval != 0: # subscribing fwds = self._fwdtable.find(evtype, source) if fromrouter not in self._fwdtable.find(evtype, source): # new subscription self._fwdtable.insert(evtype, source, fromrouter) Trace("%s _fwdtable inserted: (%s,%s) -> %s"%(self.getUri(),evtype,source,str(fromrouter)), context="EventLib.EventRouter") self._fwdcount += 1 else: # unsubscribing removed = self._fwdtable.remove(evtype, source, fromrouter) Trace("%s _fwdtable removed: %s"%(self.getUri(),map(str,removed)), context="EventLib.EventRouter") if removed: self._fwdcount -= len(removed) Trace("self._fwdcount: %u"%(self._subcount), context="EventLib.EventRouter") return
def deliver(self, event): """ Local delivery of an event. Returns a Deferred object with the final status of the deliver operation. """ Trace("%s deliver (%s,%s,%s)"% (self.getUri(), event.getType(), event.getSource(), event.getPayload()), context="EventLib.EventPubSub") (evtyp, evsrc) = getEventTypeSource(event) if isSubscribeEvent(evtyp): for (e,t,handler) in self._sub.iterateWild(evtyp, evsrc): # Deliver to watchers only (no wildcard event type) if e == URI.EventSubscribeType: Trace("%s - to watcher %s"%(self.getUri(), handler.getUri()), context="EventLib.EventPubSub") sts = handler.handleEvent(event) if sts.syncValue() != StatusVal.OK: return sts else: for handler in self._sub.iterate(evtyp, evsrc): Trace("%s - to handler %s (%s,%s)"%(self.getUri(), handler.getUri(), event.getType(), event.getSource()), context="EventLib.EventPubSub") sts = handler.handleEvent(event) if sts.syncValue() != StatusVal.OK: return sts return makeDeferred(StatusVal.OK)
def receive(self, fromrouter, envelope): """ Receive an event from an external event router. The event received is wrapped in a forwarding envelope, which contains additional information about the event delivery path that is used, possibly among other things, to detect event forwarding loops. Returns a Deferred object with the final status of the receive operation. """ sts = makeDeferred(StatusVal.OK) event = envelope.unWrap(self.getUri()) # unWrap handles loop-detection if event: Trace("%s receive %s from %s"%(self.getUri(),str(event), fromrouter), "EventLib.EventRouter") self.deliver(event) sub = openSubscribeEvent(isSubscribeEvent, event) Trace("%s openSubscribeEvent %s"%(self.getUri(),sub), "EventLib.EventRouter") if sub: # New subscription request self.doSubscribeRequest(fromrouter, sub[2], sub[3], sub[4]) newenv = envelope.nextHop(self.getUri()) sts = self.forward(event, newenv) if sub and sub[2] != 0 and sts.syncValue() != StatusVal.OK: # Undo subscription self.doSubscribeRequest(fromrouter, 0, sub[3], sub[4]) return sts
def watch(self, interval, handler, evtype=None): """ Request to receive notice of subscribe/unsubscribe events for a given event type. (Currently there is no option to also select on subscriber.) Returns a Deferred status value is returned indicating the outcome of the subscribe operation - StatusVal.SUBSCRIBED indicates a successful outcome. HACK: for subscription notification events, the subscribed event type is treated as the source for subscription. This avoids having to keep a separate table for watchers. """ Trace("%s watch %us, handler %s to %s"%(self.getUri(), interval, str(handler), evtype), context="EventLib.EventPubSub") sts = self.subscribe(interval, handler, evtype=URI.EventSubscribeType, source=evtype) if sts.syncValue() == StatusVal.SUBSCRIBED: # Scan existing subscriptions for immediate watch events Trace("- scanning subscriptions", context="EventLib.EventPubSub") event = makeEvent(evtype=URI.EventSubscribeType, source=evtype) for (t,s,h) in self._sub.iterateWild(evtype, None): Trace("- subscribed (%s,%s,%s)"%(t,s,h), context="EventLib.EventPubSub") handler.handleEvent(event) pass return sts
def testBlockingEventDelivery(self): es = makeEventAgent(uri="es") eh = makeEventHandler(uri="eh", handler=eventHandlerBlocking, initSubscription=subHandler, endSubscription=unsubHandler) eh.subcount = 0 eh.evcount = 0 evtyp = "R3Events1/ev1" # Select event routed R3 -> R1 -> R2 .. evsrc = "R3Source1/src1" # .. (see setUp) ev = makeEvent(evtype=evtyp, source=evsrc) sts = self.R2.subscribe(60, eh, evtype=evtyp, source=evsrc) Trace("Subscribe returned", "EventLib.TestEventRouterThreaded") time.sleep(0.1) # Allow subscribe time to propagate Trace("Subscribed", "EventLib.TestEventRouterThreaded") self.assertEqual(eh.evcount, 0) sts = self.R3.publish(es, ev) self.assertEqual(eh.evcount, 0) # Not delivered immediately ... Trace("Before sleep", "EventLib.TestEventRouterThreaded") time.sleep(1.5) # Note: evcent handler blocks for 1 sec Trace("After sleep: eh.evcount %d" % (eh.evcount), "EventLib.TestEventRouterThreaded") self.assertEqual(eh.evcount, 1) # ... but sometime after a second self.assertEqual(eh.event.getType(), evtyp) self.assertEqual(eh.event.getSource(), evsrc) Trace("testBlockingEventDelivery OK", "EventLib.TestEventRouterThreaded") return
def subscribe(self, interval, handler, evtype=None, source=None): """ Subscribe an event handler to an event/source combination. 'interval' is a time interval, in seconds, for which the subscription is to be maintained - if zero, no subscription is created, and any existing subscription is cancelled. Returns a Deferred status value is returned indicating the outcome of the operation. """ Trace("%s subscribe %us, handler %s to (%s,%s)"% (self.getUri(), interval, str(handler), evtype, source), context="EventLib.EventPubSub") sts = self.unsubscribe(handler, evtype, source) if interval != 0: self._sub.insert(evtype, source, handler) self._subcount += 1 handler.initSubscription(StatusVal.SUBSCRIBED) # Publish subscribe request and notify events sts = self.publishSubscription(handler, interval, evtype, source) if sts.syncValue() != StatusVal.OK: Trace("publish subscription returns %s"%(str(sts.syncValue())), context="EventLib.EventPubSub") self.unsubscribe(handler, evtype, source) else: sts = makeDeferred(StatusVal.SUBSCRIBED) return sts
def eventHandlerBlocking(h, e): Trace("%s eventHandlerBlocking %s" % (h, str(e)), "EventLib.TestEventRouterThreaded") time.sleep(1.0) h.evcount += 1 h.event = e Trace("%s eventHandlerBlocking - return" % (h), "EventLib.TestEventRouterThreaded") return makeDeferred(StatusVal.OK)
def getQueuedItem(self): """ Wait for an item to be queued, then return it. """ Trace("%s getQueuedItem ..." % (self.getUri()), context="EventLib.EventRelayHTTPC") item = self._queue.get() Trace("%s getQueuedItem (%s)" % (self.getUri(), item), context="EventLib.EventRelayHTTPC") self._event.set() return item
def tearDown(self): Trace("tearDown","TestEventPubSub") if self.a: self.router.unsubscribe(self.a, evtype="t1", source="s1") if self.b: self.router.unsubscribe(self.b, evtype="t2", source="s1") if self.c: self.router.unsubscribe(self.c, evtype="t1", source="s2") if self.d: self.router.unsubscribe(self.d, evtype="t2", source="s2") if self.e: self.router.unsubscribe(self.e, evtype="t1") if self.f: self.router.unsubscribe(self.f, source="s1") if self.g: self.router.unsubscribe(self.g) if self.h: self.router.unsubscribe(self.h, evtype="t2", source="s1") Trace("tearDown exit","TestEventPubSub") return
def close(self): """ Shut down the event router thread """ Trace("%s close" % (self.getUri()), "EventLib.EventRelayHTTPC") self._httpcon.close() self._closing = True self._event.set() self._queue.put(["closedown", []]) self._thread.join() Trace("%s closed" % (self.getUri()), "EventLib.EventRelayHTTPC") return
def close(self): """ Shut down the event router thread """ # assert False,"EventRelayHTTPS .close called" Trace("%s close" % (self.getUri()), "EventLib.EventRelayHTTPS") self._closing = True self._event.set() self._queue.put(["closedown", []]) self._server.stop() Trace("%s stopped" % (self.getUri()), "EventLib.EventRelayHTTPS") self._thread.join() Trace("%s closed" % (self.getUri()), "EventLib.EventRelayHTTPS") return
def getQueuedItem(self): """ Wait for an item to be queued, then return it. """ try: item = self._queue.get(timeout=QUEUE_WAIT_TIMEOUT) except Empty: Trace("%s getQueuedItem: timeout" % (self.getUri()), "EventLib.EventRelayHTTPS") item = ["idle", []] Trace("%s getQueuedItem: item %s" % (self.getUri(), str(item)), "EventLib.EventRelayHTTPS") self._event.set() return item
def testWatch8(self): Trace("testWatch8","TestEventPubSub") self.setUpRouter() h1 = MyEventHandler("H1") w1 = MyEventHandler("W1") self.router.watch(5, w1, evtype="t1") self.router.subscribe(60, h1, evtype="t1", source="s1") assert w1.isWatched(),"Watch for t1 not triggered on subscribe" ev = w1.getSubEvent() Trace("testWatch8 - ev %s:%s"%(str(ev),repr(ev.getPayload())),"TestEventPubSub") assert ev.getType() == URI.EventSubscribeType, "Subscribe event type must be URI.EventSubscribeType" assert ev.getSource() == h1.getUri(), "Subscribe event agent not handler URI" assert ev.getPayload()[0] == 60, "Subscribe payload[0] not interval" assert ev.getPayload()[1] == "t1", "Subscribe payload[1] not subscribed event type" assert ev.getPayload()[2] == "s1", "Subscribe payload[2] not subscribed event source" return
def testEventTypeSourceDict2(self): Trace("testEventTypeSourceDict2","TestEventPubSub") # Construct a test dinctionary ed = EventTypeSourceDict() tsvs = [ ("bt","bs","B1"), ("bt","bs","B2") , ("at","as","A1"), ("at","as","A2") , (None,None,"N1"), (None,None,"N2") ] for (t,s,v) in tsvs: ed.insert(t, s, v) self.assertEqual(list(ed.iterateAll()), tsvs) self.assertEqual(ed.count(), 6) # Test wildcard scanning operations self.assertEqual(list(ed.iterateWild(None,None)), tsvs) self.assertEqual(list(ed.iterateWild("at","as")), [("at","as","A1"), ("at","as","A2"), (None,None,"N1"), (None,None,"N2")]) self.assertEqual(list(ed.iterateWild("at",None)), [("at","as","A1"), ("at","as","A2"), (None,None,"N1"), (None,None,"N2")]) self.assertEqual(list(ed.iterateWild(None,"as")), [("at","as","A1"), ("at","as","A2"), (None,None,"N1"), (None,None,"N2")]) self.assertEqual(list(ed.iterateWild("bt","bs")), [("bt","bs","B1"), ("bt","bs","B2"), (None,None,"N1"), (None,None,"N2")]) self.assertEqual(list(ed.iterateWild("bt",None)), [("bt","bs","B1"), ("bt","bs","B2"), (None,None,"N1"), (None,None,"N2")]) self.assertEqual(list(ed.iterateWild(None,"bs")), [("bt","bs","B1"), ("bt","bs","B2"), (None,None,"N1"), (None,None,"N2")]) self.assertEqual(list(ed.iterateWild("ct","cs")), [(None,None,"N1"), (None,None,"N2")]) self.assertEqual(list(ed.iterateWild("ct",None)), [(None,None,"N1"), (None,None,"N2")]) self.assertEqual(list(ed.iterateWild(None,"cs")), [(None,None,"N1"), (None,None,"N2")]) return
def eventHandlerQueueing(h, e): Trace("%s eventHandlerQueueing %s" % (h, str(e)), "EventLib.TestEventRouterThreaded") h.evcount += 1 h.event = e h.queue.append(e) return makeDeferred(StatusVal.OK)
def testWatchPrior1(self): Trace("testWatchPrior1","TestEventPubSub") self.setUpRouter() h1 = MyEventHandler("H1") w1 = MyEventHandler("W1") self.router.subscribe(60, h1, evtype="t1", source="s1") self.router.watch(5,w1,evtype="t1") assert w1.isWatched(),"Watch t1 not triggered for subscribe that precedes watch"
def forward(self, event, env): """ Internal function to process event received from HTTP connection: add new hop to envelope and pass it straight on to the associated router object. """ Trace("%s forward %s" % (self.getUri(), event), "EventLib.EventRelayHTTPC") return self._router.receive(self, env.nextHop(self.getUri()))
def testUnsubscribe2(self): Trace("testUnsubscribe2","TestEventPubSub") self.setUpRouter() self.router.unsubscribe(self.handlers[4], evtype="t1") self.router.unsubscribe(self.handlers[5], source="s1") # A B C D E F G H expectTriggered = (True, False, False, False, False, False, True, False) self.doTestPublish("t1","s1",expectTriggered)
def stop(self): """ Request server to stop. """ Trace("%s stop" % (self._relay.getUri()), "EventLib.EventRelayHTTPS") self._run = False time.sleep(self._timeout + 1) self.socket.close()
def tearDown(self): Trace("TearDown", "TestEventRouterHTTP") if self.R1: self.R1.close() if self.R2: self.R2.close() if self.R3: self.R3.close() if self.R2C: self.R2C.close() if self.R3C: self.R3C.close() return
def endWatch(self, handler, evtype=None): """ Terminate request to receive notice of subscribe/unsubscribe events for a given event type. """ Trace("%s endWatch %s from %s"%(self.getUri(), evtype, str(handler)), context="EventLib.EventPubSub") return self.unsubscribe(handler, evtype=URI.EventSubscribeType, source=evtype)
def testWatchPrior4(self): Trace("testWatchPrior4","TestEventPubSub") self.setUpRouter() h1 = MyEventHandler("H1") w1 = MyEventHandler("W1") self.router.subscribe(60, h1) self.router.watch(5, w1) assert w1.isWatched(),"Watch * not triggered for prior wildcard subscribe"
def syncValue(self): """ Wait for value to be available, then return it """ Trace("QueueDeferred.syncValue (%s)"%(str(self._value)), context="EventLib.QueueDeferred") self._event.clear() if not self._queue.empty(): self._event.wait(timeout=20) # Timeout is precaution to prevent total lockout return self._value
def close_request(self, request): """ Override close_request to deal with None request at closedown """ Trace("%s close_request %s" % (self._relay.getUri(), str(request)), "EventLib.EventRelayHTTPS") if request: # super(EventRequestServer, self).close_request(request) HTTPServer.close_request(self, request)
def __init__(self, uri=None): """ Initialize a new EventPubSub object """ if not uri: uri = "(EventPubSub %u)"%(id(self)) Trace("%s Init"%(uri), context="EventLib.EventPubSub") super(EventPubSub, self).__init__(uri) # Subscription table for local delivery of events self._sub = EventTypeSourceDict() self._subcount = 0 return
def queueItem(self, item): """ Add item to the queue, and return a deferred object that fires when an item is removed (or the queue is empty). """ Trace("%s queueItem (%s)" % (self.getUri(), item), "EventLib.EventRelayHTTPC") if not self._closing: self._queue.put(item) return makeQueueDeferred(StatusVal.OK, self._queue, self._event) return makeDeferred(StatusVal.OK)
def testWatch6(self): Trace("testWatch6","TestEventPubSub") self.setUpRouter() h1 = MyEventHandler("H1") w1 = MyEventHandler("W1") self.router.watch(5, w1) w1.resetWatched() self.router.subscribe(60, h1, evtype="t1", source="s1") assert w1.isWatched(),"Watch * not triggered on subscribe" w1.resetWatched() self.router.unsubscribe(h1, evtype="t1", source="s1") assert w1.isWatched(),"Watch * not triggered on unsubscribe"
def testWatch7(self): Trace("testWatch7","TestEventPubSub") self.setUpRouter() h1 = MyEventHandler("H1") w1 = MyEventHandler("W1") self.router.watch(5, w1) w1.resetWatched() self.router.subscribe(60, h1) assert w1.isWatched(),"Watch * not triggered on wild subscribe" w1.resetWatched() self.router.unsubscribe(h1) assert w1.isWatched(),"Watch * not triggered on wild unsubscribe"
def testWatch2(self): Trace("testWatch2","TestEventPubSub") self.setUpRouter() w1 = MyEventHandler("W1") w2 = MyEventHandler("W2") self.router.watch(5, w1, evtype="t1") self.router.watch(5, w2, evtype="t2") w1.resetWatched() w2.resetWatched() self.router.subscribe(60, self.handlers[0], evtype="t2") assert not w1.isWatched(), "Watch t1 triggered on subscribe" assert w2.isWatched(), "Watch t2 not triggered on subscribe"
def setUpRouter(self): Trace("setUp","TestEventPubSub") a = MyEventHandler("A") b = MyEventHandler("B") c = MyEventHandler("C") d = MyEventHandler("D") e = MyEventHandler("E") f = MyEventHandler("F") g = MyEventHandler("G") h = MyEventHandler("H") self.handlers = (a, b, c, d, e, f, g, h) self.router = EventPubSub(uri="LocalRouter") self.router.subscribe(60, a, evtype="t1", source="s1") self.router.subscribe(60, b, evtype="t2", source="s1") self.router.subscribe(60, c, evtype="t1", source="s2") self.router.subscribe(60, d, evtype="t2", source="s2") self.router.subscribe(60, e, evtype="t1") self.router.subscribe(60, f, evtype="", source="s1") self.router.subscribe(60, g, source="") self.router.subscribe(60, h, evtype="t2", source="s1") # same as b Trace("setUp exit","TestEventPubSub") return