def _submit_message(self, event, queue, msg_id, message, channel="*"): """ Create and fire an ActionMessage """ try: message_id = "ID:resilient-54199-{val}-6:2:12:1:1".format( val=msg_id) reply_to = "/queue/acks.{org}.{queue}".format(org=self.org_id, queue=queue) destination = "/queue/actions.{org}.{queue}".format( org=self.org_id, queue=queue) headers = { "reply-to": reply_to, "expires": "0", "timestamp": str(int(time.time()) * 1000), "destination": destination, "correlation-id": "invid:390", "persistent": "True", "priority": "4", "Co3MessagePayload": "ActionDataDTO", "Co3ContentType": "application/json", "message-id": message_id, "Co3ContextToken": "dummy", "subscription": "stomp-{queue}".format(queue=queue) } try: sock = self.actions_sent.get(msg_id) if not isinstance(message, dict): message = json.loads(message) assert (isinstance(message, dict)) except Exception as e: LOG.exception("Bad Message<action %d>: %s", msg_id, message) if sock: msg = "Bad Message<action %d>! %s" % (msg_id, str(e)) self.fire_message(sock, msg) return if message.get("function"): channel = "functions." + message["function"]["name"] action_event = FunctionMessage(source=self.parent, headers=headers, message=message, test=True, test_msg_id=msg_id) else: channel = "actions." + queue action_event = ActionMessage(source=self.parent, headers=headers, message=message, test=True, test_msg_id=msg_id) action_event.parent = event self.fire(action_event, channel) if sock: self.fire_message(sock, "Action Submitted<action %d>" % msg_id) except Exception as e: LOG.exception("Action Failed<action %d>", msg_id) if sock: msg = "Action Failed<action %d>: %s" % (msg_id, str(e)) self.fire_message(sock, msg)
def on_stomp_message(self, event, headers, message): """STOMP produced a message.""" # Find the queue name from the subscription id (stomp_listener_xxx) msg_id = event.frame.headers.get("message-id") if not msg_id: LOG.error("Received message with no message id. %s", event.frame.info()) raise ValueError("Stomp message with no message id received") elif msg_id in self._resilient_ack_delivery_failures or msg_id in self._stomp_ack_delivery_failures: # This is a message we have already processed but we failed to acknowledge # Don't process it again, just acknowledge it LOG.info( "Skipping reprocess of message %s. Sending saved ack now.", msg_id) failure_info = self._resilient_ack_delivery_failures.get(msg_id) if failure_info: self.fire( Send(headers={'correlation-id': headers['correlation-id']}, body=failure_info["result"]["body"], destination=headers['reply-to'], message_id=msg_id)) self._resilient_ack_delivery_failures.pop(msg_id) failure_info = self._stomp_ack_delivery_failures.get(msg_id) if failure_info: self.fire(Ack(event.frame)) self._stomp_ack_delivery_failures.pop(msg_id) else: subscription = self.stomp_component.get_subscription(event.frame) LOG.debug('STOMP listener: message for %s', subscription) queue_name = subscription.split(".", 2)[2] channel = "actions." + queue_name LOG.debug("Got Message: %s", event.frame.info()) try: # Expect the message payload to always be UTF8 JSON. # However, it may contain surrogate pairs, and in Python 3 that causes problems: # - surrogate pairs are not allowed by the default (strict) utf8 decoder, # - if we pass them, it will cause downstream issues, so we should re-encode. try: mstr = message.decode('utf-8') except UnicodeDecodeError: LOG.debug("Failed utf8 decode, trying surrogate") mstr = message.decode('utf-8', "surrogatepass").encode( "utf-16", "surrogatepass").decode("utf-16") message = json.loads(mstr) # Construct a Circuits event with the message, and fire it on the channel if message.get("function"): channel = "functions." + message["function"]["name"] event = FunctionMessage(source=self, headers=headers, message=message, frame=event.frame, log_dir=self.logging_directory) else: event = ActionMessage(source=self, headers=headers, message=message, frame=event.frame, log_dir=self.logging_directory) LOG.info("Event: %s Channel: %s", event, channel) self.fire(event, channel) except Exception as exc: LOG.exception(exc) if not isinstance(message, dict): LOG.error("DATA:%s", base64.b64encode(message)) # Normally the event won't be ack'd. Just report it and carry on. if self.ignore_message_failure: # Construct and fire anyway, which will ack the message LOG.warning("This message failure will be ignored...") event = ActionMessage(source=self, headers=headers, message=None, frame=event.frame, log_dir=self.logging_directory) self.fire(event, channel)