def test_5_integration_stomp_1_1_heartbeat(self):
        version = StompSpec.VERSION_1_1
        if BROKER == 'apollo':
            print "Broker %s doesn't properly support heart-beating. Skipping test." % BROKER
            return

        port = 61612 if (BROKER == 'activemq') else PORT # stomp+nio on 61613 does not work properly, so use stomp on 61612
        client = Stomp(self.getConfig(StompSpec.VERSION_1_1, port))
        self.assertEquals(client.lastReceived, None)
        self.assertEquals(client.lastSent, None)

        heartBeatPeriod = 100
        try:
            client.connect(host=VIRTUALHOST, heartBeats=(heartBeatPeriod, heartBeatPeriod), versions=[version])
        except StompProtocolError as e:
            print 'Broker does not support STOMP protocol %s. Skipping this test case. [%s]' % (e, version)
            return

        self.assertTrue((time.time() - client.lastReceived) < 0.1)
        if not (client.serverHeartBeat and client.clientHeartBeat):
            print 'broker does not support heart-beating. disconnecting ...'
            client.disconnect()
            client.close()
            return

        serverHeartBeatInSeconds = client.serverHeartBeat / 1000.0
        clientHeartBeatInSeconds = client.clientHeartBeat / 1000.0

        start = time.time()
        while (time.time() - start) < (2.5 * max(serverHeartBeatInSeconds, clientHeartBeatInSeconds)):
            time.sleep(0.5 * min(serverHeartBeatInSeconds, clientHeartBeatInSeconds))
            client.canRead(0)
            self.assertTrue((time.time() - client.lastReceived) < (2.0 * serverHeartBeatInSeconds))
            if (time.time() - client.lastSent) > (0.5 * clientHeartBeatInSeconds):
                client.beat()
                self.assertTrue((time.time() - client.lastSent) < 0.1)

        start = time.time()
        try:
            while not client.canRead(0.5 * clientHeartBeatInSeconds):
                pass
        except StompConnectionError:
            self.assertTrue((time.time() - start) < (3.0 * clientHeartBeatInSeconds))
            self.assertTrue((time.time() - client.lastReceived) < (2.0 * serverHeartBeatInSeconds))
            self.assertTrue((time.time() - client.lastSent) > clientHeartBeatInSeconds)
        else:
            raise
        client.close()
    def test_5_integration_stomp_1_1_heartbeat(self):
        version = StompSpec.VERSION_1_1

        port = 61612 if (BROKER == 'activemq') else PORT # stomp+nio on 61613 does not work properly, so use stomp on 61612
        client = Stomp(self.getConfig(StompSpec.VERSION_1_1, port))
        self.assertEquals(client.lastReceived, None)
        self.assertEquals(client.lastSent, None)

        heartBeatPeriod = 100
        try:
            client.connect(host=VIRTUALHOST, heartBeats=(heartBeatPeriod, heartBeatPeriod), versions=[version])
        except StompProtocolError as e:
            print 'Broker does not support STOMP protocol %s. Skipping this test case. [%s]' % (e, version)
            return

        self.assertTrue((time.time() - client.lastReceived) < 0.1)
        if not (client.serverHeartBeat and client.clientHeartBeat):
            print 'broker does not support heart-beating. disconnecting ...'
            client.disconnect()
            client.close()
            return

        serverHeartBeatInSeconds = client.serverHeartBeat / 1000.0
        clientHeartBeatInSeconds = client.clientHeartBeat / 1000.0

        start = time.time()
        while (time.time() - start) < (2.5 * max(serverHeartBeatInSeconds, clientHeartBeatInSeconds)):
            time.sleep(0.5 * min(serverHeartBeatInSeconds, clientHeartBeatInSeconds))
            client.canRead(0)
            self.assertTrue((time.time() - client.lastReceived) < (2.0 * serverHeartBeatInSeconds))
            if (time.time() - client.lastSent) > (0.5 * clientHeartBeatInSeconds):
                client.beat()
                self.assertTrue((time.time() - client.lastSent) < 0.1)

        start = time.time()
        try:
            while not client.canRead(0.5 * clientHeartBeatInSeconds):
                pass
            if client.receiveFrame().command == StompSpec.ERROR:
                raise StompProtocolError()
        except (StompConnectionError, StompProtocolError):
            self.assertTrue((time.time() - start) < (3.0 * clientHeartBeatInSeconds))
            self.assertTrue((time.time() - client.lastReceived) < (2.0 * serverHeartBeatInSeconds))
            self.assertTrue((time.time() - client.lastSent) > clientHeartBeatInSeconds)
        else:
            raise
        client.close()
    def test_5_integration_stomp_1_1_heartbeat(self):
        version = StompSpec.VERSION_1_1

        client = Stomp(self.getConfig(StompSpec.VERSION_1_1))
        self.assertEqual(client.lastReceived, None)
        self.assertEqual(client.lastSent, None)

        heartBeatPeriod = 100
        try:
            client.connect(host=VIRTUALHOST, heartBeats=(heartBeatPeriod, heartBeatPeriod), versions=[version])
        except StompProtocolError as e:
            print('Broker does not support STOMP protocol %s. Skipping this test case. [%s]' % (e, version))
            return

        self.assertTrue((time.time() - client.lastReceived) < 0.1)
        if not (client.serverHeartBeat and client.clientHeartBeat):
            print('broker does not support heart-beating. disconnecting ...')
            client.disconnect()
            client.close()
            return

        serverHeartBeatInSeconds = client.serverHeartBeat / 1000.0
        clientHeartBeatInSeconds = client.clientHeartBeat / 1000.0

        start = time.time()
        while (time.time() - start) < (2.5 * max(serverHeartBeatInSeconds, clientHeartBeatInSeconds)):
            time.sleep(0.5 * min(serverHeartBeatInSeconds, clientHeartBeatInSeconds))
            client.canRead(0)
            self.assertTrue((time.time() - client.lastReceived) < (2.0 * serverHeartBeatInSeconds))
            if (time.time() - client.lastSent) > (0.5 * clientHeartBeatInSeconds):
                client.beat()
                self.assertTrue((time.time() - client.lastSent) < 0.1)

        start = time.time()
        try:
            while not client.canRead(0.5 * clientHeartBeatInSeconds):
                pass
            if client.receiveFrame().command == StompSpec.ERROR:
                raise StompProtocolError()
        except (StompConnectionError, StompProtocolError):
            self.assertTrue((time.time() - start) < (3.0 * clientHeartBeatInSeconds))
            self.assertTrue((time.time() - client.lastReceived) < (2.0 * serverHeartBeatInSeconds))
            self.assertTrue((time.time() - client.lastSent) > clientHeartBeatInSeconds)
        else:
            raise
        client.close()
