class ListeningBaseEndpoint(BaseEndpoint): """ Establishes channel type for a host of derived, listen/react endpoint factories. """ channel_type = ListenChannel def __init__(self, node=None, name=None, from_name=None, binding=None): BaseEndpoint.__init__(self, node=node) if name: log.warn( "ListeningBaseEndpoint: name param is deprecated, please use from_name instead" ) self._recv_name = from_name or name # ensure NameTrio if not isinstance(self._recv_name, NameTrio): self._recv_name = NameTrio( bootstrap.get_sys_name(), self._recv_name, binding) # if _recv_name is tuple it takes precedence self._ready_event = event.Event() self._binding = binding self._chan = None def _create_channel(self, **kwargs): """ Overrides the BaseEndpoint create channel to supply a transport if our recv name is one. """ if isinstance(self._recv_name, BaseTransport): kwargs.update({'transport': self._recv_name}) return BaseEndpoint._create_channel(self, **kwargs) def get_ready_event(self): """ Returns an async event you can .wait() on. Used to indicate when listen() is ready to start listening. """ return self._ready_event def _setup_listener(self, name, binding=None): self._chan.setup_listener(name, binding=binding) def listen(self, binding=None): """ Main driving method for ListeningBaseEndpoint. Meant to be spawned in a greenlet. This method creates/sets up a channel to listen, starts listening, and consumes messages in a loop until the Endpoint is closed. """ log.debug("LEF.listen") self.prepare_listener(binding=binding) # notify any listeners of our readiness self._ready_event.set() while True: log.debug("LEF: %s blocking, waiting for a message", self._recv_name) try: self.get_one_msg() except ChannelClosedError as ex: log.debug('Channel was closed during LEF.listen') break def prepare_listener(self, binding=None): """ Creates a channel, prepares it, and begins consuming on it. Used by listen. """ log.debug("LEF.prepare_listener: binding %s", binding) binding = binding or self._binding or self._recv_name.binding self._ensure_node() kwargs = {} if isinstance(self._recv_name, BaseTransport): kwargs.update({'transport': self._recv_name}) self._chan = self.node.channel(self.channel_type, **kwargs) # @TODO this does not feel right if isinstance(self._recv_name, BaseTransport): self._recv_name.setup_listener(binding, self._setup_listener) self._chan._recv_name = self._recv_name else: self._setup_listener(self._recv_name, binding=binding) self._chan.start_consume() def get_one_msg(self, timeout=None): """ Retrieves a single message and passes it through an EndpointUnit's message received. This method will block until a message arrives, or until an optional timeout is reached. @raises ChannelClosedError If the channel has been closed. @returns A boolean indicating if a message was retrieved. Will only be false if a timeout is specified. """ assert self._chan, "get_one_msg needs a channel setup" try: with self._chan.accept(timeout=timeout) as newchan: msg, headers, delivery_tag = newchan.recv() log.debug( "LEF %s received message %s, headers %s, delivery_tag %s", self._recv_name, "-", headers, delivery_tag) log_message(self._recv_name, msg, headers, delivery_tag) try: e = self.create_endpoint(existing_channel=newchan) e._message_received(msg, headers) except Exception: log.exception( "Unhandled error while handling received message") raise finally: # ALWAYS ACK newchan.ack(delivery_tag) except Empty: # only occurs when timeout specified, capture the Empty we get from accept and return False return False return True def close(self): BaseEndpoint.close(self) self._chan.close() def get_stats(self): """ Passthrough to channel's get_stats. """ if not self._chan: raise EndpointError("No channel attached") return self._chan.get_stats()
class ListeningBaseEndpoint(BaseEndpoint): """ Establishes channel type for a host of derived, listen/react endpoint factories. """ channel_type = ListenChannel def __init__(self, node=None, name=None, from_name=None, binding=None): BaseEndpoint.__init__(self, node=node) if name: log.warn("ListeningBaseEndpoint: name param is deprecated, please use from_name instead") self._recv_name = from_name or name # ensure NameTrio if not isinstance(self._recv_name, NameTrio): self._recv_name = NameTrio(bootstrap.get_sys_name(), self._recv_name, binding) # if _recv_name is tuple it takes precedence self._ready_event = event.Event() self._binding = binding def _create_channel(self, **kwargs): """ Overrides the BaseEndpoint create channel to supply a transport if our recv name is one. """ if isinstance(self._recv_name, BaseTransport): kwargs.update({'transport':self._recv_name}) return BaseEndpoint._create_channel(self, **kwargs) def get_ready_event(self): """ Returns an async event you can .wait() on. Used to indicate when listen() is ready to start listening. """ return self._ready_event def _setup_listener(self, name, binding=None): self._chan.setup_listener(name, binding=binding) def listen(self, binding=None): log.debug("LEF.listen: binding %s", binding) binding = binding or self._binding or self._recv_name.binding self._ensure_node() kwargs = {} if isinstance(self._recv_name, BaseTransport): kwargs.update({'transport':self._recv_name}) self._chan = self.node.channel(self.channel_type, **kwargs) # @TODO this does not feel right if isinstance(self._recv_name, BaseTransport): self._recv_name.setup_listener(binding, self._setup_listener) self._chan._recv_name = self._recv_name else: self._setup_listener(self._recv_name, binding=binding) self._chan.start_consume() # notify any listeners of our readiness self._ready_event.set() while True: log.debug("LEF: %s blocking, waiting for a message" % str(self._recv_name)) try: newchan = self._chan.accept() msg, headers, delivery_tag = newchan.recv() log.debug("LEF %s received message %s, headers %s, delivery_tag %s", self._recv_name, msg, headers, delivery_tag) log_message(self._recv_name, msg, headers, delivery_tag) except ChannelClosedError as ex: log.debug('Channel was closed during LEF.listen') break try: e = self.create_endpoint(existing_channel=newchan) e._message_received(msg, headers) except Exception: log.exception("Unhandled error while handling received message") raise finally: # ALWAYS ACK newchan.ack(delivery_tag) def close(self): BaseEndpoint.close(self) self._chan.close()
class ListeningBaseEndpoint(BaseEndpoint): """ Establishes channel type for a host of derived, listen/react endpoint factories. """ channel_type = ListenChannel class MessageObject(object): """ Received message wrapper. Contains a body, headers, and a delivery_tag. Internally used by listen, the standard method used by ListeningBaseEndpoint, but will be returned to you if you use get_one_msg or get_n_msgs. If using the latter, you are responsible for calling ack or reject. make_body calls the endpoint's interceptor incoming stack - this may potentially raise an IonException in normal program flow. If this happens, the body/headers attributes will remain None and the error attribute will be set. Calling route() will be a no-op, but ack/reject work. """ def __init__(self, msgtuple, ch, e): self.channel = ch self.endpoint = e self.raw_body, self.raw_headers, self.delivery_tag = msgtuple self.body = None self.headers = None self.error = None def make_body(self): """ Runs received raw message through the endpoint's interceptors. """ try: self.body, self.headers = self.endpoint.intercept_in( self.raw_body, self.raw_headers) except IonException as ex: log.info("MessageObject.make_body raised an error: \n%s", traceback.format_exc(ex)) self.error = ex def ack(self): """ Passthrough to underlying channel's ack. Must call this if using get_one_msg/get_n_msgs. """ self.channel.ack(self.delivery_tag) def reject(self, requeue=False): """ Passthrough to underlying channel's reject. Must call this if using get_one_msg/get_n_msgs. """ self.channel.reject(self.delivery_tag, requeue=requeue) def route(self): """ Call default endpoint's _message_received, where business logic takes place. For instance, a Subscriber would call the registered callback, or an RPCServer would call the Service's operation. You are likely not to use this if using get_one_msg/get_n_msgs. """ if self.error is not None: log.info("Refusing to route a MessageObject with an error") return self.endpoint._message_received(self.body, self.headers) def __init__(self, node=None, name=None, from_name=None, binding=None): BaseEndpoint.__init__(self, node=node) if name: log.warn( "ListeningBaseEndpoint: name param is deprecated, please use from_name instead" ) self._recv_name = from_name or name # ensure NameTrio if not isinstance(self._recv_name, NameTrio): self._recv_name = NameTrio( bootstrap.get_sys_name(), self._recv_name, binding) # if _recv_name is tuple it takes precedence self._ready_event = event.Event() self._binding = binding self._chan = None def _create_channel(self, **kwargs): """ Overrides the BaseEndpoint create channel to supply a transport if our recv name is one. """ if isinstance(self._recv_name, BaseTransport): kwargs.update({'transport': self._recv_name}) return BaseEndpoint._create_channel(self, **kwargs) def get_ready_event(self): """ Returns an async event you can .wait() on. Used to indicate when listen() is ready to start listening. """ return self._ready_event def _setup_listener(self, name, binding=None): self._chan.setup_listener(name, binding=binding) def listen(self, binding=None): """ Main driving method for ListeningBaseEndpoint. Meant to be spawned in a greenlet. This method creates/sets up a channel to listen, starts listening, and consumes messages in a loop until the Endpoint is closed. """ #log.debug("LEF.listen") self.prepare_listener(binding=binding) # notify any listeners of our readiness self._ready_event.set() while True: #log.debug("LEF: %s blocking, waiting for a message", self._recv_name) m = None try: m = self.get_one_msg() m.route() # call default handler except ChannelClosedError as ex: break finally: # ChannelClosedError will go into here too, so make sure we have a message object to ack with if m is not None: m.ack() def prepare_listener(self, binding=None): """ Creates a channel, prepares it, and begins consuming on it. Used by listen. """ #log.debug("LEF.prepare_listener: binding %s", binding) self.initialize(binding=binding) self.activate() def initialize(self, binding=None): """ Creates a channel and prepares it for use. After this, the endpoint is inthe ready state. """ binding = binding or self._binding or self._recv_name.binding self._ensure_node() kwargs = {} if isinstance(self._recv_name, BaseTransport): kwargs.update({'transport': self._recv_name}) self._chan = self.node.channel(self.channel_type, **kwargs) # @TODO this does not feel right if isinstance(self._recv_name, BaseTransport): self._recv_name.setup_listener(binding, self._setup_listener) self._chan._recv_name = self._recv_name else: self._setup_listener(self._recv_name, binding=binding) def activate(self): """ Begins consuming. You must have called initialize first. """ assert self._chan self._chan.start_consume() def deactivate(self): """ Stops consuming. You must have called initialize and activate first. """ assert self._chan self._chan.stop_consume( ) # channel will yell at you if this is invalid def get_stats(self): """ Returns a tuple of the form (# ready messages, # of consumers). This endpoint must have been initialized in order to have a valid queue to work on. Passes down to the channel layer to get this info. """ assert self._chan return self._chan.get_stats() def _get_n_msgs(self, num=1, timeout=None): """ Internal method to accept n messages, create MessageObject wrappers, return them. INBOUND INTERCEPTORS ARE PROCESSED HERE. If the Interceptor stack throws an IonException, the response will be sent immediatly and the MessageObject returned to you will not have body/headers set and will have error set. You should expect to check body/headers or error. """ assert self._chan, "_get_n_msgs: needs the endpoint to have been initialized" mos = [] newch = self._chan.accept(n=num, timeout=timeout) for x in xrange(newch._recv_queue.qsize()): mo = self.MessageObject( newch.recv(), newch, self.create_endpoint(existing_channel=newch)) mo.make_body() # puts through EP interceptors mos.append(mo) log_message("MESSAGE RECV >>> RPC-request", mo.raw_body, mo.raw_headers, self._recv_name, mo.delivery_tag, is_send=False) return mos def get_one_msg(self, timeout=None): """ Receives one message. Blocks until one message is received, or the optional timeout is reached. INBOUND INTERCEPTORS ARE PROCESSED HERE. If the Interceptor stack throws an IonException, the response will be sent immediatly and the MessageObject returned to you will not have body/headers set and will have error set. You should expect to check body/headers or error. @raises ChannelClosedError If the channel has been closed. @raises Timeout If no messages available when timeout is reached. @returns A MessageObject. """ mos = self._get_n_msgs(num=1, timeout=timeout) return mos[0] def get_n_msgs(self, num, timeout=None): """ Receives num messages. INBOUND INTERCEPTORS ARE PROCESSED HERE. If the Interceptor stack throws an IonException, the response will be sent immediatly and the MessageObject returned to you will not have body/headers set and will have error set. You should expect to check body/headers or error. Blocks until all messages received, or the optional timeout is reached. @raises ChannelClosedError If the channel has been closed. @raises Timeout If no messages available when timeout is reached. @returns A list of MessageObjects. """ return self._get_n_msgs(num, timeout=timeout) def get_all_msgs(self, timeout=None): """ Receives all available messages on the queue. WARNING: If the queue is not exclusive, there is a possibility this method behaves incorrectly. You should always pass a timeout to this method. Blocks until all messages received, or the optional timeout is reached. @raises ChannelClosedError If the channel has been closed. @raises Timeout If no messages available when timeout is reached. @returns A list of MessageObjects. """ n, _ = self.get_stats() return self._get_n_msgs(n, timeout=timeout) def close(self): BaseEndpoint.close(self) self._chan.close() def get_stats(self): """ Passthrough to channel's get_stats. """ if not self._chan: raise EndpointError("No channel attached") return self._chan.get_stats()
class ListeningBaseEndpoint(BaseEndpoint): """ Establishes channel type for a host of derived, listen/react endpoint factories. """ channel_type = ListenChannel class MessageObject(object): """ Received message wrapper. Contains a body, headers, and a delivery_tag. Internally used by listen, the standard method used by ListeningBaseEndpoint, but will be returned to you if you use get_one_msg or get_n_msgs. If using the latter, you are responsible for calling ack or reject. make_body calls the endpoint's interceptor incoming stack - this may potentially raise an IonException in normal program flow. If this happens, the body/headers attributes will remain None and the error attribute will be set. Calling route() will be a no-op, but ack/reject work. """ def __init__(self, msgtuple, ch, e): self.channel = ch self.endpoint = e self.raw_body, self.raw_headers, self.delivery_tag = msgtuple self.body = None self.headers = None self.error = None def make_body(self): """ Runs received raw message through the endpoint's interceptors. """ try: self.body, self.headers = self.endpoint.intercept_in(self.raw_body, self.raw_headers) except IonException as ex: log.info("MessageObject.make_body raised an error: \n%s", traceback.format_exc(ex)) self.error = ex def ack(self): """ Passthrough to underlying channel's ack. Must call this if using get_one_msg/get_n_msgs. """ self.channel.ack(self.delivery_tag) def reject(self, requeue=False): """ Passthrough to underlying channel's reject. Must call this if using get_one_msg/get_n_msgs. """ self.channel.reject(self.delivery_tag, requeue=requeue) def route(self): """ Call default endpoint's _message_received, where business logic takes place. For instance, a Subscriber would call the registered callback, or an RPCServer would call the Service's operation. You are likely not to use this if using get_one_msg/get_n_msgs. """ if self.error is not None: log.info("Refusing to route a MessageObject with an error") return self.endpoint._message_received(self.body, self.headers) def __init__(self, node=None, name=None, from_name=None, binding=None): BaseEndpoint.__init__(self, node=node) if name: log.warn("ListeningBaseEndpoint: name param is deprecated, please use from_name instead") self._recv_name = from_name or name # ensure NameTrio if not isinstance(self._recv_name, NameTrio): self._recv_name = NameTrio(bootstrap.get_sys_name(), self._recv_name, binding) # if _recv_name is tuple it takes precedence self._ready_event = event.Event() self._binding = binding self._chan = None def _create_channel(self, **kwargs): """ Overrides the BaseEndpoint create channel to supply a transport if our recv name is one. """ if isinstance(self._recv_name, BaseTransport): kwargs.update({'transport':self._recv_name}) return BaseEndpoint._create_channel(self, **kwargs) def get_ready_event(self): """ Returns an async event you can .wait() on. Used to indicate when listen() is ready to start listening. """ return self._ready_event def _setup_listener(self, name, binding=None): self._chan.setup_listener(name, binding=binding) def listen(self, binding=None): """ Main driving method for ListeningBaseEndpoint. Meant to be spawned in a greenlet. This method creates/sets up a channel to listen, starts listening, and consumes messages in a loop until the Endpoint is closed. """ #log.debug("LEF.listen") self.prepare_listener(binding=binding) # notify any listeners of our readiness self._ready_event.set() while True: #log.debug("LEF: %s blocking, waiting for a message", self._recv_name) m = None try: m = self.get_one_msg() m.route() # call default handler except ChannelClosedError as ex: break finally: # ChannelClosedError will go into here too, so make sure we have a message object to ack with if m is not None: m.ack() def prepare_listener(self, binding=None): """ Creates a channel, prepares it, and begins consuming on it. Used by listen. """ #log.debug("LEF.prepare_listener: binding %s", binding) self.initialize(binding=binding) self.activate() def initialize(self, binding=None): """ Creates a channel and prepares it for use. After this, the endpoint is inthe ready state. """ binding = binding or self._binding or self._recv_name.binding self._ensure_node() kwargs = {} if isinstance(self._recv_name, BaseTransport): kwargs.update({'transport':self._recv_name}) self._chan = self.node.channel(self.channel_type, **kwargs) # @TODO this does not feel right if isinstance(self._recv_name, BaseTransport): self._recv_name.setup_listener(binding, self._setup_listener) self._chan._recv_name = self._recv_name else: self._setup_listener(self._recv_name, binding=binding) def activate(self): """ Begins consuming. You must have called initialize first. """ assert self._chan self._chan.start_consume() def deactivate(self): """ Stops consuming. You must have called initialize and activate first. """ assert self._chan self._chan.stop_consume() # channel will yell at you if this is invalid def get_stats(self): """ Returns a tuple of the form (# ready messages, # of consumers). This endpoint must have been initialized in order to have a valid queue to work on. Passes down to the channel layer to get this info. """ assert self._chan return self._chan.get_stats() def _get_n_msgs(self, num=1, timeout=None): """ Internal method to accept n messages, create MessageObject wrappers, return them. INBOUND INTERCEPTORS ARE PROCESSED HERE. If the Interceptor stack throws an IonException, the response will be sent immediatly and the MessageObject returned to you will not have body/headers set and will have error set. You should expect to check body/headers or error. """ assert self._chan, "_get_n_msgs: needs the endpoint to have been initialized" mos = [] newch = self._chan.accept(n=num, timeout=timeout) for x in xrange(newch._recv_queue.qsize()): mo = self.MessageObject(newch.recv(), newch, self.create_endpoint(existing_channel=newch)) mo.make_body() # puts through EP interceptors mos.append(mo) log_message("MESSAGE RECV >>> RPC-request", mo.raw_body, mo.raw_headers, self._recv_name, mo.delivery_tag, is_send=False) return mos def get_one_msg(self, timeout=None): """ Receives one message. Blocks until one message is received, or the optional timeout is reached. INBOUND INTERCEPTORS ARE PROCESSED HERE. If the Interceptor stack throws an IonException, the response will be sent immediatly and the MessageObject returned to you will not have body/headers set and will have error set. You should expect to check body/headers or error. @raises ChannelClosedError If the channel has been closed. @raises Timeout If no messages available when timeout is reached. @returns A MessageObject. """ mos = self._get_n_msgs(num=1, timeout=timeout) return mos[0] def get_n_msgs(self, num, timeout=None): """ Receives num messages. INBOUND INTERCEPTORS ARE PROCESSED HERE. If the Interceptor stack throws an IonException, the response will be sent immediatly and the MessageObject returned to you will not have body/headers set and will have error set. You should expect to check body/headers or error. Blocks until all messages received, or the optional timeout is reached. @raises ChannelClosedError If the channel has been closed. @raises Timeout If no messages available when timeout is reached. @returns A list of MessageObjects. """ return self._get_n_msgs(num, timeout=timeout) def get_all_msgs(self, timeout=None): """ Receives all available messages on the queue. WARNING: If the queue is not exclusive, there is a possibility this method behaves incorrectly. You should always pass a timeout to this method. Blocks until all messages received, or the optional timeout is reached. @raises ChannelClosedError If the channel has been closed. @raises Timeout If no messages available when timeout is reached. @returns A list of MessageObjects. """ n, _ = self.get_stats() return self._get_n_msgs(n, timeout=timeout) def close(self): BaseEndpoint.close(self) self._chan.close() def get_stats(self): """ Passthrough to channel's get_stats. """ if not self._chan: raise EndpointError("No channel attached") return self._chan.get_stats()
class ListeningBaseEndpoint(BaseEndpoint): """ Establishes channel type for a host of derived, listen/react endpoint factories. """ channel_type = ListenChannel def __init__(self, node=None, name=None, from_name=None, binding=None): BaseEndpoint.__init__(self, node=node) if name: log.warn("ListeningBaseEndpoint: name param is deprecated, please use from_name instead") self._recv_name = from_name or name # ensure NameTrio if not isinstance(self._recv_name, NameTrio): self._recv_name = NameTrio(bootstrap.get_sys_name(), self._recv_name, binding) # if _recv_name is tuple it takes precedence self._ready_event = event.Event() self._binding = binding def _create_channel(self, **kwargs): """ Overrides the BaseEndpoint create channel to supply a transport if our recv name is one. """ if isinstance(self._recv_name, BaseTransport): kwargs.update({'transport':self._recv_name}) return BaseEndpoint._create_channel(self, **kwargs) def get_ready_event(self): """ Returns an async event you can .wait() on. Used to indicate when listen() is ready to start listening. """ return self._ready_event def _setup_listener(self, name, binding=None): self._chan.setup_listener(name, binding=binding) def listen(self, binding=None): log.debug("LEF.listen: binding %s", binding) binding = binding or self._binding or self._recv_name.binding self._ensure_node() kwargs = {} if isinstance(self._recv_name, BaseTransport): kwargs.update({'transport':self._recv_name}) self._chan = self.node.channel(self.channel_type, **kwargs) # @TODO this does not feel right if isinstance(self._recv_name, BaseTransport): self._recv_name.setup_listener(binding, self._setup_listener) self._chan._recv_name = self._recv_name else: self._setup_listener(self._recv_name, binding=binding) self._chan.start_consume() # notify any listeners of our readiness self._ready_event.set() while True: log.debug("LEF: %s blocking, waiting for a message", self._recv_name) try: with self._chan.accept() as newchan: msg, headers, delivery_tag = newchan.recv() log.debug("LEF %s received message %s, headers %s, delivery_tag %s", self._recv_name, "-", headers, delivery_tag) log_message(self._recv_name, msg, headers, delivery_tag) try: e = self.create_endpoint(existing_channel=newchan) e._message_received(msg, headers) except Exception: log.exception("Unhandled error while handling received message") raise finally: # ALWAYS ACK newchan.ack(delivery_tag) except ChannelClosedError as ex: log.debug('Channel was closed during LEF.listen') break # while True: # log.debug("LEF: %s blocking, waiting for a message", self._recv_name) # try: # newchan = self._chan.accept() # msg, headers, delivery_tag = newchan.recv() # # log.debug("LEF %s received message %s, headers %s, delivery_tag %s", self._recv_name, "-", headers, delivery_tag) # log_message(self._recv_name, msg, headers, delivery_tag) # # except ChannelClosedError as ex: # log.debug('Channel was closed during LEF.listen') # break # # try: # e = self.create_endpoint(existing_channel=newchan) # e._message_received(msg, headers) # except Exception: # log.exception("Unhandled error while handling received message") # raise # finally: # # ALWAYS ACK # newchan.ack(delivery_tag) def close(self): BaseEndpoint.close(self) self._chan.close()