Exemplo n.º 4
0
    print "--------------------------------------------------------------------"
    print "state        : ", client.session.state
    print "client HB    : ", client.session.clientHeartBeat
    print "server HB    : ", client.session.serverHeartBeat
    print "server       : ", client.session.server
    print "id           : ", client.session.id
    print "lastSent     : ", toTime(client.session.lastSent)
    print "lastReceived : ", toTime(client.session.lastReceived)

    start = time.time()
    elapsed = lambda t=None: (t or time.time()) - start

    times = lambda: 'elapsed: %.2f, last received: %.2f, last sent: %.2f' % (
        elapsed(), elapsed(client.lastReceived), elapsed(client.lastSent))

    for x in range(0, 7):
        client.beat()
        print times()
        time.sleep(1)

#    while elapsed() < 2 * client.clientHeartBeat / 1000.0:
#        client.canRead(0.8 * client.serverHeartBeat / 1000.0) # poll server heart-beats
#        client.beat() # send client heart-beats
#        print times()

#    client.canRead()
#    print times()

    client.disconnect()
    sys.exit(0)
Exemplo n.º 5
0
if __name__ == '__main__':

    client = Stomp(CONFIG)

    client.connect(connectedTimeout=4, heartBeats=(1000,0))

    print "--------------------------------------------------------------------"
    print "state        : ", client.session.state
    print "client HB    : ", client.session.clientHeartBeat
    print "server HB    : ", client.session.serverHeartBeat
    print "server       : ", client.session.server
    print "id           : ", client.session.id
    print "lastSent     : ", toTime(client.session.lastSent)
    print "lastReceived : ", toTime(client.session.lastReceived)
    
    start = time.time()
    elapsed = lambda t = None: (t or time.time()) - start

    times = lambda: 'elapsed: %.2f, last received: %.2f, last sent: %.2f' % (
        elapsed(), elapsed(client.lastReceived), elapsed(client.lastSent)
        )

    for x in range(0, 4):
        client.beat()
        print times()
        time.sleep(1)

    client.disconnect()
    sys.exit(0)
    
Exemplo n.º 6
0
class StompClient(BaseComponent):
    """ Send and Receive messages from a STOMP queue """
    channel = "stomp"

    def init(self, host, port, username=None, password=None,
             connect_timeout=3, connected_timeout=3,
             version=StompSpec.VERSION_1_2, accept_versions=["1.0", "1.1", "1.2"],
             heartbeats=(0, 0), ssl_context=None,
             use_ssl=True,
             key_file=None,
             cert_file=None,
             ca_certs=None,
             ssl_version=ssl.PROTOCOL_SSLv23,
             key_file_password=None,
             proxy_host=None,
             proxy_port=None,
             proxy_user=None,
             proxy_password=None,
             channel=channel):
        """ Initialize StompClient.  Called after __init__ """
        self.channel = channel
        if proxy_host:
            LOG.info("Connect to %s:%s through proxy %s:%d", host, port, proxy_host, proxy_port)
        else:
            LOG.info("Connect to %s:%s", host, port)

        if use_ssl and not ssl_context:

            ssl_params = dict(key_file=key_file,
                              cert_file=cert_file,
                              ca_certs=ca_certs,
                              ssl_version=ssl_version,
                              password=key_file_password)
            LOG.info("Request to use old-style socket wrapper: %s", ssl_params)
            ssl_context = ssl_params

        if use_ssl:
            uri = "ssl://%s:%s" % (host, port)
        else:
            uri = "tcp://%s:%s" % (host, port)

        # Configure failover options so it only tries to connect once
        self._stomp_server = "failover:(%s)?maxReconnectAttempts=1,startupMaxReconnectAttempts=1" % uri

        self._stomp_config = StompConfig(uri=self._stomp_server, sslContext=ssl_context,
                                         version=version,
                                         login=username,
                                         passcode=password)

        self._heartbeats = heartbeats
        self._accept_versions = accept_versions
        self._connect_timeout = connect_timeout
        self._connected_timeout = connected_timeout
        Stomp._transportFactory = EnhancedStompFrameTransport
        Stomp._transportFactory.proxy_host = proxy_host
        Stomp._transportFactory.proxy_port = proxy_port
        Stomp._transportFactory.proxy_user = proxy_user
        Stomp._transportFactory.proxy_password = proxy_password
        self._client = Stomp(self._stomp_config)
        self._subscribed = {}
        self.server_heartbeat = None
        self.client_heartbeat = None
        self.ALLOWANCE = 2  # multiplier for heartbeat timeouts

    @property
    def connected(self):
        if self._client.session:
            return self._client.session.state == StompSession.CONNECTED
        else:
            return False

    @property
    def subscribed(self):
        return self._subscribed.keys()

    @property
    def stomp_logger(self):
        return LOG_CATEGORY

    @handler("disconnect")
    def _disconnect(self, receipt=None):
        if self.connected:
            self._client.disconnect(receipt=receipt)
        self._client.close(flush=True)
        self.fire(disconnected(reconnect=False))
        self._subscribed = {}
        return "disconnected"

    def start_heartbeats(self):
        LOG.info("Client HB: %s  Server HB: %s", self._client.clientHeartBeat, self._client.serverHeartBeat)
        if self._client.clientHeartBeat:
            if self.client_heartbeat:
                # Timer already exists, just reset it
                self.client_heartbeat.reset()
            else:
                LOG.info("Client will send heartbeats to server")
                # Send heartbeats at 80% of agreed rate
                self.client_heartbeat = Timer((self._client.clientHeartBeat / 1000.0) * 0.8,
                                              client_heartbeat(), persist=True)
                self.client_heartbeat.register(self)
        else:
            LOG.info("No Client heartbeats will be sent")

        if self._client.serverHeartBeat:
            if self.server_heartbeat:
                # Timer already exists, just reset it
                self.server_heartbeat.reset()
            else:
                LOG.info("Requested heartbeats from server.")
                # Allow a grace period on server heartbeats
                self.server_heartbeat = Timer((self._client.serverHeartBeat / 1000.0) * self.ALLOWANCE,
                                              server_heartbeat(), persist=True)
                self.server_heartbeat.register(self)
        else:
            LOG.info("Expecting no heartbeats from Server")

    @handler("connect")
    def connect(self, event, host=None, *args, **kwargs):
        """ connect to Stomp server """
        LOG.info("Connect to Stomp...")
        try:
            self._client.connect(heartBeats=self._heartbeats,
                                 host=host,
                                 versions=self._accept_versions,
                                 connectTimeout=self._connect_timeout,
                                 connectedTimeout=self._connected_timeout)
            LOG.info("State after Connection Attempt: %s", self._client.session.state)
            if self.connected:
                LOG.info("Connected to %s", self._stomp_server)
                self.fire(connected())
                self.start_heartbeats()
                return "success"

        except StompConnectionError:
            LOG.debug(traceback.format_exc())
            self.fire(connection_failed(self._stomp_server))
            event.success = False
        return "fail"

    @handler("server_heartbeat")
    def check_server_heartbeat(self, event):
        """ Confirm that heartbeat from server hasn't timed out """
        now = time.time()
        last = self._client.lastReceived or 0
        if last:
            elapsed = now - last
        else:
            elapsed = -1
        LOG.debug("Last received data %d seconds ago", elapsed)
        if ((self._client.serverHeartBeat / 1000.0) * self.ALLOWANCE + last) < now:
            LOG.error("Server heartbeat timeout. %d seconds since last heartbeat.  Disconnecting.", elapsed)
            event.success = False
            self.fire(heartbeat_timeout())
            if self.connected:
                self._client.disconnect()
            # TODO: Try to auto-reconnect?

    @handler("client_heartbeat")
    def send_heartbeat(self, event):
        if self.connected:
            LOG.debug("Sending heartbeat")
            try:
                self._client.beat()
            except StompConnectionError:
                event.success = False
                self.fire(disconnected())

    @handler("generate_events")
    def generate_events(self, event):
        if not self.connected:
            return
        try:
            if self._client.canRead(1):
                frame = self._client.receiveFrame()
                LOG.debug("Recieved frame %s", frame)
                self.fire(message(frame))
        except StompConnectionError:
            self.fire(disconnected())

    @handler("send")
    def send(self, event, destination, body, headers=None, receipt=None):
        LOG.debug("send()")
        if not self.connected:
            LOG.error("Can't send when Stomp is disconnected")
            self.fire(on_stomp_error(None, Exception("Message send attempted with stomp disconnected")))
            event.success = False
            return
        try:
            self._client.send(destination, body=body.encode('utf-8'), headers=headers, receipt=receipt)
            LOG.debug("Message sent")
        except StompConnectionError as err:
            event.success = False
            self.fire(disconnected())
        except StompError as err:
            LOG.error("Error sending ack")
            event.success = False
            self.fire(on_stomp_error(None, err))

    @handler("subscribe")
    def _subscribe(self, event, destination, ack=ACK_CLIENT_INDIVIDUAL):
        if ack not in ACK_MODES:
            raise ValueError("Invalid client ack mode specified")
        LOG.info("Subscribe to message destination %s", destination)
        try:
            # Set ID to match destination name for easy reference later
            frame, token = self._client.subscribe(destination,
                                                  headers={StompSpec.ACK_HEADER: ack,
                                                           'id': destination})
            self._subscribed[destination] = token
        except StompConnectionError as err:
            event.success = False
            self.fire(disconnected())
        except StompError as err:
            event.success = False
            LOG.debug(traceback.format_exc())
            self.fire(on_stomp_error(None, err))

    @handler("unsubscribe")
    def _unsubscribe(self, event, destination):
        if destination not in self._subscribed:
            LOG.error("Unsubscribe Request Ignored. Not subscribed to %s", destination)
            return
        try:
            token = self._subscribed.pop(destination)
            frame = self._client.unsubscribe(token)
            LOG.debug("Unsubscribed: %s", frame)
        except StompConnectionError as err:
            event.success = False
            self.fire(disconnected())
        except StompError as err:
            LOG.error("Error sending ack")
            event.success = False
            self.fire(on_stomp_error(frame, err))

    @handler("message")
    def on_message(self, event, headers, message):
        LOG.info("Stomp message received")

    @handler("ack")
    def ack_frame(self, event, frame):
        LOG.debug("ack_frame()")
        try:
            self._client.ack(frame)
            LOG.debug("Ack Sent")
        except StompConnectionError as err:
            LOG.error("Error sending ack")
            event.success = False
            self.fire(disconnected())
        except StompError as err:
            LOG.error("Error sending ack")
            event.success = False
            self.fire(on_stomp_error(frame, err))

    def get_subscription(self, frame):
        """ Get subscription from frame """
        LOG.info(self._subscribed)
        _, token = self._client.message(frame)
        return self._subscribed[token]
Exemplo n.º 7
0
class StompClient(BaseComponent):

    channel = "stomp"

    def init(
            self,
            host,
            port,
            username=None,
            password=None,
            connect_timeout=3,
            connected_timeout=3,
            version=StompSpec.VERSION_1_2,
            accept_versions=["1.0", "1.1", "1.2"],
            heartbeats=(0, 0),
            ssl_context=None,
            use_ssl=True,
            key_file=None,
            cert_file=None,
            ca_certs=None,
            ssl_version=ssl.PROTOCOL_SSLv23,  # means 'latest available'
            key_file_password=None,
            proxy_host=None,
            proxy_port=None,
            proxy_user=None,
            proxy_password=None,
            channel=channel):
        """ Initialize StompClient.  Called after __init__ """
        self.channel = channel
        if proxy_host:
            LOG.info("Connect to %s:%s through proxy %s:%d", host, port,
                     proxy_host, proxy_port)
        else:
            LOG.info("Connect to %s:%s", host, port)

        if use_ssl and not ssl_context:

            ssl_params = dict(key_file=key_file,
                              cert_file=cert_file,
                              ca_certs=ca_certs,
                              ssl_version=ssl_version,
                              password=key_file_password)
            LOG.info("Request to use old-style socket wrapper: %s", ssl_params)
            ssl_context = ssl_params

        if use_ssl:
            uri = "ssl://%s:%s" % (host, port)
        else:
            uri = "tcp://%s:%s" % (host, port)

        # Configure failover options so it only tries to connect once
        self._stomp_server = "failover:(%s)?maxReconnectAttempts=1,startupMaxReconnectAttempts=1" % uri

        self._stomp_config = StompConfig(uri=self._stomp_server,
                                         sslContext=ssl_context,
                                         version=version,
                                         login=username,
                                         passcode=password)

        self._heartbeats = heartbeats
        self._accept_versions = accept_versions
        self._connect_timeout = connect_timeout
        self._connected_timeout = connected_timeout
        Stomp._transportFactory = EnhancedStompFrameTransport
        Stomp._transportFactory.proxy_host = proxy_host
        Stomp._transportFactory.proxy_port = proxy_port
        Stomp._transportFactory.proxy_user = proxy_user
        Stomp._transportFactory.proxy_password = proxy_password
        self._client = Stomp(self._stomp_config)
        self._subscribed = {}
        self.server_heartbeat = None
        self.client_heartbeat = None
        self.last_heartbeat = 0
        self.ALLOWANCE = 2  # multiplier for heartbeat timeouts

    @property
    def connected(self):
        if self._client.session:
            return self._client.session.state == StompSession.CONNECTED
        else:
            return False

    @property
    def socket_connected(self):
        try:
            if self._client._transport:
                return True
        except:
            pass
        return False

    @property
    def subscribed(self):
        return self._subscribed.keys()

    @property
    def stomp_logger(self):
        return LOG_CATEGORY

    @handler("Disconnect")
    def _disconnect(self, receipt=None, flush=True, reconnect=False):
        try:
            if flush:
                self._subscribed = {}
            if self.connected:
                self._client.disconnect(receipt=receipt)
        except Exception as e:
            LOG.error("Failed to disconnect client")
        try:
            self.fire(Disconnected(reconnect=reconnect))
            self._client.close(flush=flush)
        except Exception as e:
            LOG.error("Failed to close client connection")

        return "disconnected"

    def start_heartbeats(self):
        LOG.info("Client HB: %s  Server HB: %s", self._client.clientHeartBeat,
                 self._client.serverHeartBeat)
        if self._client.clientHeartBeat:
            if self.client_heartbeat:
                # Timer already exists, just reset it
                self.client_heartbeat.reset()
            else:
                LOG.info("Client will send heartbeats to server")
                # Send heartbeats at 80% of agreed rate
                self.client_heartbeat = Timer(
                    (self._client.clientHeartBeat / 1000.0) * 0.8,
                    ClientHeartbeat(),
                    persist=True)
                self.client_heartbeat.register(self)
        else:
            LOG.info("No Client heartbeats will be sent")

        if self._client.serverHeartBeat:
            if self.server_heartbeat:
                # Timer already exists, just reset it
                self.server_heartbeat.reset()
            else:
                LOG.info("Requested heartbeats from server.")
                # Allow a grace period on server heartbeats
                self.server_heartbeat = Timer(
                    (self._client.serverHeartBeat / 1000.0) * self.ALLOWANCE,
                    ServerHeartbeat(),
                    persist=True)
                self.server_heartbeat.register(self)
        else:
            LOG.info("Expecting no heartbeats from Server")

    @handler("Connect")
    def connect(self, event, host=None, *args, **kwargs):
        """ connect to Stomp server """
        LOG.info("Connect to Stomp...")
        try:
            self._client.connect(heartBeats=self._heartbeats,
                                 host=host,
                                 versions=self._accept_versions,
                                 connectTimeout=self._connect_timeout,
                                 connectedTimeout=self._connected_timeout)
            LOG.debug("State after Connection Attempt: %s",
                      self._client.session.state)
            if self.connected:
                LOG.info("Connected to %s", self._stomp_server)
                self.fire(Connected())
                self.start_heartbeats()
                return "success"

        except StompConnectionError as err:
            LOG.debug(traceback.format_exc())
            self.fire(ConnectionFailed(self._stomp_server))
            event.success = False
        return "fail"

    @handler("ServerHeartbeat")
    def check_server_heartbeat(self, event):
        """ Confirm that heartbeat from server hasn't timed out """
        now = time.time()
        self.last_heartbeat = self._client.lastReceived or self.last_heartbeat
        if self.last_heartbeat:
            elapsed = now - self.last_heartbeat
        else:
            elapsed = -1
        if ((self._client.serverHeartBeat / 1000.0) * self.ALLOWANCE +
                self.last_heartbeat) < now:
            LOG.error(
                "Server heartbeat timeout. %d seconds since last heartbeat.",
                elapsed)
            event.success = False
            self.fire(HeartbeatTimeout())

    @handler("ClientHeartbeat")
    def send_heartbeat(self, event):
        if self.connected:
            LOG.debug("Sending heartbeat")
            try:
                self._client.beat()
            except (StompConnectionError, StompError) as err:
                event.success = False
                self.fire(OnStompError(None, err))

    @handler("generate_events")
    def generate_events(self, event):
        event.reduce_time_left(0.1)
        if not self.connected:
            return
        try:
            if self._client.canRead(0):
                frame = self._client.receiveFrame()
                LOG.debug("Recieved frame %s", frame)
                if frame.command == StompSpec.ERROR:
                    self.fire(OnStompError(frame, None))
                else:
                    self.fire(Message(frame))
        except (StompConnectionError, StompError) as err:
            LOG.error("Failed attempt to generate events.")
            self.fire(OnStompError(None, err))

    @handler("Send")
    def send(self, event, destination, body, headers=None, receipt=None):
        LOG.debug("send()")
        try:
            self._client.send(destination,
                              body=body.encode('utf-8'),
                              headers=headers,
                              receipt=receipt)
            LOG.debug("Message sent")
        except (StompConnectionError, StompError) as err:
            LOG.error("Error sending frame")
            event.success = False
            self.fire(OnStompError(None, err))
            raise  # To fire Send_failure event

    @handler("Subscribe")
    def _subscribe(self,
                   event,
                   destination,
                   additional_headers=None,
                   ack=ACK_CLIENT_INDIVIDUAL):
        if ack not in ACK_MODES:
            raise ValueError("Invalid client ack mode specified")
        if destination in self._client.session._subscriptions:
            LOG.debug("Ignoring subscribe request to %s. Already subscribed.",
                      destination)
        LOG.info("Subscribe to message destination %s", destination)
        try:
            headers = {StompSpec.ACK_HEADER: ack, 'id': destination}
            if additional_headers:
                headers.update(additional_headers)

            # Set ID to match destination name for easy reference later
            frame, token = self._client.subscribe(destination, headers)
            self._subscribed[destination] = token
        except (StompConnectionError, StompError) as err:
            LOG.error("Failed to subscribe to queue.")
            event.success = False
            LOG.debug(traceback.format_exc())
            self.fire(OnStompError(None, err))

    @handler("Unsubscribe")
    def _unsubscribe(self, event, destination):
        if destination not in self._subscribed:
            LOG.error("Unsubscribe Request Ignored. Not subscribed to %s",
                      destination)
            return
        try:
            token = self._subscribed.pop(destination)
            frame = self._client.unsubscribe(token)
            LOG.debug("Unsubscribed: %s", frame)
        except (StompConnectionError, StompError) as err:
            event.success = False
            LOG.error("Unsubscribe Failed.")
            self.fire(OnStompError(frame, err))

    @handler("Message")
    def on_message(self, event, headers, message):
        LOG.debug("Stomp message received")

    @handler("Ack")
    def ack_frame(self, event, frame):
        LOG.debug("ack_frame()")
        try:
            self._client.ack(frame)
            LOG.debug("Ack Sent")
        except (StompConnectionError, StompError) as err:
            LOG.error("Error sending ack")
            event.success = False
            self.fire(OnStompError(frame, err))
            raise  # To fire Ack_failure event

    def get_subscription(self, frame):
        """ Get subscription from frame """
        _, token = self._client.message(frame)
        return self._subscribed[token]
Exemplo n.º 8
0
class client_activemq():
    def __init__(self, worker_threads=2):
        self.log = logging.getLogger('client_activemq')
        self.log.debug('Initializing client_activemq')

        self.host = '127.0.0.1'  #manually set here until there is a better place
        self.port = 61613  #manually set here until there is a better place
        self.worker_threads = worker_threads

        self.client = None
        self.connection_issue = True
        self.continue_running = True
        self.subscriptions = {}
        self.outbound_msg_queue = []
        self.outbound_msg_queue_lazy = []

        self.threads = []
        self.work_queue = Queue.Queue()

        connection_handler = threading.Thread(target=self.connection_handler)
        connection_handler.daemon = True
        connection_handler.start()

        send_event_hopeful_thread = threading.Thread(
            target=self.send_event_hopeful_thread)
        send_event_hopeful_thread.daemon = True
        send_event_hopeful_thread.start()

        send_event_lazy_thread = threading.Thread(
            target=self.send_event_lazy_thread)
        send_event_lazy_thread.daemon = True
        send_event_lazy_thread.start()

        publish_loop = threading.Thread(target=self.publish_loop)
        publish_loop.daemon = True
        publish_loop.start()

    def init_connection(self):
        if (self.client != None and self.client.session.state == 'connected'):
            self.log.error('Re-init connection')
            try:
                self.client.disconnect()
            except:
                pass

        config = StompConfig('tcp://%s:%s' % (self.host, self.port),
                             version=StompSpec.VERSION_1_1,
                             check=True)
        self.client = Stomp(config)
        self.client.connect(heartBeats=(30000, 0),
                            connectTimeout=1,
                            connectedTimeout=1)

    def build_worker_pool(self, capacity):
        for thread in self.threads:
            if thread.is_alive() == False:
                self.threads.remove(thread)

        while len(self.threads) < capacity:
            t = threading.Thread(target=self.worker, args=(self.work_queue, ))
            t.daemon = True
            t.start()
            self.threads.append(t)

    def connection_handler(self):
        #This func will just try to reconnect repeatedly in a thread if we're flagged as having an issue.
        while (True):
            if (self.connection_issue == True):
                try:
                    self.init_connection()
                    self.connection_issue = False
                    time.sleep(0.1)
                    for subscription in self.subscriptions:
                        self.log.info('Re-subscribing upon reconnection: %s' %
                                      subscription)
                        self.subscribe(
                            subscription,
                            self.subscriptions[subscription]['callback_class'],
                            self.subscriptions[subscription]['callback'], True,
                            self.subscriptions[subscription]['selector'])

                except Exception as e:
                    self.log.critical('Except: %s' % e)
            else:

                try:
                    self.client.beat()
                except:
                    self.log.warning('Failed to heartbeat?')
            self.build_worker_pool(self.worker_threads)
            time.sleep(10)

    def subscribe(self,
                  queue,
                  callback_class,
                  callback,
                  resub=False,
                  selector=None):
        #This needs to exist so we can keep track of what subs we have and re-sub on reconnect
        if queue in self.subscriptions and not resub:
            self.log.info('Ignoring existing subscription %s' % queue)
            return True  #we're already subscribed

        this_uuid = '%s' % uuid.uuid4()
        if (self.connection_issue == True):
            self.log.info(
                'Cannot process subscription request as were not properly connected'
            )
            self.subscriptions[queue] = {
                'uuid': this_uuid,
                'callback': callback,
                'callback_class': callback_class,
                'selector': selector
            }
            return None
        self.log.info('subscribe(%s %s %s %s)' %
                      (queue, callback_class, callback, resub))

        try:
            self.subscriptions[queue] = {
                'uuid': this_uuid,
                'callback': callback,
                'callback_class': callback_class,
                'selector': selector
            }
            if selector != None:
                self.client.subscribe(
                    queue, {
                        StompSpec.ID_HEADER: this_uuid,
                        StompSpec.ACK_HEADER: StompSpec.ACK_AUTO,
                        'selector': selector
                    })
            else:
                self.client.subscribe(
                    queue, {
                        StompSpec.ID_HEADER: this_uuid,
                        StompSpec.ACK_HEADER: StompSpec.ACK_AUTO,
                    })
        except Exception as e:
            self.log.fatal('%s' % e)
            self.connection_issue = True

    def unsubscribe(self, queue):
        if queue not in self.subscriptions:
            return False  #cant unsubscribe, we're not subscribed

        if (self.connection_issue == True):
            del self.subscriptions[queue]
            return None
        try:
            self.client.unsubscribe(self.subscriptions[queue]['uuid'])
            del self.subscriptions[queue]
        except Exception as e:
            self.log.error('%s' % e)
            self.log.error('%s' % traceback.format_exc())
            sys.exc_clear()
            self.connection_issue = True

    def send_event_lazy(self, destination, body, headers={}, persistent=False):

        headers['time_queued'] = time.time()
        self.outbound_msg_queue_lazy.append({
            'destination': destination,
            'body': body,
            'headers': headers,
            'persistent': persistent
        })

    def send_event_lazy_thread(self):
        while self.continue_running:
            time.sleep(0.001)
            #If it gets there, then great, if not, well we tried!
            if (self.connection_issue == True):
                continue
            while len(self.outbound_msg_queue_lazy
                      ) > 0 and self.connection_issue == False:
                try:
                    item = self.outbound_msg_queue_lazy.pop(0)
                    if item['persistent'] == True:
                        item['headers']['persistent'] = 'true'
                    else:
                        item['headers']['persistent'] = 'false'
                    item['headers']['time_sent'] = time.time()
                    self.client.send(item['destination'],
                                     json.dumps(item['body']), item['headers'])
                except Exception as e:
                    self.log.critical('Except: %s' % e)
                    self.outbound_msg_queue_lazy.insert(0, item)
                    self.connection_issue = True

    def send_event_hopeful(self, destination, body, persist):
        self.outbound_msg_queue.append({
            'destination': destination,
            'body': body
        })

    def send_event_hopeful_thread(self):

        while self.continue_running:
            time.sleep(0.001)
            if (self.connection_issue == True):
                continue
            while len(self.outbound_msg_queue
                      ) > 0 and self.connection_issue == False:
                try:
                    item = self.outbound_msg_queue.pop(0)
                    self.client.send(item['destination'],
                                     json.dumps(item['body']),
                                     {'persistent': 'false'})

                except Exception as e:
                    self.log.critical('Except: %s' % e)
                    self.outbound_msg_queue.insert(0, item)
                    self.connection_issue = True

    def worker(self, queue):
        while self.continue_running:
            try:
                item = queue.get()
                for x in 1, 2, 3:
                    try:
                        item['callback'](item['callback_class'], item['data'],
                                         item['headers'])
                        break
                    except Exception as e:
                        self.log.error('Exception in worker thread: %s' % e)
                        traceback.print_exc()
                        sys.exc_clear()
                        time.sleep(0.01)

                queue.task_done()
            except:
                pass

    def publish_loop(self):
        self.log.debug('publish_loop() init')
        time.sleep(0.2)
        while self.continue_running:
            if self.connection_issue == False:
                try:
                    if not self.client.canRead(0.01):
                        continue
                    try:
                        frame = self.client.receiveFrame()
                        data = json.loads(frame.body)
                        queue = frame.headers['destination']
                        try:
                            time_sent = float(frame.headers['time_sent'])
                            time_queued = float(frame.headers['time_queued'])
                            send_latency = (time.time() - time_sent)
                            queue_latency = (time_sent - time_queued)
                            if queue_latency > 0.1:
                                print 'Queue Latency: %s' % (queue_latency)
                            if send_latency > 0.1:
                                print 'Send Latency: %s' % (send_latency)
                        except:
                            pass

                        self.work_queue.put({
                            'callback':
                            self.subscriptions[queue]['callback'],
                            'callback_class':
                            self.subscriptions[queue]['callback_class'],
                            'data':
                            data,
                            'headers':
                            frame.headers,
                        })
                    #self.client.ack(frame)
                    except Exception as e:
                        raise
                        self.log.critical('Except: %s' % e)
                        #self.client.nack(frame)
                except Exception as e:
                    self.log.fatal('except: %s' % e)
                    self.connection_issue = True